aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/lib/thumbnail.ts
diff options
context:
space:
mode:
Diffstat (limited to 'server/lib/thumbnail.ts')
-rw-r--r--server/lib/thumbnail.ts327
1 files changed, 0 insertions, 327 deletions
diff --git a/server/lib/thumbnail.ts b/server/lib/thumbnail.ts
deleted file mode 100644
index 0b98da14f..000000000
--- a/server/lib/thumbnail.ts
+++ /dev/null
@@ -1,327 +0,0 @@
1import { join } from 'path'
2import { ThumbnailType } from '@shared/models'
3import { generateImageFilename, generateImageFromVideoFile } from '../helpers/image-utils'
4import { CONFIG } from '../initializers/config'
5import { ASSETS_PATH, PREVIEWS_SIZE, THUMBNAILS_SIZE } from '../initializers/constants'
6import { ThumbnailModel } from '../models/video/thumbnail'
7import { MVideoFile, MVideoThumbnail, MVideoUUID, MVideoWithAllFiles } from '../types/models'
8import { MThumbnail } from '../types/models/video/thumbnail'
9import { MVideoPlaylistThumbnail } from '../types/models/video/video-playlist'
10import { VideoPathManager } from './video-path-manager'
11import { downloadImageFromWorker, processImageFromWorker } from './worker/parent-process'
12
13type ImageSize = { height?: number, width?: number }
14
15function updateLocalPlaylistMiniatureFromExisting (options: {
16 inputPath: string
17 playlist: MVideoPlaylistThumbnail
18 automaticallyGenerated: boolean
19 keepOriginal?: boolean // default to false
20 size?: ImageSize
21}) {
22 const { inputPath, playlist, automaticallyGenerated, keepOriginal = false, size } = options
23 const { filename, outputPath, height, width, existingThumbnail } = buildMetadataFromPlaylist(playlist, size)
24 const type = ThumbnailType.MINIATURE
25
26 const thumbnailCreator = () => {
27 return processImageFromWorker({ path: inputPath, destination: outputPath, newSize: { width, height }, keepOriginal })
28 }
29
30 return updateThumbnailFromFunction({
31 thumbnailCreator,
32 filename,
33 height,
34 width,
35 type,
36 automaticallyGenerated,
37 onDisk: true,
38 existingThumbnail
39 })
40}
41
42function updateRemotePlaylistMiniatureFromUrl (options: {
43 downloadUrl: string
44 playlist: MVideoPlaylistThumbnail
45 size?: ImageSize
46}) {
47 const { downloadUrl, playlist, size } = options
48 const { filename, basePath, height, width, existingThumbnail } = buildMetadataFromPlaylist(playlist, size)
49 const type = ThumbnailType.MINIATURE
50
51 // Only save the file URL if it is a remote playlist
52 const fileUrl = playlist.isOwned()
53 ? null
54 : downloadUrl
55
56 const thumbnailCreator = () => {
57 return downloadImageFromWorker({ url: downloadUrl, destDir: basePath, destName: filename, size: { width, height } })
58 }
59
60 return updateThumbnailFromFunction({ thumbnailCreator, filename, height, width, type, existingThumbnail, fileUrl, onDisk: true })
61}
62
63function updateLocalVideoMiniatureFromExisting (options: {
64 inputPath: string
65 video: MVideoThumbnail
66 type: ThumbnailType
67 automaticallyGenerated: boolean
68 size?: ImageSize
69 keepOriginal?: boolean // default to false
70}) {
71 const { inputPath, video, type, automaticallyGenerated, size, keepOriginal = false } = options
72
73 const { filename, outputPath, height, width, existingThumbnail } = buildMetadataFromVideo(video, type, size)
74
75 const thumbnailCreator = () => {
76 return processImageFromWorker({ path: inputPath, destination: outputPath, newSize: { width, height }, keepOriginal })
77 }
78
79 return updateThumbnailFromFunction({
80 thumbnailCreator,
81 filename,
82 height,
83 width,
84 type,
85 automaticallyGenerated,
86 existingThumbnail,
87 onDisk: true
88 })
89}
90
91function generateLocalVideoMiniature (options: {
92 video: MVideoThumbnail
93 videoFile: MVideoFile
94 type: ThumbnailType
95}) {
96 const { video, videoFile, type } = options
97
98 return VideoPathManager.Instance.makeAvailableVideoFile(videoFile.withVideoOrPlaylist(video), input => {
99 const { filename, basePath, height, width, existingThumbnail, outputPath } = buildMetadataFromVideo(video, type)
100
101 const thumbnailCreator = videoFile.isAudio()
102 ? () => processImageFromWorker({
103 path: ASSETS_PATH.DEFAULT_AUDIO_BACKGROUND,
104 destination: outputPath,
105 newSize: { width, height },
106 keepOriginal: true
107 })
108 : () => generateImageFromVideoFile({
109 fromPath: input,
110 folder: basePath,
111 imageName: filename,
112 size: { height, width }
113 })
114
115 return updateThumbnailFromFunction({
116 thumbnailCreator,
117 filename,
118 height,
119 width,
120 type,
121 automaticallyGenerated: true,
122 onDisk: true,
123 existingThumbnail
124 })
125 })
126}
127
128// ---------------------------------------------------------------------------
129
130function updateLocalVideoMiniatureFromUrl (options: {
131 downloadUrl: string
132 video: MVideoThumbnail
133 type: ThumbnailType
134 size?: ImageSize
135}) {
136 const { downloadUrl, video, type, size } = options
137 const { filename: updatedFilename, basePath, height, width, existingThumbnail } = buildMetadataFromVideo(video, type, size)
138
139 // Only save the file URL if it is a remote video
140 const fileUrl = video.isOwned()
141 ? null
142 : downloadUrl
143
144 const thumbnailUrlChanged = hasThumbnailUrlChanged(existingThumbnail, downloadUrl, video)
145
146 // Do not change the thumbnail filename if the file did not change
147 const filename = thumbnailUrlChanged
148 ? updatedFilename
149 : existingThumbnail.filename
150
151 const thumbnailCreator = () => {
152 if (thumbnailUrlChanged) {
153 return downloadImageFromWorker({ url: downloadUrl, destDir: basePath, destName: filename, size: { width, height } })
154 }
155
156 return Promise.resolve()
157 }
158
159 return updateThumbnailFromFunction({ thumbnailCreator, filename, height, width, type, existingThumbnail, fileUrl, onDisk: true })
160}
161
162function updateRemoteVideoThumbnail (options: {
163 fileUrl: string
164 video: MVideoThumbnail
165 type: ThumbnailType
166 size: ImageSize
167 onDisk: boolean
168}) {
169 const { fileUrl, video, type, size, onDisk } = options
170 const { filename: generatedFilename, height, width, existingThumbnail } = buildMetadataFromVideo(video, type, size)
171
172 const thumbnail = existingThumbnail || new ThumbnailModel()
173
174 // Do not change the thumbnail filename if the file did not change
175 if (hasThumbnailUrlChanged(existingThumbnail, fileUrl, video)) {
176 thumbnail.filename = generatedFilename
177 }
178
179 thumbnail.height = height
180 thumbnail.width = width
181 thumbnail.type = type
182 thumbnail.fileUrl = fileUrl
183 thumbnail.onDisk = onDisk
184
185 return thumbnail
186}
187
188// ---------------------------------------------------------------------------
189
190async function regenerateMiniaturesIfNeeded (video: MVideoWithAllFiles) {
191 if (video.getMiniature().automaticallyGenerated === true) {
192 const miniature = await generateLocalVideoMiniature({
193 video,
194 videoFile: video.getMaxQualityFile(),
195 type: ThumbnailType.MINIATURE
196 })
197 await video.addAndSaveThumbnail(miniature)
198 }
199
200 if (video.getPreview().automaticallyGenerated === true) {
201 const preview = await generateLocalVideoMiniature({
202 video,
203 videoFile: video.getMaxQualityFile(),
204 type: ThumbnailType.PREVIEW
205 })
206 await video.addAndSaveThumbnail(preview)
207 }
208}
209
210// ---------------------------------------------------------------------------
211
212export {
213 generateLocalVideoMiniature,
214 regenerateMiniaturesIfNeeded,
215 updateLocalVideoMiniatureFromUrl,
216 updateLocalVideoMiniatureFromExisting,
217 updateRemoteVideoThumbnail,
218 updateRemotePlaylistMiniatureFromUrl,
219 updateLocalPlaylistMiniatureFromExisting
220}
221
222// ---------------------------------------------------------------------------
223// Private
224// ---------------------------------------------------------------------------
225
226function hasThumbnailUrlChanged (existingThumbnail: MThumbnail, downloadUrl: string, video: MVideoUUID) {
227 const existingUrl = existingThumbnail
228 ? existingThumbnail.fileUrl
229 : null
230
231 // If the thumbnail URL did not change and has a unique filename (introduced in 3.1), avoid thumbnail processing
232 return !existingUrl || existingUrl !== downloadUrl || downloadUrl.endsWith(`${video.uuid}.jpg`)
233}
234
235function buildMetadataFromPlaylist (playlist: MVideoPlaylistThumbnail, size: ImageSize) {
236 const filename = playlist.generateThumbnailName()
237 const basePath = CONFIG.STORAGE.THUMBNAILS_DIR
238
239 return {
240 filename,
241 basePath,
242 existingThumbnail: playlist.Thumbnail,
243 outputPath: join(basePath, filename),
244 height: size ? size.height : THUMBNAILS_SIZE.height,
245 width: size ? size.width : THUMBNAILS_SIZE.width
246 }
247}
248
249function buildMetadataFromVideo (video: MVideoThumbnail, type: ThumbnailType, size?: ImageSize) {
250 const existingThumbnail = Array.isArray(video.Thumbnails)
251 ? video.Thumbnails.find(t => t.type === type)
252 : undefined
253
254 if (type === ThumbnailType.MINIATURE) {
255 const filename = generateImageFilename()
256 const basePath = CONFIG.STORAGE.THUMBNAILS_DIR
257
258 return {
259 filename,
260 basePath,
261 existingThumbnail,
262 outputPath: join(basePath, filename),
263 height: size ? size.height : THUMBNAILS_SIZE.height,
264 width: size ? size.width : THUMBNAILS_SIZE.width
265 }
266 }
267
268 if (type === ThumbnailType.PREVIEW) {
269 const filename = generateImageFilename()
270 const basePath = CONFIG.STORAGE.PREVIEWS_DIR
271
272 return {
273 filename,
274 basePath,
275 existingThumbnail,
276 outputPath: join(basePath, filename),
277 height: size ? size.height : PREVIEWS_SIZE.height,
278 width: size ? size.width : PREVIEWS_SIZE.width
279 }
280 }
281
282 return undefined
283}
284
285async function updateThumbnailFromFunction (parameters: {
286 thumbnailCreator: () => Promise<any>
287 filename: string
288 height: number
289 width: number
290 type: ThumbnailType
291 onDisk: boolean
292 automaticallyGenerated?: boolean
293 fileUrl?: string
294 existingThumbnail?: MThumbnail
295}) {
296 const {
297 thumbnailCreator,
298 filename,
299 width,
300 height,
301 type,
302 existingThumbnail,
303 onDisk,
304 automaticallyGenerated = null,
305 fileUrl = null
306 } = parameters
307
308 const oldFilename = existingThumbnail && existingThumbnail.filename !== filename
309 ? existingThumbnail.filename
310 : undefined
311
312 const thumbnail: MThumbnail = existingThumbnail || new ThumbnailModel()
313
314 thumbnail.filename = filename
315 thumbnail.height = height
316 thumbnail.width = width
317 thumbnail.type = type
318 thumbnail.fileUrl = fileUrl
319 thumbnail.automaticallyGenerated = automaticallyGenerated
320 thumbnail.onDisk = onDisk
321
322 if (oldFilename) thumbnail.previousThumbnailFilename = oldFilename
323
324 await thumbnailCreator()
325
326 return thumbnail
327}