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