diff options
author | Chocobozzz <me@florianbigard.com> | 2019-04-17 10:07:00 +0200 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2019-04-24 16:25:52 +0200 |
commit | e8bafea35bc930cb8ac5b2d521a188642a1adffe (patch) | |
tree | 7537f957ed7307b464e3c90b71b813d992acaade /server/lib/thumbnail.ts | |
parent | 94565d52bb2883e09f16d1363170ac9c0dccb7a1 (diff) | |
download | PeerTube-e8bafea35bc930cb8ac5b2d521a188642a1adffe.tar.gz PeerTube-e8bafea35bc930cb8ac5b2d521a188642a1adffe.tar.zst PeerTube-e8bafea35bc930cb8ac5b2d521a188642a1adffe.zip |
Create a dedicated table to track video thumbnails
Diffstat (limited to 'server/lib/thumbnail.ts')
-rw-r--r-- | server/lib/thumbnail.ts | 151 |
1 files changed, 151 insertions, 0 deletions
diff --git a/server/lib/thumbnail.ts b/server/lib/thumbnail.ts new file mode 100644 index 000000000..344c28566 --- /dev/null +++ b/server/lib/thumbnail.ts | |||
@@ -0,0 +1,151 @@ | |||
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 } 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 createPlaylistThumbnailFromExisting (inputPath: string, playlist: VideoPlaylistModel, keepOriginal = false, size?: ImageSize) { | ||
16 | const { filename, outputPath, height, width, existingThumbnail } = buildMetadataFromPlaylist(playlist, size) | ||
17 | const type = ThumbnailType.THUMBNAIL | ||
18 | |||
19 | const thumbnailCreator = () => processImage({ path: inputPath }, outputPath, { width, height }, keepOriginal) | ||
20 | return createThumbnailFromFunction({ thumbnailCreator, filename, height, width, type, existingThumbnail }) | ||
21 | } | ||
22 | |||
23 | function createPlaylistThumbnailFromUrl (url: string, playlist: VideoPlaylistModel, size?: ImageSize) { | ||
24 | const { filename, basePath, height, width, existingThumbnail } = buildMetadataFromPlaylist(playlist, size) | ||
25 | const type = ThumbnailType.THUMBNAIL | ||
26 | |||
27 | const thumbnailCreator = () => downloadImage(url, basePath, filename, { width, height }) | ||
28 | return createThumbnailFromFunction({ thumbnailCreator, filename, height, width, type, existingThumbnail, url }) | ||
29 | } | ||
30 | |||
31 | function createVideoThumbnailFromUrl (url: string, video: VideoModel, type: ThumbnailType, size?: ImageSize) { | ||
32 | const { filename, basePath, height, width, existingThumbnail } = buildMetadataFromVideo(video, type, size) | ||
33 | const thumbnailCreator = () => downloadImage(url, basePath, filename, { width, height }) | ||
34 | |||
35 | return createThumbnailFromFunction({ thumbnailCreator, filename, height, width, type, existingThumbnail, url }) | ||
36 | } | ||
37 | |||
38 | function createVideoThumbnailFromExisting (inputPath: string, video: VideoModel, type: ThumbnailType, size?: ImageSize) { | ||
39 | const { filename, outputPath, height, width, existingThumbnail } = buildMetadataFromVideo(video, type, size) | ||
40 | const thumbnailCreator = () => processImage({ path: inputPath }, outputPath, { width, height }) | ||
41 | |||
42 | return createThumbnailFromFunction({ thumbnailCreator, filename, height, width, type, existingThumbnail }) | ||
43 | } | ||
44 | |||
45 | function generateVideoThumbnail (video: VideoModel, videoFile: VideoFileModel, type: ThumbnailType) { | ||
46 | const input = video.getVideoFilePath(videoFile) | ||
47 | |||
48 | const { filename, basePath, height, width, existingThumbnail } = buildMetadataFromVideo(video, type) | ||
49 | const thumbnailCreator = () => generateImageFromVideoFile(input, basePath, filename, { height, width }) | ||
50 | |||
51 | return createThumbnailFromFunction({ thumbnailCreator, filename, height, width, type, existingThumbnail }) | ||
52 | } | ||
53 | |||
54 | function createPlaceholderThumbnail (url: string, video: VideoModel, type: ThumbnailType, size: ImageSize) { | ||
55 | const { filename, height, width, existingThumbnail } = buildMetadataFromVideo(video, type, size) | ||
56 | |||
57 | const thumbnail = existingThumbnail ? existingThumbnail : new ThumbnailModel() | ||
58 | |||
59 | thumbnail.filename = filename | ||
60 | thumbnail.height = height | ||
61 | thumbnail.width = width | ||
62 | thumbnail.type = type | ||
63 | thumbnail.url = url | ||
64 | |||
65 | return thumbnail | ||
66 | } | ||
67 | |||
68 | // --------------------------------------------------------------------------- | ||
69 | |||
70 | export { | ||
71 | generateVideoThumbnail, | ||
72 | createVideoThumbnailFromUrl, | ||
73 | createVideoThumbnailFromExisting, | ||
74 | createPlaceholderThumbnail, | ||
75 | createPlaylistThumbnailFromUrl, | ||
76 | createPlaylistThumbnailFromExisting | ||
77 | } | ||
78 | |||
79 | function buildMetadataFromPlaylist (playlist: VideoPlaylistModel, size: ImageSize) { | ||
80 | const filename = playlist.generateThumbnailName() | ||
81 | const basePath = CONFIG.STORAGE.THUMBNAILS_DIR | ||
82 | |||
83 | return { | ||
84 | filename, | ||
85 | basePath, | ||
86 | existingThumbnail: playlist.Thumbnail, | ||
87 | outputPath: join(basePath, filename), | ||
88 | height: size ? size.height : THUMBNAILS_SIZE.height, | ||
89 | width: size ? size.width : THUMBNAILS_SIZE.width | ||
90 | } | ||
91 | } | ||
92 | |||
93 | function buildMetadataFromVideo (video: VideoModel, type: ThumbnailType, size?: ImageSize) { | ||
94 | const existingThumbnail = Array.isArray(video.Thumbnails) | ||
95 | ? video.Thumbnails.find(t => t.type === type) | ||
96 | : undefined | ||
97 | |||
98 | if (type === ThumbnailType.THUMBNAIL) { | ||
99 | const filename = video.generateThumbnailName() | ||
100 | const basePath = CONFIG.STORAGE.THUMBNAILS_DIR | ||
101 | |||
102 | return { | ||
103 | filename, | ||
104 | basePath, | ||
105 | existingThumbnail, | ||
106 | outputPath: join(basePath, filename), | ||
107 | height: size ? size.height : THUMBNAILS_SIZE.height, | ||
108 | width: size ? size.width : THUMBNAILS_SIZE.width | ||
109 | } | ||
110 | } | ||
111 | |||
112 | if (type === ThumbnailType.PREVIEW) { | ||
113 | const filename = video.generatePreviewName() | ||
114 | const basePath = CONFIG.STORAGE.PREVIEWS_DIR | ||
115 | |||
116 | return { | ||
117 | filename, | ||
118 | basePath, | ||
119 | existingThumbnail, | ||
120 | outputPath: join(basePath, filename), | ||
121 | height: size ? size.height : PREVIEWS_SIZE.height, | ||
122 | width: size ? size.width : PREVIEWS_SIZE.width | ||
123 | } | ||
124 | } | ||
125 | |||
126 | return undefined | ||
127 | } | ||
128 | |||
129 | async function createThumbnailFromFunction (parameters: { | ||
130 | thumbnailCreator: () => Promise<any>, | ||
131 | filename: string, | ||
132 | height: number, | ||
133 | width: number, | ||
134 | type: ThumbnailType, | ||
135 | url?: string, | ||
136 | existingThumbnail?: ThumbnailModel | ||
137 | }) { | ||
138 | const { thumbnailCreator, filename, width, height, type, existingThumbnail, url = null } = parameters | ||
139 | |||
140 | const thumbnail = existingThumbnail ? existingThumbnail : new ThumbnailModel() | ||
141 | |||
142 | thumbnail.filename = filename | ||
143 | thumbnail.height = height | ||
144 | thumbnail.width = width | ||
145 | thumbnail.type = type | ||
146 | thumbnail.url = url | ||
147 | |||
148 | await thumbnailCreator() | ||
149 | |||
150 | return thumbnail | ||
151 | } | ||