aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/controllers/api/videos
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2019-04-17 10:07:00 +0200
committerChocobozzz <me@florianbigard.com>2019-04-24 16:25:52 +0200
commite8bafea35bc930cb8ac5b2d521a188642a1adffe (patch)
tree7537f957ed7307b464e3c90b71b813d992acaade /server/controllers/api/videos
parent94565d52bb2883e09f16d1363170ac9c0dccb7a1 (diff)
downloadPeerTube-e8bafea35bc930cb8ac5b2d521a188642a1adffe.tar.gz
PeerTube-e8bafea35bc930cb8ac5b2d521a188642a1adffe.tar.zst
PeerTube-e8bafea35bc930cb8ac5b2d521a188642a1adffe.zip
Create a dedicated table to track video thumbnails
Diffstat (limited to 'server/controllers/api/videos')
-rw-r--r--server/controllers/api/videos/import.ts70
-rw-r--r--server/controllers/api/videos/index.ts62
-rw-r--r--server/controllers/api/videos/ownership.ts1
3 files changed, 79 insertions, 54 deletions
diff --git a/server/controllers/api/videos/import.ts b/server/controllers/api/videos/import.ts
index a72b8c72e..f9a24a0c2 100644
--- a/server/controllers/api/videos/import.ts
+++ b/server/controllers/api/videos/import.ts
@@ -3,7 +3,7 @@ import * as magnetUtil from 'magnet-uri'
3import 'multer' 3import 'multer'
4import { auditLoggerFactory, getAuditIdFromRes, VideoImportAuditView } from '../../../helpers/audit-logger' 4import { auditLoggerFactory, getAuditIdFromRes, VideoImportAuditView } from '../../../helpers/audit-logger'
5import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate, videoImportAddValidator } from '../../../middlewares' 5import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate, videoImportAddValidator } from '../../../middlewares'
6import { MIMETYPES, PREVIEWS_SIZE, THUMBNAILS_SIZE } from '../../../initializers/constants' 6import { MIMETYPES } from '../../../initializers/constants'
7import { getYoutubeDLInfo, YoutubeDLInfo } from '../../../helpers/youtube-dl' 7import { getYoutubeDLInfo, YoutubeDLInfo } from '../../../helpers/youtube-dl'
8import { createReqFiles } from '../../../helpers/express-utils' 8import { createReqFiles } from '../../../helpers/express-utils'
9import { logger } from '../../../helpers/logger' 9import { logger } from '../../../helpers/logger'
@@ -13,12 +13,10 @@ import { getVideoActivityPubUrl } from '../../../lib/activitypub'
13import { TagModel } from '../../../models/video/tag' 13import { TagModel } from '../../../models/video/tag'
14import { VideoImportModel } from '../../../models/video/video-import' 14import { VideoImportModel } from '../../../models/video/video-import'
15import { JobQueue } from '../../../lib/job-queue/job-queue' 15import { JobQueue } from '../../../lib/job-queue/job-queue'
16import { processImage } from '../../../helpers/image-utils'
17import { join } from 'path' 16import { join } from 'path'
18import { isArray } from '../../../helpers/custom-validators/misc' 17import { isArray } from '../../../helpers/custom-validators/misc'
19import { FilteredModelAttributes } from 'sequelize-typescript/lib/models/Model' 18import { FilteredModelAttributes } from 'sequelize-typescript/lib/models/Model'
20import { VideoChannelModel } from '../../../models/video/video-channel' 19import { VideoChannelModel } from '../../../models/video/video-channel'
21import { UserModel } from '../../../models/account/user'
22import * as Bluebird from 'bluebird' 20import * as Bluebird from 'bluebird'
23import * as parseTorrent from 'parse-torrent' 21import * as parseTorrent from 'parse-torrent'
24import { getSecureTorrentName } from '../../../helpers/utils' 22import { getSecureTorrentName } from '../../../helpers/utils'
@@ -26,6 +24,9 @@ import { move, readFile } from 'fs-extra'
26import { autoBlacklistVideoIfNeeded } from '../../../lib/video-blacklist' 24import { autoBlacklistVideoIfNeeded } from '../../../lib/video-blacklist'
27import { CONFIG } from '../../../initializers/config' 25import { CONFIG } from '../../../initializers/config'
28import { sequelizeTypescript } from '../../../initializers/database' 26import { sequelizeTypescript } from '../../../initializers/database'
27import { createVideoThumbnailFromExisting } from '../../../lib/thumbnail'
28import { ThumbnailType } from '../../../../shared/models/videos/thumbnail.type'
29import { ThumbnailModel } from '../../../models/video/thumbnail'
29 30
30const auditLogger = auditLoggerFactory('video-imports') 31const auditLogger = auditLoggerFactory('video-imports')
31const videoImportsRouter = express.Router() 32const videoImportsRouter = express.Router()
@@ -89,10 +90,10 @@ async function addTorrentImport (req: express.Request, res: express.Response, to
89 videoName = isArray(parsed.name) ? parsed.name[ 0 ] : parsed.name as string 90 videoName = isArray(parsed.name) ? parsed.name[ 0 ] : parsed.name as string
90 } 91 }
91 92
92 const video = buildVideo(res.locals.videoChannel.id, body, { name: videoName }, user) 93 const video = buildVideo(res.locals.videoChannel.id, body, { name: videoName })
93 94
94 await processThumbnail(req, video) 95 const thumbnailModel = await processThumbnail(req, video)
95 await processPreview(req, video) 96 const previewModel = await processPreview(req, video)
96 97
97 const tags = body.tags || undefined 98 const tags = body.tags || undefined
98 const videoImportAttributes = { 99 const videoImportAttributes = {
@@ -101,7 +102,14 @@ async function addTorrentImport (req: express.Request, res: express.Response, to
101 state: VideoImportState.PENDING, 102 state: VideoImportState.PENDING,
102 userId: user.id 103 userId: user.id
103 } 104 }
104 const videoImport = await insertIntoDB(video, res.locals.videoChannel, tags, videoImportAttributes) 105 const videoImport = await insertIntoDB({
106 video,
107 thumbnailModel,
108 previewModel,
109 videoChannel: res.locals.videoChannel,
110 tags,
111 videoImportAttributes
112 })
105 113
106 // Create job to import the video 114 // Create job to import the video
107 const payload = { 115 const payload = {
@@ -132,10 +140,10 @@ async function addYoutubeDLImport (req: express.Request, res: express.Response)
132 }).end() 140 }).end()
133 } 141 }
134 142
135 const video = buildVideo(res.locals.videoChannel.id, body, youtubeDLInfo, user) 143 const video = buildVideo(res.locals.videoChannel.id, body, youtubeDLInfo)
136 144
137 const downloadThumbnail = !await processThumbnail(req, video) 145 const thumbnailModel = await processThumbnail(req, video)
138 const downloadPreview = !await processPreview(req, video) 146 const previewModel = await processPreview(req, video)
139 147
140 const tags = body.tags || youtubeDLInfo.tags 148 const tags = body.tags || youtubeDLInfo.tags
141 const videoImportAttributes = { 149 const videoImportAttributes = {
@@ -143,15 +151,22 @@ async function addYoutubeDLImport (req: express.Request, res: express.Response)
143 state: VideoImportState.PENDING, 151 state: VideoImportState.PENDING,
144 userId: user.id 152 userId: user.id
145 } 153 }
146 const videoImport = await insertIntoDB(video, res.locals.videoChannel, tags, videoImportAttributes) 154 const videoImport = await insertIntoDB({
155 video: video,
156 thumbnailModel,
157 previewModel,
158 videoChannel: res.locals.videoChannel,
159 tags,
160 videoImportAttributes
161 })
147 162
148 // Create job to import the video 163 // Create job to import the video
149 const payload = { 164 const payload = {
150 type: 'youtube-dl' as 'youtube-dl', 165 type: 'youtube-dl' as 'youtube-dl',
151 videoImportId: videoImport.id, 166 videoImportId: videoImport.id,
152 thumbnailUrl: youtubeDLInfo.thumbnailUrl, 167 thumbnailUrl: youtubeDLInfo.thumbnailUrl,
153 downloadThumbnail, 168 downloadThumbnail: !thumbnailModel,
154 downloadPreview 169 downloadPreview: !previewModel
155 } 170 }
156 await JobQueue.Instance.createJob({ type: 'video-import', payload }) 171 await JobQueue.Instance.createJob({ type: 'video-import', payload })
157 172
@@ -160,7 +175,7 @@ async function addYoutubeDLImport (req: express.Request, res: express.Response)
160 return res.json(videoImport.toFormattedJSON()).end() 175 return res.json(videoImport.toFormattedJSON()).end()
161} 176}
162 177
163function buildVideo (channelId: number, body: VideoImportCreate, importData: YoutubeDLInfo, user: UserModel) { 178function buildVideo (channelId: number, body: VideoImportCreate, importData: YoutubeDLInfo) {
164 const videoData = { 179 const videoData = {
165 name: body.name || importData.name || 'Unknown name', 180 name: body.name || importData.name || 'Unknown name',
166 remote: false, 181 remote: false,
@@ -189,32 +204,34 @@ async function processThumbnail (req: express.Request, video: VideoModel) {
189 const thumbnailField = req.files ? req.files['thumbnailfile'] : undefined 204 const thumbnailField = req.files ? req.files['thumbnailfile'] : undefined
190 if (thumbnailField) { 205 if (thumbnailField) {
191 const thumbnailPhysicalFile = thumbnailField[ 0 ] 206 const thumbnailPhysicalFile = thumbnailField[ 0 ]
192 await processImage(thumbnailPhysicalFile, join(CONFIG.STORAGE.THUMBNAILS_DIR, video.getThumbnailName()), THUMBNAILS_SIZE)
193 207
194 return true 208 return createVideoThumbnailFromExisting(thumbnailPhysicalFile.path, video, ThumbnailType.THUMBNAIL)
195 } 209 }
196 210
197 return false 211 return undefined
198} 212}
199 213
200async function processPreview (req: express.Request, video: VideoModel) { 214async function processPreview (req: express.Request, video: VideoModel) {
201 const previewField = req.files ? req.files['previewfile'] : undefined 215 const previewField = req.files ? req.files['previewfile'] : undefined
202 if (previewField) { 216 if (previewField) {
203 const previewPhysicalFile = previewField[0] 217 const previewPhysicalFile = previewField[0]
204 await processImage(previewPhysicalFile, join(CONFIG.STORAGE.PREVIEWS_DIR, video.getPreviewName()), PREVIEWS_SIZE)
205 218
206 return true 219 return createVideoThumbnailFromExisting(previewPhysicalFile.path, video, ThumbnailType.PREVIEW)
207 } 220 }
208 221
209 return false 222 return undefined
210} 223}
211 224
212function insertIntoDB ( 225function insertIntoDB (parameters: {
213 video: VideoModel, 226 video: VideoModel,
227 thumbnailModel: ThumbnailModel,
228 previewModel: ThumbnailModel,
214 videoChannel: VideoChannelModel, 229 videoChannel: VideoChannelModel,
215 tags: string[], 230 tags: string[],
216 videoImportAttributes: FilteredModelAttributes<VideoImportModel> 231 videoImportAttributes: FilteredModelAttributes<VideoImportModel>
217): Bluebird<VideoImportModel> { 232}): Bluebird<VideoImportModel> {
233 let { video, thumbnailModel, previewModel, videoChannel, tags, videoImportAttributes } = parameters
234
218 return sequelizeTypescript.transaction(async t => { 235 return sequelizeTypescript.transaction(async t => {
219 const sequelizeOptions = { transaction: t } 236 const sequelizeOptions = { transaction: t }
220 237
@@ -222,6 +239,15 @@ function insertIntoDB (
222 const videoCreated = await video.save(sequelizeOptions) 239 const videoCreated = await video.save(sequelizeOptions)
223 videoCreated.VideoChannel = videoChannel 240 videoCreated.VideoChannel = videoChannel
224 241
242 if (thumbnailModel) {
243 thumbnailModel.videoId = videoCreated.id
244 videoCreated.addThumbnail(await thumbnailModel.save({ transaction: t }))
245 }
246 if (previewModel) {
247 previewModel.videoId = videoCreated.id
248 videoCreated.addThumbnail(await previewModel.save({ transaction: t }))
249 }
250
225 await autoBlacklistVideoIfNeeded(video, videoChannel.Account.User, t) 251 await autoBlacklistVideoIfNeeded(video, videoChannel.Account.User, t)
226 252
227 // Set tags to the video 253 // Set tags to the video
diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts
index d6f513254..24721a17f 100644
--- a/server/controllers/api/videos/index.ts
+++ b/server/controllers/api/videos/index.ts
@@ -2,20 +2,11 @@ import * as express from 'express'
2import { extname, join } from 'path' 2import { extname, join } from 'path'
3import { VideoCreate, VideoPrivacy, VideoState, VideoUpdate } from '../../../../shared' 3import { VideoCreate, VideoPrivacy, VideoState, VideoUpdate } from '../../../../shared'
4import { getVideoFileFPS, getVideoFileResolution } from '../../../helpers/ffmpeg-utils' 4import { getVideoFileFPS, getVideoFileResolution } from '../../../helpers/ffmpeg-utils'
5import { processImage } from '../../../helpers/image-utils'
6import { logger } from '../../../helpers/logger' 5import { logger } from '../../../helpers/logger'
7import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger' 6import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger'
8import { getFormattedObjects, getServerActor } from '../../../helpers/utils' 7import { getFormattedObjects, getServerActor } from '../../../helpers/utils'
9import { autoBlacklistVideoIfNeeded } from '../../../lib/video-blacklist' 8import { autoBlacklistVideoIfNeeded } from '../../../lib/video-blacklist'
10import { 9import { MIMETYPES, VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_PRIVACIES } from '../../../initializers/constants'
11 MIMETYPES,
12 PREVIEWS_SIZE,
13 THUMBNAILS_SIZE,
14 VIDEO_CATEGORIES,
15 VIDEO_LANGUAGES,
16 VIDEO_LICENCES,
17 VIDEO_PRIVACIES
18} from '../../../initializers/constants'
19import { 10import {
20 changeVideoChannelShare, 11 changeVideoChannelShare,
21 federateVideoIfNeeded, 12 federateVideoIfNeeded,
@@ -61,6 +52,8 @@ import { Notifier } from '../../../lib/notifier'
61import { sendView } from '../../../lib/activitypub/send/send-view' 52import { sendView } from '../../../lib/activitypub/send/send-view'
62import { CONFIG } from '../../../initializers/config' 53import { CONFIG } from '../../../initializers/config'
63import { sequelizeTypescript } from '../../../initializers/database' 54import { sequelizeTypescript } from '../../../initializers/database'
55import { createVideoThumbnailFromExisting, generateVideoThumbnail } from '../../../lib/thumbnail'
56import { ThumbnailType } from '../../../../shared/models/videos/thumbnail.type'
64 57
65const auditLogger = auditLoggerFactory('videos') 58const auditLogger = auditLoggerFactory('videos')
66const videosRouter = express.Router() 59const videosRouter = express.Router()
@@ -220,21 +213,15 @@ async function addVideo (req: express.Request, res: express.Response) {
220 213
221 // Process thumbnail or create it from the video 214 // Process thumbnail or create it from the video
222 const thumbnailField = req.files['thumbnailfile'] 215 const thumbnailField = req.files['thumbnailfile']
223 if (thumbnailField) { 216 const thumbnailModel = thumbnailField
224 const thumbnailPhysicalFile = thumbnailField[0] 217 ? await createVideoThumbnailFromExisting(thumbnailField[0].path, video, ThumbnailType.THUMBNAIL)
225 await processImage(thumbnailPhysicalFile, join(CONFIG.STORAGE.THUMBNAILS_DIR, video.getThumbnailName()), THUMBNAILS_SIZE) 218 : await generateVideoThumbnail(video, videoFile, ThumbnailType.THUMBNAIL)
226 } else {
227 await video.createThumbnail(videoFile)
228 }
229 219
230 // Process preview or create it from the video 220 // Process preview or create it from the video
231 const previewField = req.files['previewfile'] 221 const previewField = req.files['previewfile']
232 if (previewField) { 222 const previewModel = previewField
233 const previewPhysicalFile = previewField[0] 223 ? await createVideoThumbnailFromExisting(previewField[0].path, video, ThumbnailType.PREVIEW)
234 await processImage(previewPhysicalFile, join(CONFIG.STORAGE.PREVIEWS_DIR, video.getPreviewName()), PREVIEWS_SIZE) 224 : await generateVideoThumbnail(video, videoFile, ThumbnailType.PREVIEW)
235 } else {
236 await video.createPreview(videoFile)
237 }
238 225
239 // Create the torrent file 226 // Create the torrent file
240 await video.createTorrentAndSetInfoHash(videoFile) 227 await video.createTorrentAndSetInfoHash(videoFile)
@@ -243,6 +230,13 @@ async function addVideo (req: express.Request, res: express.Response) {
243 const sequelizeOptions = { transaction: t } 230 const sequelizeOptions = { transaction: t }
244 231
245 const videoCreated = await video.save(sequelizeOptions) 232 const videoCreated = await video.save(sequelizeOptions)
233
234 thumbnailModel.videoId = videoCreated.id
235 previewModel.videoId = videoCreated.id
236
237 videoCreated.addThumbnail(await thumbnailModel.save({ transaction: t }))
238 videoCreated.addThumbnail(await previewModel.save({ transaction: t }))
239
246 // Do not forget to add video channel information to the created video 240 // Do not forget to add video channel information to the created video
247 videoCreated.VideoChannel = res.locals.videoChannel 241 videoCreated.VideoChannel = res.locals.videoChannel
248 242
@@ -313,16 +307,13 @@ async function updateVideo (req: express.Request, res: express.Response) {
313 const wasUnlistedVideo = videoInstance.privacy === VideoPrivacy.UNLISTED 307 const wasUnlistedVideo = videoInstance.privacy === VideoPrivacy.UNLISTED
314 308
315 // Process thumbnail or create it from the video 309 // Process thumbnail or create it from the video
316 if (req.files && req.files['thumbnailfile']) { 310 const thumbnailModel = req.files && req.files['thumbnailfile']
317 const thumbnailPhysicalFile = req.files['thumbnailfile'][0] 311 ? await createVideoThumbnailFromExisting(req.files['thumbnailfile'][0].path, videoInstance, ThumbnailType.THUMBNAIL)
318 await processImage(thumbnailPhysicalFile, join(CONFIG.STORAGE.THUMBNAILS_DIR, videoInstance.getThumbnailName()), THUMBNAILS_SIZE) 312 : undefined
319 }
320 313
321 // Process preview or create it from the video 314 const previewModel = req.files && req.files['previewfile']
322 if (req.files && req.files['previewfile']) { 315 ? await createVideoThumbnailFromExisting(req.files['previewfile'][0].path, videoInstance, ThumbnailType.PREVIEW)
323 const previewPhysicalFile = req.files['previewfile'][0] 316 : undefined
324 await processImage(previewPhysicalFile, join(CONFIG.STORAGE.PREVIEWS_DIR, videoInstance.getPreviewName()), PREVIEWS_SIZE)
325 }
326 317
327 try { 318 try {
328 const videoInstanceUpdated = await sequelizeTypescript.transaction(async t => { 319 const videoInstanceUpdated = await sequelizeTypescript.transaction(async t => {
@@ -355,6 +346,15 @@ async function updateVideo (req: express.Request, res: express.Response) {
355 346
356 const videoInstanceUpdated = await videoInstance.save(sequelizeOptions) 347 const videoInstanceUpdated = await videoInstance.save(sequelizeOptions)
357 348
349 if (thumbnailModel) {
350 thumbnailModel.videoId = videoInstanceUpdated.id
351 videoInstanceUpdated.addThumbnail(await thumbnailModel.save({ transaction: t }))
352 }
353 if (previewModel) {
354 previewModel.videoId = videoInstanceUpdated.id
355 videoInstanceUpdated.addThumbnail(await previewModel.save({ transaction: t }))
356 }
357
358 // Video tags update? 358 // Video tags update?
359 if (videoInfoToUpdate.tags !== undefined) { 359 if (videoInfoToUpdate.tags !== undefined) {
360 const tagInstances = await TagModel.findOrCreateTags(videoInfoToUpdate.tags, t) 360 const tagInstances = await TagModel.findOrCreateTags(videoInfoToUpdate.tags, t)
diff --git a/server/controllers/api/videos/ownership.ts b/server/controllers/api/videos/ownership.ts
index fc73856c9..bc247c4ee 100644
--- a/server/controllers/api/videos/ownership.ts
+++ b/server/controllers/api/videos/ownership.ts
@@ -17,7 +17,6 @@ import { VideoChannelModel } from '../../../models/video/video-channel'
17import { getFormattedObjects } from '../../../helpers/utils' 17import { getFormattedObjects } from '../../../helpers/utils'
18import { changeVideoChannelShare } from '../../../lib/activitypub' 18import { changeVideoChannelShare } from '../../../lib/activitypub'
19import { sendUpdateVideo } from '../../../lib/activitypub/send' 19import { sendUpdateVideo } from '../../../lib/activitypub/send'
20import { UserModel } from '../../../models/account/user'
21 20
22const ownershipVideoRouter = express.Router() 21const ownershipVideoRouter = express.Router()
23 22