2024-08-28 04:43:23 -07:00
|
|
|
import { isBlurhashValid } from 'blurhash';
|
2024-10-13 15:25:30 -07:00
|
|
|
import * as v from 'valibot';
|
2024-08-28 04:43:23 -07:00
|
|
|
|
|
|
|
import { mimeSchema } from './utils';
|
|
|
|
|
|
|
|
const blurhashSchema = z.string().superRefine((value, ctx) => {
|
|
|
|
const r = isBlurhashValid(value);
|
|
|
|
|
|
|
|
if (!r.result) {
|
|
|
|
ctx.addIssue({
|
|
|
|
code: z.ZodIssueCode.custom,
|
|
|
|
message: r.errorReason,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2024-10-13 15:25:30 -07:00
|
|
|
const baseAttachmentSchema = v.object({
|
|
|
|
id: v.string(),
|
|
|
|
type: v.string(),
|
2024-10-14 11:54:44 -07:00
|
|
|
url: v.fallback(v.pipe(v.string(), v.url()), ''),
|
|
|
|
preview_url: v.fallback(v.pipe(v.string(), v.url()), ''),
|
2024-10-14 13:50:34 -07:00
|
|
|
remote_url: v.fallback(v.nullable(v.pipe(v.string(), v.url())), null),
|
2024-10-13 15:25:30 -07:00
|
|
|
description: v.fallback(v.string(), ''),
|
|
|
|
blurhash: v.fallback(v.nullable(blurhashSchema), null),
|
2024-08-28 04:43:23 -07:00
|
|
|
|
2024-10-13 15:25:30 -07:00
|
|
|
mime_type: v.fallback(v.nullable(mimeSchema), null),
|
2024-08-28 04:43:23 -07:00
|
|
|
});
|
|
|
|
|
2024-10-13 15:25:30 -07:00
|
|
|
const imageMetaSchema = v.object({
|
2024-10-14 11:54:44 -07:00
|
|
|
width: v.number(),
|
|
|
|
height: v.number(),
|
2024-10-14 13:50:34 -07:00
|
|
|
size: v.fallback(v.nullable(v.pipe(v.string(), v.regex(/\d+x\d+$/))), null),
|
2024-10-13 15:25:30 -07:00
|
|
|
aspect: v.fallback(v.nullable(v.number()), null),
|
2024-08-28 04:43:23 -07:00
|
|
|
});
|
|
|
|
|
2024-10-14 13:50:34 -07:00
|
|
|
const imageAttachmentSchema = v.object({
|
|
|
|
...baseAttachmentSchema.entries,
|
2024-10-14 11:54:44 -07:00
|
|
|
type: v.literal('image'),
|
|
|
|
meta: v.fallback(v.object({
|
|
|
|
original: v.fallback(v.optional(imageMetaSchema), undefined),
|
|
|
|
small: v.fallback(v.optional(imageMetaSchema), undefined),
|
|
|
|
focus: v.fallback(v.optional(v.object({
|
2024-08-28 04:43:23 -07:00
|
|
|
x: z.number().min(-1).max(1),
|
|
|
|
y: z.number().min(-1).max(1),
|
2024-10-14 11:54:44 -07:00
|
|
|
})), undefined),
|
|
|
|
}), {}),
|
2024-08-28 04:43:23 -07:00
|
|
|
});
|
|
|
|
|
2024-10-14 13:50:34 -07:00
|
|
|
const videoAttachmentSchema = v.object({
|
|
|
|
...baseAttachmentSchema.entries,
|
2024-10-14 11:54:44 -07:00
|
|
|
type: v.literal('video'),
|
|
|
|
meta: v.fallback(v.object({
|
|
|
|
duration: v.fallback(v.optional(v.number()), undefined),
|
|
|
|
original: v.fallback(v.optional(imageMetaSchema.extend({
|
|
|
|
frame_rate: v.fallback(v.nullable(v.pipe(v.string(), v.regex(/\d+\/\d+$/))), null),
|
|
|
|
duration: v.fallback(v.nullable(z.number().nonnegative()), null),
|
|
|
|
})), undefined),
|
|
|
|
small: v.fallback(v.optional(imageMetaSchema), undefined),
|
2024-08-28 04:43:23 -07:00
|
|
|
// WIP: add rest
|
2024-10-14 11:54:44 -07:00
|
|
|
}), {}),
|
2024-08-28 04:43:23 -07:00
|
|
|
});
|
|
|
|
|
2024-10-14 13:50:34 -07:00
|
|
|
const gifvAttachmentSchema = v.object({
|
|
|
|
...baseAttachmentSchema.entries,
|
2024-10-14 11:54:44 -07:00
|
|
|
type: v.literal('gifv'),
|
|
|
|
meta: v.fallback(v.object({
|
|
|
|
duration: v.fallback(v.optional(v.number()), undefined),
|
|
|
|
original: v.fallback(v.optional(imageMetaSchema), undefined),
|
|
|
|
}), {}),
|
2024-08-28 04:43:23 -07:00
|
|
|
});
|
|
|
|
|
2024-10-14 13:50:34 -07:00
|
|
|
const audioAttachmentSchema = v.object({
|
|
|
|
...baseAttachmentSchema.entries,
|
2024-10-14 11:54:44 -07:00
|
|
|
type: v.literal('audio'),
|
|
|
|
meta: v.fallback(v.object({
|
|
|
|
duration: v.fallback(v.optional(v.number()), undefined),
|
|
|
|
colors: v.fallback(v.optional(v.object({
|
2024-10-13 15:25:30 -07:00
|
|
|
background: v.fallback(v.optional(v.string()), undefined),
|
|
|
|
foreground: v.fallback(v.optional(v.string()), undefined),
|
|
|
|
accent: v.fallback(v.optional(v.string()), undefined),
|
2024-10-14 11:54:44 -07:00
|
|
|
duration: v.fallback(v.optional(v.number()), undefined),
|
|
|
|
})), undefined),
|
|
|
|
original: v.fallback(v.optional(v.object({
|
|
|
|
duration: v.fallback(v.optional(v.number()), undefined),
|
2024-08-28 04:43:23 -07:00
|
|
|
bitrate: z.number().nonnegative().optional().catch(undefined),
|
2024-10-14 11:54:44 -07:00
|
|
|
})), undefined),
|
|
|
|
}), {}),
|
2024-08-28 04:43:23 -07:00
|
|
|
});
|
|
|
|
|
2024-10-14 13:50:34 -07:00
|
|
|
const unknownAttachmentSchema = v.object({
|
|
|
|
...baseAttachmentSchema.entries,
|
2024-10-14 11:54:44 -07:00
|
|
|
type: v.literal('unknown'),
|
2024-08-28 04:43:23 -07:00
|
|
|
});
|
|
|
|
|
|
|
|
/** @see {@link https://docs.joinmastodon.org/entities/MediaAttachment} */
|
2024-08-28 15:55:30 -07:00
|
|
|
const mediaAttachmentSchema = z.preprocess((data: any) => {
|
|
|
|
if (!data) return null;
|
|
|
|
|
|
|
|
return {
|
|
|
|
mime_type: data.pleroma?.mime_type,
|
|
|
|
preview_url: data.url,
|
|
|
|
...data,
|
|
|
|
};
|
2024-10-14 13:50:34 -07:00
|
|
|
}, v.variant('type', [
|
2024-08-28 04:43:23 -07:00
|
|
|
imageAttachmentSchema,
|
|
|
|
videoAttachmentSchema,
|
|
|
|
gifvAttachmentSchema,
|
|
|
|
audioAttachmentSchema,
|
|
|
|
unknownAttachmentSchema,
|
|
|
|
]));
|
|
|
|
|
2024-10-13 15:25:30 -07:00
|
|
|
type MediaAttachment = v.InferOutput<typeof mediaAttachmentSchema>;
|
2024-08-28 04:43:23 -07:00
|
|
|
|
|
|
|
export { blurhashSchema, mediaAttachmentSchema, type MediaAttachment };
|