84791955e0cc00c908b9882dae22377b93dd9188
[oweals/peertube.git] / server / lib / thumbnail.ts
1 import { generateImageFromVideoFile } from '../helpers/ffmpeg-utils'
2 import { CONFIG } from '../initializers/config'
3 import { ASSETS_PATH, PREVIEWS_SIZE, THUMBNAILS_SIZE } from '../initializers/constants'
4 import { ThumbnailModel } from '../models/video/thumbnail'
5 import { ThumbnailType } from '../../shared/models/videos/thumbnail.type'
6 import { processImage } from '../helpers/image-utils'
7 import { join } from 'path'
8 import { downloadImage } from '../helpers/requests'
9 import { MVideoPlaylistThumbnail } from '../typings/models/video/video-playlist'
10 import { MVideoFile, MVideoThumbnail } from '../typings/models'
11 import { MThumbnail } from '../typings/models/video/thumbnail'
12
13 type ImageSize = { height: number, width: number }
14
15 function createPlaylistMiniatureFromExisting (
16   inputPath: string,
17   playlist: MVideoPlaylistThumbnail,
18   automaticallyGenerated: boolean,
19   keepOriginal = false,
20   size?: ImageSize
21 ) {
22   const { filename, outputPath, height, width, existingThumbnail } = buildMetadataFromPlaylist(playlist, size)
23   const type = ThumbnailType.MINIATURE
24
25   const thumbnailCreator = () => processImage(inputPath, outputPath, { width, height }, keepOriginal)
26   return createThumbnailFromFunction({ thumbnailCreator, filename, height, width, type, automaticallyGenerated, existingThumbnail })
27 }
28
29 function createPlaylistMiniatureFromUrl (fileUrl: string, playlist: MVideoPlaylistThumbnail, size?: ImageSize) {
30   const { filename, basePath, height, width, existingThumbnail } = buildMetadataFromPlaylist(playlist, size)
31   const type = ThumbnailType.MINIATURE
32
33   const thumbnailCreator = () => downloadImage(fileUrl, basePath, filename, { width, height })
34   return createThumbnailFromFunction({ thumbnailCreator, filename, height, width, type, existingThumbnail, fileUrl })
35 }
36
37 function createVideoMiniatureFromUrl (fileUrl: string, video: MVideoThumbnail, type: ThumbnailType, size?: ImageSize) {
38   const { filename, basePath, height, width, existingThumbnail } = buildMetadataFromVideo(video, type, size)
39   const thumbnailCreator = () => downloadImage(fileUrl, basePath, filename, { width, height })
40
41   return createThumbnailFromFunction({ thumbnailCreator, filename, height, width, type, existingThumbnail, fileUrl })
42 }
43
44 function createVideoMiniatureFromExisting (
45   inputPath: string,
46   video: MVideoThumbnail,
47   type: ThumbnailType,
48   automaticallyGenerated: boolean,
49   size?: ImageSize
50 ) {
51   const { filename, outputPath, height, width, existingThumbnail } = buildMetadataFromVideo(video, type, size)
52   const thumbnailCreator = () => processImage(inputPath, outputPath, { width, height })
53
54   return createThumbnailFromFunction({ thumbnailCreator, filename, height, width, type, automaticallyGenerated, existingThumbnail })
55 }
56
57 function generateVideoMiniature (video: MVideoThumbnail, videoFile: MVideoFile, type: ThumbnailType) {
58   const input = video.getVideoFilePath(videoFile)
59
60   const { filename, basePath, height, width, existingThumbnail, outputPath } = buildMetadataFromVideo(video, type)
61   const thumbnailCreator = videoFile.isAudio()
62     ? () => processImage(ASSETS_PATH.DEFAULT_AUDIO_BACKGROUND, outputPath, { width, height }, true)
63     : () => generateImageFromVideoFile(input, basePath, filename, { height, width })
64
65   return createThumbnailFromFunction({ thumbnailCreator, filename, height, width, type, automaticallyGenerated: true, existingThumbnail })
66 }
67
68 function createPlaceholderThumbnail (fileUrl: string, video: MVideoThumbnail, type: ThumbnailType, size: ImageSize) {
69   const { filename, height, width, existingThumbnail } = buildMetadataFromVideo(video, type, size)
70
71   const thumbnail = existingThumbnail ? existingThumbnail : new ThumbnailModel()
72
73   thumbnail.filename = filename
74   thumbnail.height = height
75   thumbnail.width = width
76   thumbnail.type = type
77   thumbnail.fileUrl = fileUrl
78
79   return thumbnail
80 }
81
82 // ---------------------------------------------------------------------------
83
84 export {
85   generateVideoMiniature,
86   createVideoMiniatureFromUrl,
87   createVideoMiniatureFromExisting,
88   createPlaceholderThumbnail,
89   createPlaylistMiniatureFromUrl,
90   createPlaylistMiniatureFromExisting
91 }
92
93 function buildMetadataFromPlaylist (playlist: MVideoPlaylistThumbnail, size: ImageSize) {
94   const filename = playlist.generateThumbnailName()
95   const basePath = CONFIG.STORAGE.THUMBNAILS_DIR
96
97   return {
98     filename,
99     basePath,
100     existingThumbnail: playlist.Thumbnail,
101     outputPath: join(basePath, filename),
102     height: size ? size.height : THUMBNAILS_SIZE.height,
103     width: size ? size.width : THUMBNAILS_SIZE.width
104   }
105 }
106
107 function buildMetadataFromVideo (video: MVideoThumbnail, type: ThumbnailType, size?: ImageSize) {
108   const existingThumbnail = Array.isArray(video.Thumbnails)
109     ? video.Thumbnails.find(t => t.type === type)
110     : undefined
111
112   if (type === ThumbnailType.MINIATURE) {
113     const filename = video.generateThumbnailName()
114     const basePath = CONFIG.STORAGE.THUMBNAILS_DIR
115
116     return {
117       filename,
118       basePath,
119       existingThumbnail,
120       outputPath: join(basePath, filename),
121       height: size ? size.height : THUMBNAILS_SIZE.height,
122       width: size ? size.width : THUMBNAILS_SIZE.width
123     }
124   }
125
126   if (type === ThumbnailType.PREVIEW) {
127     const filename = video.generatePreviewName()
128     const basePath = CONFIG.STORAGE.PREVIEWS_DIR
129
130     return {
131       filename,
132       basePath,
133       existingThumbnail,
134       outputPath: join(basePath, filename),
135       height: size ? size.height : PREVIEWS_SIZE.height,
136       width: size ? size.width : PREVIEWS_SIZE.width
137     }
138   }
139
140   return undefined
141 }
142
143 async function createThumbnailFromFunction (parameters: {
144   thumbnailCreator: () => Promise<any>,
145   filename: string,
146   height: number,
147   width: number,
148   type: ThumbnailType,
149   automaticallyGenerated?: boolean,
150   fileUrl?: string,
151   existingThumbnail?: MThumbnail
152 }) {
153   const { thumbnailCreator, filename, width, height, type, existingThumbnail, automaticallyGenerated = null, fileUrl = null } = parameters
154
155   const thumbnail = existingThumbnail ? existingThumbnail : new ThumbnailModel()
156
157   thumbnail.filename = filename
158   thumbnail.height = height
159   thumbnail.width = width
160   thumbnail.type = type
161   thumbnail.fileUrl = fileUrl
162   thumbnail.automaticallyGenerated = automaticallyGenerated
163
164   await thumbnailCreator()
165
166   return thumbnail
167 }