From 53d4db2a8a11407165da6525cad3198439686d36 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Mon, 19 Jun 2023 09:56:12 +0200 Subject: [PATCH] Fix playlist thumbnail generation --- server/controllers/api/video-playlist.ts | 8 +++++--- server/lib/activitypub/playlists/create-update.ts | 4 ++-- .../activitypub/videos/shared/abstract-builder.ts | 8 ++++---- .../shared/abstract-permanent-file-cache.ts | 6 ++++-- server/lib/thumbnail.ts | 12 ++++++------ server/lib/video-pre-import.ts | 4 ++-- server/lib/worker/workers/image-downloader.ts | 2 ++ 7 files changed, 25 insertions(+), 19 deletions(-) diff --git a/server/controllers/api/video-playlist.ts b/server/controllers/api/video-playlist.ts index 1568ee597..304f1ddfb 100644 --- a/server/controllers/api/video-playlist.ts +++ b/server/controllers/api/video-playlist.ts @@ -1,6 +1,6 @@ import express from 'express' -import { join } from 'path' import { scheduleRefreshIfNeeded } from '@server/lib/activitypub/playlists' +import { VideoMiniaturePermanentFileCache } from '@server/lib/files-cache' import { Hooks } from '@server/lib/plugins/hooks' import { getServerActor } from '@server/models/application/application' import { MVideoPlaylistFull, MVideoPlaylistThumbnail, MVideoThumbnail } from '@server/types/models' @@ -18,7 +18,6 @@ import { resetSequelizeInstance } from '../../helpers/database-utils' import { createReqFiles } from '../../helpers/express-utils' import { logger } from '../../helpers/logger' import { getFormattedObjects } from '../../helpers/utils' -import { CONFIG } from '../../initializers/config' import { MIMETYPES, VIDEO_PLAYLIST_PRIVACIES } from '../../initializers/constants' import { sequelizeTypescript } from '../../initializers/database' import { sendCreateVideoPlaylist, sendDeleteVideoPlaylist, sendUpdateVideoPlaylist } from '../../lib/activitypub/send' @@ -496,7 +495,10 @@ async function generateThumbnailForPlaylist (videoPlaylist: MVideoPlaylistThumbn return } - const inputPath = join(CONFIG.STORAGE.THUMBNAILS_DIR, videoMiniature.filename) + // Ensure the file is on disk + const videoMiniaturePermanentFileCache = new VideoMiniaturePermanentFileCache() + const inputPath = await videoMiniaturePermanentFileCache.downloadRemoteFile(videoMiniature) + const thumbnailModel = await updateLocalPlaylistMiniatureFromExisting({ inputPath, playlist: videoPlaylist, diff --git a/server/lib/activitypub/playlists/create-update.ts b/server/lib/activitypub/playlists/create-update.ts index 920d3943a..b24299f29 100644 --- a/server/lib/activitypub/playlists/create-update.ts +++ b/server/lib/activitypub/playlists/create-update.ts @@ -4,7 +4,7 @@ import { retryTransactionWrapper } from '@server/helpers/database-utils' import { logger, loggerTagsFactory } from '@server/helpers/logger' import { CRAWL_REQUEST_CONCURRENCY } from '@server/initializers/constants' import { sequelizeTypescript } from '@server/initializers/database' -import { updatePlaylistMiniatureFromUrl } from '@server/lib/thumbnail' +import { updateRemotePlaylistMiniatureFromUrl } from '@server/lib/thumbnail' import { VideoPlaylistModel } from '@server/models/video/video-playlist' import { VideoPlaylistElementModel } from '@server/models/video/video-playlist-element' import { FilteredModelAttributes } from '@server/types' @@ -104,7 +104,7 @@ async function updatePlaylistThumbnail (playlistObject: PlaylistObject, playlist let thumbnailModel: MThumbnail try { - thumbnailModel = await updatePlaylistMiniatureFromUrl({ downloadUrl: playlistObject.icon.url, playlist }) + thumbnailModel = await updateRemotePlaylistMiniatureFromUrl({ downloadUrl: playlistObject.icon.url, playlist }) await playlist.setAndSaveThumbnail(thumbnailModel, undefined) } catch (err) { logger.warn('Cannot set thumbnail of %s.', playlistObject.id, { err, ...lTags(playlistObject.id, playlist.uuid, playlist.url) }) diff --git a/server/lib/activitypub/videos/shared/abstract-builder.ts b/server/lib/activitypub/videos/shared/abstract-builder.ts index 4f74316d3..8b6a7fd75 100644 --- a/server/lib/activitypub/videos/shared/abstract-builder.ts +++ b/server/lib/activitypub/videos/shared/abstract-builder.ts @@ -1,7 +1,7 @@ import { CreationAttributes, Transaction } from 'sequelize/types' import { deleteAllModels, filterNonExistingModels } from '@server/helpers/database-utils' import { logger, LoggerTagsFn } from '@server/helpers/logger' -import { updateRemoteThumbnail } from '@server/lib/thumbnail' +import { updateRemoteVideoThumbnail } from '@server/lib/thumbnail' import { setVideoTags } from '@server/lib/video' import { StoryboardModel } from '@server/models/video/storyboard' import { VideoCaptionModel } from '@server/models/video/video-caption' @@ -48,7 +48,7 @@ export abstract class APVideoAbstractBuilder { return undefined } - const miniatureModel = updateRemoteThumbnail({ + const miniatureModel = updateRemoteVideoThumbnail({ fileUrl: miniatureIcon.url, video, type: ThumbnailType.MINIATURE, @@ -63,12 +63,12 @@ export abstract class APVideoAbstractBuilder { const previewIcon = getPreviewFromIcons(this.videoObject) if (!previewIcon) return - const previewModel = updateRemoteThumbnail({ + const previewModel = updateRemoteVideoThumbnail({ fileUrl: previewIcon.url, video, type: ThumbnailType.PREVIEW, size: previewIcon, - onDisk: false // Don't fetch the preview that could be big, create a placeholder instead + onDisk: false // Lazy download remote previews }) await video.addAndSaveThumbnail(previewModel, t) diff --git a/server/lib/files-cache/shared/abstract-permanent-file-cache.ts b/server/lib/files-cache/shared/abstract-permanent-file-cache.ts index 22596c3eb..297461035 100644 --- a/server/lib/files-cache/shared/abstract-permanent-file-cache.ts +++ b/server/lib/files-cache/shared/abstract-permanent-file-cache.ts @@ -66,10 +66,10 @@ export abstract class AbstractPermanentFileCache { }) } - private async downloadRemoteFile (image: M) { + async downloadRemoteFile (image: M) { logger.info('Download remote image %s lazily.', image.fileUrl) - await this.downloadImage({ + const destination = await this.downloadImage({ filename: image.filename, fileUrl: image.fileUrl, size: this.getImageSize(image) @@ -78,6 +78,8 @@ export abstract class AbstractPermanentFileCache { image.onDisk = true image.save() .catch(err => logger.error('Cannot save new image disk state.', { err })) + + return destination } private onServeError (options: { diff --git a/server/lib/thumbnail.ts b/server/lib/thumbnail.ts index 90f5dc2c8..d95442795 100644 --- a/server/lib/thumbnail.ts +++ b/server/lib/thumbnail.ts @@ -39,7 +39,7 @@ function updateLocalPlaylistMiniatureFromExisting (options: { }) } -function updatePlaylistMiniatureFromUrl (options: { +function updateRemotePlaylistMiniatureFromUrl (options: { downloadUrl: string playlist: MVideoPlaylistThumbnail size?: ImageSize @@ -127,7 +127,7 @@ function generateLocalVideoMiniature (options: { // --------------------------------------------------------------------------- -function updateVideoMiniatureFromUrl (options: { +function updateLocalVideoMiniatureFromUrl (options: { downloadUrl: string video: MVideoThumbnail type: ThumbnailType @@ -159,7 +159,7 @@ function updateVideoMiniatureFromUrl (options: { return updateThumbnailFromFunction({ thumbnailCreator, filename, height, width, type, existingThumbnail, fileUrl, onDisk: true }) } -function updateRemoteThumbnail (options: { +function updateRemoteVideoThumbnail (options: { fileUrl: string video: MVideoThumbnail type: ThumbnailType @@ -189,10 +189,10 @@ function updateRemoteThumbnail (options: { export { generateLocalVideoMiniature, - updateVideoMiniatureFromUrl, + updateLocalVideoMiniatureFromUrl, updateLocalVideoMiniatureFromExisting, - updateRemoteThumbnail, - updatePlaylistMiniatureFromUrl, + updateRemoteVideoThumbnail, + updateRemotePlaylistMiniatureFromUrl, updateLocalPlaylistMiniatureFromExisting } diff --git a/server/lib/video-pre-import.ts b/server/lib/video-pre-import.ts index 1471d4091..381f1f535 100644 --- a/server/lib/video-pre-import.ts +++ b/server/lib/video-pre-import.ts @@ -29,7 +29,7 @@ import { } from '@server/types/models' import { ThumbnailType, VideoImportCreate, VideoImportPayload, VideoImportState, VideoPrivacy, VideoState } from '@shared/models' import { getLocalVideoActivityPubUrl } from './activitypub/url' -import { updateLocalVideoMiniatureFromExisting, updateVideoMiniatureFromUrl } from './thumbnail' +import { updateLocalVideoMiniatureFromExisting, updateLocalVideoMiniatureFromUrl } from './thumbnail' import { VideoPasswordModel } from '@server/models/video/video-password' class YoutubeDlImportError extends Error { @@ -266,7 +266,7 @@ async function forgeThumbnail ({ inputPath, video, downloadUrl, type }: { if (downloadUrl) { try { - return await updateVideoMiniatureFromUrl({ downloadUrl, video, type }) + return await updateLocalVideoMiniatureFromUrl({ downloadUrl, video, type }) } catch (err) { logger.warn('Cannot process thumbnail %s from youtube-dl.', downloadUrl, { err }) } diff --git a/server/lib/worker/workers/image-downloader.ts b/server/lib/worker/workers/image-downloader.ts index 4b32f723e..209594589 100644 --- a/server/lib/worker/workers/image-downloader.ts +++ b/server/lib/worker/workers/image-downloader.ts @@ -24,6 +24,8 @@ async function downloadImage (options: { throw err } + + return destPath } module.exports = downloadImage