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-08-28 04:43:23 -07:00
|
|
|
url: z.string().url().catch(''),
|
|
|
|
preview_url: z.string().url().catch(''),
|
|
|
|
remote_url: z.string().url().nullable().catch(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-08-28 04:43:23 -07:00
|
|
|
width: z.number(),
|
|
|
|
height: z.number(),
|
|
|
|
size: z.string().regex(/\d+x\d+$/).nullable().catch(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
|
|
|
});
|
|
|
|
|
|
|
|
const imageAttachmentSchema = baseAttachmentSchema.extend({
|
|
|
|
type: z.literal('image'),
|
2024-10-13 15:25:30 -07:00
|
|
|
meta: v.object({
|
2024-08-28 04:43:23 -07:00
|
|
|
original: imageMetaSchema.optional().catch(undefined),
|
|
|
|
small: imageMetaSchema.optional().catch(undefined),
|
2024-10-13 15:25:30 -07:00
|
|
|
focus: v.object({
|
2024-08-28 04:43:23 -07:00
|
|
|
x: z.number().min(-1).max(1),
|
|
|
|
y: z.number().min(-1).max(1),
|
|
|
|
}).optional().catch(undefined),
|
|
|
|
}).catch({}),
|
|
|
|
});
|
|
|
|
|
|
|
|
const videoAttachmentSchema = baseAttachmentSchema.extend({
|
|
|
|
type: z.literal('video'),
|
2024-10-13 15:25:30 -07:00
|
|
|
meta: v.object({
|
2024-08-28 04:43:23 -07:00
|
|
|
duration: z.number().optional().catch(undefined),
|
|
|
|
original: imageMetaSchema.extend({
|
|
|
|
frame_rate: z.string().regex(/\d+\/\d+$/).nullable().catch(null),
|
|
|
|
duration: z.number().nonnegative().nullable().catch(null),
|
|
|
|
}).optional().catch(undefined),
|
|
|
|
small: imageMetaSchema.optional().catch(undefined),
|
|
|
|
// WIP: add rest
|
|
|
|
}).catch({}),
|
|
|
|
});
|
|
|
|
|
|
|
|
const gifvAttachmentSchema = baseAttachmentSchema.extend({
|
|
|
|
type: z.literal('gifv'),
|
2024-10-13 15:25:30 -07:00
|
|
|
meta: v.object({
|
2024-08-28 04:43:23 -07:00
|
|
|
duration: z.number().optional().catch(undefined),
|
|
|
|
original: imageMetaSchema.optional().catch(undefined),
|
|
|
|
}).catch({}),
|
|
|
|
});
|
|
|
|
|
|
|
|
const audioAttachmentSchema = baseAttachmentSchema.extend({
|
|
|
|
type: z.literal('audio'),
|
2024-10-13 15:25:30 -07:00
|
|
|
meta: v.object({
|
2024-08-28 04:43:23 -07:00
|
|
|
duration: z.number().optional().catch(undefined),
|
2024-10-13 15:25:30 -07:00
|
|
|
colors: v.object({
|
|
|
|
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-08-28 04:43:23 -07:00
|
|
|
duration: z.number().optional().catch(undefined),
|
|
|
|
}).optional().catch(undefined),
|
2024-10-13 15:25:30 -07:00
|
|
|
original: v.object({
|
2024-08-28 04:43:23 -07:00
|
|
|
duration: z.number().optional().catch(undefined),
|
|
|
|
bitrate: z.number().nonnegative().optional().catch(undefined),
|
|
|
|
}).optional().catch(undefined),
|
|
|
|
}).catch({}),
|
|
|
|
});
|
|
|
|
|
|
|
|
const unknownAttachmentSchema = baseAttachmentSchema.extend({
|
|
|
|
type: z.literal('unknown'),
|
|
|
|
});
|
|
|
|
|
|
|
|
/** @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,
|
|
|
|
};
|
|
|
|
}, z.discriminatedUnion('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 };
|