feat: initial builtin video thumbnail generator implementation
when you disable "cache external media", video thumbnails off of remote instances do not get generated. misskey has a videoThumbnailGenerator config option to point to an external service to make that happen, but they do not provide any kind of implementation (or any documentation beyond a comment on the config file) this provides a video thumbnail generator that uses the same thumbnail generation code path used in local files, providing a quick and dirty solution to instances that want video thumbnails without the need to store external media permanently the eventual goal of this is to be the fallback implementation when that config option is unset. the current implementation is extremely bare bones and performs no caching or any other optimizations whatsoever
This commit is contained in:
parent
c8a7e27e70
commit
1cd59c1ee3
|
@ -81,6 +81,13 @@ export class FileServerService {
|
|||
.catch(err => this.errorHandler(request, reply, err));
|
||||
});
|
||||
|
||||
fastify.get<{
|
||||
Querystring: { url: string; };
|
||||
}>('/proxy/thumbnail.webp', async (request, reply) => {
|
||||
return await this.videoThumbnailHandler(request, reply)
|
||||
.catch(err => this.errorHandler(request, reply, err));
|
||||
});
|
||||
|
||||
fastify.get<{
|
||||
Params: { url: string; };
|
||||
Querystring: { url?: string; };
|
||||
|
@ -371,6 +378,51 @@ export class FileServerService {
|
|||
}
|
||||
}
|
||||
|
||||
@bindThis
|
||||
private async videoThumbnailHandler(request: FastifyRequest<{ Querystring: { url: string; }; }>, reply: FastifyReply) {
|
||||
const file = await this.getStreamAndTypeFromUrl(request.query.url);
|
||||
|
||||
if (file === '404') {
|
||||
reply.code(404);
|
||||
reply.header('Cache-Control', 'max-age=86400');
|
||||
return reply.sendFile('/dummy.png', assets); // TODO: return webp
|
||||
}
|
||||
|
||||
if (file === '204') {
|
||||
reply.code(204);
|
||||
reply.header('Cache-Control', 'max-age=86400');
|
||||
return;
|
||||
}
|
||||
|
||||
if (file.file?.thumbnailUrl) {
|
||||
return await reply.redirect(301, file.file.thumbnailUrl);
|
||||
}
|
||||
|
||||
if (!file.mime.startsWith('video/')) {
|
||||
if ('cleanup' in file) {
|
||||
file.cleanup();
|
||||
}
|
||||
|
||||
reply.code(400);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const image = await this.videoProcessingService.generateVideoThumbnail(file.path);
|
||||
|
||||
if ('cleanup' in file) {
|
||||
file.cleanup();
|
||||
}
|
||||
|
||||
reply.header('Content-Type', image.type);
|
||||
reply.header('Cache-Control', 'max-age=31536000, immutable');
|
||||
return image.data;
|
||||
} catch (e) {
|
||||
if ('cleanup' in file) file.cleanup();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@bindThis
|
||||
private async getStreamAndTypeFromUrl(url: string): Promise<
|
||||
{ state: 'remote'; fileRole?: 'thumbnail' | 'webpublic' | 'original'; file?: MiDriveFile; mime: string; ext: string | null; path: string; cleanup: () => void; filename: string; }
|
||||
|
|
Loading…
Reference in New Issue