1
0
Fork 0

Fix playlist thumbnail generation

This commit is contained in:
Chocobozzz 2023-06-19 09:56:12 +02:00
parent 109d4a7f01
commit 53d4db2a8a
No known key found for this signature in database
GPG key ID: 583A612D890159BE
7 changed files with 25 additions and 19 deletions

View file

@ -1,6 +1,6 @@
import express from 'express' import express from 'express'
import { join } from 'path'
import { scheduleRefreshIfNeeded } from '@server/lib/activitypub/playlists' import { scheduleRefreshIfNeeded } from '@server/lib/activitypub/playlists'
import { VideoMiniaturePermanentFileCache } from '@server/lib/files-cache'
import { Hooks } from '@server/lib/plugins/hooks' import { Hooks } from '@server/lib/plugins/hooks'
import { getServerActor } from '@server/models/application/application' import { getServerActor } from '@server/models/application/application'
import { MVideoPlaylistFull, MVideoPlaylistThumbnail, MVideoThumbnail } from '@server/types/models' 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 { createReqFiles } from '../../helpers/express-utils'
import { logger } from '../../helpers/logger' import { logger } from '../../helpers/logger'
import { getFormattedObjects } from '../../helpers/utils' import { getFormattedObjects } from '../../helpers/utils'
import { CONFIG } from '../../initializers/config'
import { MIMETYPES, VIDEO_PLAYLIST_PRIVACIES } from '../../initializers/constants' import { MIMETYPES, VIDEO_PLAYLIST_PRIVACIES } from '../../initializers/constants'
import { sequelizeTypescript } from '../../initializers/database' import { sequelizeTypescript } from '../../initializers/database'
import { sendCreateVideoPlaylist, sendDeleteVideoPlaylist, sendUpdateVideoPlaylist } from '../../lib/activitypub/send' import { sendCreateVideoPlaylist, sendDeleteVideoPlaylist, sendUpdateVideoPlaylist } from '../../lib/activitypub/send'
@ -496,7 +495,10 @@ async function generateThumbnailForPlaylist (videoPlaylist: MVideoPlaylistThumbn
return 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({ const thumbnailModel = await updateLocalPlaylistMiniatureFromExisting({
inputPath, inputPath,
playlist: videoPlaylist, playlist: videoPlaylist,

View file

@ -4,7 +4,7 @@ import { retryTransactionWrapper } from '@server/helpers/database-utils'
import { logger, loggerTagsFactory } from '@server/helpers/logger' import { logger, loggerTagsFactory } from '@server/helpers/logger'
import { CRAWL_REQUEST_CONCURRENCY } from '@server/initializers/constants' import { CRAWL_REQUEST_CONCURRENCY } from '@server/initializers/constants'
import { sequelizeTypescript } from '@server/initializers/database' 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 { VideoPlaylistModel } from '@server/models/video/video-playlist'
import { VideoPlaylistElementModel } from '@server/models/video/video-playlist-element' import { VideoPlaylistElementModel } from '@server/models/video/video-playlist-element'
import { FilteredModelAttributes } from '@server/types' import { FilteredModelAttributes } from '@server/types'
@ -104,7 +104,7 @@ async function updatePlaylistThumbnail (playlistObject: PlaylistObject, playlist
let thumbnailModel: MThumbnail let thumbnailModel: MThumbnail
try { try {
thumbnailModel = await updatePlaylistMiniatureFromUrl({ downloadUrl: playlistObject.icon.url, playlist }) thumbnailModel = await updateRemotePlaylistMiniatureFromUrl({ downloadUrl: playlistObject.icon.url, playlist })
await playlist.setAndSaveThumbnail(thumbnailModel, undefined) await playlist.setAndSaveThumbnail(thumbnailModel, undefined)
} catch (err) { } catch (err) {
logger.warn('Cannot set thumbnail of %s.', playlistObject.id, { err, ...lTags(playlistObject.id, playlist.uuid, playlist.url) }) logger.warn('Cannot set thumbnail of %s.', playlistObject.id, { err, ...lTags(playlistObject.id, playlist.uuid, playlist.url) })

View file

@ -1,7 +1,7 @@
import { CreationAttributes, Transaction } from 'sequelize/types' import { CreationAttributes, Transaction } from 'sequelize/types'
import { deleteAllModels, filterNonExistingModels } from '@server/helpers/database-utils' import { deleteAllModels, filterNonExistingModels } from '@server/helpers/database-utils'
import { logger, LoggerTagsFn } from '@server/helpers/logger' 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 { setVideoTags } from '@server/lib/video'
import { StoryboardModel } from '@server/models/video/storyboard' import { StoryboardModel } from '@server/models/video/storyboard'
import { VideoCaptionModel } from '@server/models/video/video-caption' import { VideoCaptionModel } from '@server/models/video/video-caption'
@ -48,7 +48,7 @@ export abstract class APVideoAbstractBuilder {
return undefined return undefined
} }
const miniatureModel = updateRemoteThumbnail({ const miniatureModel = updateRemoteVideoThumbnail({
fileUrl: miniatureIcon.url, fileUrl: miniatureIcon.url,
video, video,
type: ThumbnailType.MINIATURE, type: ThumbnailType.MINIATURE,
@ -63,12 +63,12 @@ export abstract class APVideoAbstractBuilder {
const previewIcon = getPreviewFromIcons(this.videoObject) const previewIcon = getPreviewFromIcons(this.videoObject)
if (!previewIcon) return if (!previewIcon) return
const previewModel = updateRemoteThumbnail({ const previewModel = updateRemoteVideoThumbnail({
fileUrl: previewIcon.url, fileUrl: previewIcon.url,
video, video,
type: ThumbnailType.PREVIEW, type: ThumbnailType.PREVIEW,
size: previewIcon, 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) await video.addAndSaveThumbnail(previewModel, t)

View file

@ -66,10 +66,10 @@ export abstract class AbstractPermanentFileCache <M extends ImageModel> {
}) })
} }
private async downloadRemoteFile (image: M) { async downloadRemoteFile (image: M) {
logger.info('Download remote image %s lazily.', image.fileUrl) logger.info('Download remote image %s lazily.', image.fileUrl)
await this.downloadImage({ const destination = await this.downloadImage({
filename: image.filename, filename: image.filename,
fileUrl: image.fileUrl, fileUrl: image.fileUrl,
size: this.getImageSize(image) size: this.getImageSize(image)
@ -78,6 +78,8 @@ export abstract class AbstractPermanentFileCache <M extends ImageModel> {
image.onDisk = true image.onDisk = true
image.save() image.save()
.catch(err => logger.error('Cannot save new image disk state.', { err })) .catch(err => logger.error('Cannot save new image disk state.', { err }))
return destination
} }
private onServeError (options: { private onServeError (options: {

View file

@ -39,7 +39,7 @@ function updateLocalPlaylistMiniatureFromExisting (options: {
}) })
} }
function updatePlaylistMiniatureFromUrl (options: { function updateRemotePlaylistMiniatureFromUrl (options: {
downloadUrl: string downloadUrl: string
playlist: MVideoPlaylistThumbnail playlist: MVideoPlaylistThumbnail
size?: ImageSize size?: ImageSize
@ -127,7 +127,7 @@ function generateLocalVideoMiniature (options: {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
function updateVideoMiniatureFromUrl (options: { function updateLocalVideoMiniatureFromUrl (options: {
downloadUrl: string downloadUrl: string
video: MVideoThumbnail video: MVideoThumbnail
type: ThumbnailType type: ThumbnailType
@ -159,7 +159,7 @@ function updateVideoMiniatureFromUrl (options: {
return updateThumbnailFromFunction({ thumbnailCreator, filename, height, width, type, existingThumbnail, fileUrl, onDisk: true }) return updateThumbnailFromFunction({ thumbnailCreator, filename, height, width, type, existingThumbnail, fileUrl, onDisk: true })
} }
function updateRemoteThumbnail (options: { function updateRemoteVideoThumbnail (options: {
fileUrl: string fileUrl: string
video: MVideoThumbnail video: MVideoThumbnail
type: ThumbnailType type: ThumbnailType
@ -189,10 +189,10 @@ function updateRemoteThumbnail (options: {
export { export {
generateLocalVideoMiniature, generateLocalVideoMiniature,
updateVideoMiniatureFromUrl, updateLocalVideoMiniatureFromUrl,
updateLocalVideoMiniatureFromExisting, updateLocalVideoMiniatureFromExisting,
updateRemoteThumbnail, updateRemoteVideoThumbnail,
updatePlaylistMiniatureFromUrl, updateRemotePlaylistMiniatureFromUrl,
updateLocalPlaylistMiniatureFromExisting updateLocalPlaylistMiniatureFromExisting
} }

View file

@ -29,7 +29,7 @@ import {
} from '@server/types/models' } from '@server/types/models'
import { ThumbnailType, VideoImportCreate, VideoImportPayload, VideoImportState, VideoPrivacy, VideoState } from '@shared/models' import { ThumbnailType, VideoImportCreate, VideoImportPayload, VideoImportState, VideoPrivacy, VideoState } from '@shared/models'
import { getLocalVideoActivityPubUrl } from './activitypub/url' import { getLocalVideoActivityPubUrl } from './activitypub/url'
import { updateLocalVideoMiniatureFromExisting, updateVideoMiniatureFromUrl } from './thumbnail' import { updateLocalVideoMiniatureFromExisting, updateLocalVideoMiniatureFromUrl } from './thumbnail'
import { VideoPasswordModel } from '@server/models/video/video-password' import { VideoPasswordModel } from '@server/models/video/video-password'
class YoutubeDlImportError extends Error { class YoutubeDlImportError extends Error {
@ -266,7 +266,7 @@ async function forgeThumbnail ({ inputPath, video, downloadUrl, type }: {
if (downloadUrl) { if (downloadUrl) {
try { try {
return await updateVideoMiniatureFromUrl({ downloadUrl, video, type }) return await updateLocalVideoMiniatureFromUrl({ downloadUrl, video, type })
} catch (err) { } catch (err) {
logger.warn('Cannot process thumbnail %s from youtube-dl.', downloadUrl, { err }) logger.warn('Cannot process thumbnail %s from youtube-dl.', downloadUrl, { err })
} }

View file

@ -24,6 +24,8 @@ async function downloadImage (options: {
throw err throw err
} }
return destPath
} }
module.exports = downloadImage module.exports = downloadImage