diff --git a/config/default.yaml b/config/default.yaml index 3a4891eff..81e22e350 100644 --- a/config/default.yaml +++ b/config/default.yaml @@ -417,6 +417,14 @@ remote_runners: live: '30 seconds' vod: '2 minutes' +thumbnails: + # When automatically generating a thumbnail from the video + generation_from_video: + # How many frames to analyze at the middle of the video to select the most appropriate one + # Increasing this value will increase CPU and memory usage when generating the thumbnail, especially for high video resolution + # Minimum value is 2 + frames_to_analyze: 50 + cache: previews: size: 500 # Max number of previews you want to cache diff --git a/config/production.yaml.example b/config/production.yaml.example index 90c31072e..827e4219c 100644 --- a/config/production.yaml.example +++ b/config/production.yaml.example @@ -415,6 +415,14 @@ remote_runners: live: '30 seconds' vod: '2 minutes' +thumbnails: + # When automatically generating a thumbnail from the video + generation_from_video: + # How many frames to analyze at the middle of the video to select the most appropriate one + # Increasing this value will increase CPU and memory usage when generating the thumbnail, especially for high video resolution + # Minimum value is 2 + frames_to_analyze: 50 + ############################################################################### # # From this point, almost all following keys can be overridden by the web interface diff --git a/packages/ffmpeg/src/ffmpeg-images.ts b/packages/ffmpeg/src/ffmpeg-images.ts index b6aaa5db5..739c96141 100644 --- a/packages/ffmpeg/src/ffmpeg-images.ts +++ b/packages/ffmpeg/src/ffmpeg-images.ts @@ -39,16 +39,17 @@ export class FFmpegImage { async generateThumbnailFromVideo (options: { fromPath: string output: string + framesToAnalyze: number ffprobe?: FfprobeData }) { - const { fromPath, output, ffprobe } = options + const { fromPath, output, ffprobe, framesToAnalyze } = options let duration = await getVideoStreamDuration(fromPath, ffprobe) if (isNaN(duration)) duration = 0 this.commandWrapper.buildCommand(fromPath) .seekInput(duration / 2) - .videoFilter('thumbnail=50') + .videoFilter('thumbnail=' + framesToAnalyze) .outputOption('-frames:v 1') .output(output) diff --git a/server/core/initializers/checker-after-init.ts b/server/core/initializers/checker-after-init.ts index 5e7e513f1..3a2699eeb 100644 --- a/server/core/initializers/checker-after-init.ts +++ b/server/core/initializers/checker-after-init.ts @@ -53,6 +53,7 @@ function checkConfig () { checkLiveConfig() checkObjectStorageConfig() checkVideoStudioConfig() + checkThumbnailsConfig() } // We get db by param to not import it in this file (import orders) @@ -331,3 +332,9 @@ function checkVideoStudioConfig () { throw new Error('Video studio cannot be enabled if transcoding is disabled') } } + +function checkThumbnailsConfig () { + if (CONFIG.THUMBNAILS.GENERATION_FROM_VIDEO.FRAMES_TO_ANALYZE < 2) { + throw new Error('thumbnails.generation_from_video.frames_to_analyze must be a number greater than 1') + } +} diff --git a/server/core/initializers/checker-before-init.ts b/server/core/initializers/checker-before-init.ts index f33da0914..e6fd56461 100644 --- a/server/core/initializers/checker-before-init.ts +++ b/server/core/initializers/checker-before-init.ts @@ -39,6 +39,7 @@ function checkMissedConfig () { 'video_studio.enabled', 'video_studio.remote_runners.enabled', 'video_file.update.enabled', 'remote_runners.stalled_jobs.vod', 'remote_runners.stalled_jobs.live', + 'thumbnails.generation_from_video.frames_to_analyze', 'import.videos.http.enabled', 'import.videos.torrent.enabled', 'import.videos.concurrency', 'import.videos.timeout', 'import.video_channel_synchronization.enabled', 'import.video_channel_synchronization.max_per_user', 'import.video_channel_synchronization.check_interval', 'import.video_channel_synchronization.videos_limit_per_synchronization', diff --git a/server/core/initializers/config.ts b/server/core/initializers/config.ts index a96c7eba9..a5380308a 100644 --- a/server/core/initializers/config.ts +++ b/server/core/initializers/config.ts @@ -344,6 +344,11 @@ const CONFIG = { VOD: parseDurationToMs(config.get('remote_runners.stalled_jobs.vod')) } }, + THUMBNAILS: { + GENERATION_FROM_VIDEO: { + FRAMES_TO_ANALYZE: config.get('thumbnails.generation_from_video.frames_to_analyze') + } + }, ADMIN: { get EMAIL () { return config.get('admin.email') } }, diff --git a/server/core/lib/thumbnail.ts b/server/core/lib/thumbnail.ts index 9aeade32f..1362f9e4d 100644 --- a/server/core/lib/thumbnail.ts +++ b/server/core/lib/thumbnail.ts @@ -379,7 +379,8 @@ async function generateImageFromVideoFile (options: { const pendingImagePath = join(folder, pendingImageName) try { - await generateThumbnailFromVideo({ fromPath, output: pendingImagePath, ffprobe }) + const framesToAnalyze = CONFIG.THUMBNAILS.GENERATION_FROM_VIDEO.FRAMES_TO_ANALYZE + await generateThumbnailFromVideo({ fromPath, output: pendingImagePath, framesToAnalyze, ffprobe }) const destination = join(folder, imageName) await processImageFromWorker({ path: pendingImagePath, destination, newSize: size })