aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/controllers/api/videos/upload.ts
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2022-03-16 15:34:21 +0100
committerChocobozzz <me@florianbigard.com>2022-03-16 15:45:58 +0100
commitf012319a644fe8d9d33f2f567fa828442a3b39fd (patch)
tree8fc7aeff10749ed8088e3f89745433b59bb62c20 /server/controllers/api/videos/upload.ts
parent83664918901564830f3b7d1bd9879411a1b857a8 (diff)
downloadPeerTube-f012319a644fe8d9d33f2f567fa828442a3b39fd.tar.gz
PeerTube-f012319a644fe8d9d33f2f567fa828442a3b39fd.tar.zst
PeerTube-f012319a644fe8d9d33f2f567fa828442a3b39fd.zip
Process video torrents in order
Prevent update before video torrent generation for example
Diffstat (limited to 'server/controllers/api/videos/upload.ts')
-rw-r--r--server/controllers/api/videos/upload.ts64
1 files changed, 28 insertions, 36 deletions
diff --git a/server/controllers/api/videos/upload.ts b/server/controllers/api/videos/upload.ts
index dd69cf238..14ae9d920 100644
--- a/server/controllers/api/videos/upload.ts
+++ b/server/controllers/api/videos/upload.ts
@@ -2,8 +2,8 @@ import express from 'express'
2import { move } from 'fs-extra' 2import { move } from 'fs-extra'
3import { basename } from 'path' 3import { basename } from 'path'
4import { getResumableUploadPath } from '@server/helpers/upload' 4import { getResumableUploadPath } from '@server/helpers/upload'
5import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent'
6import { getLocalVideoActivityPubUrl } from '@server/lib/activitypub/url' 5import { getLocalVideoActivityPubUrl } from '@server/lib/activitypub/url'
6import { JobQueue } from '@server/lib/job-queue'
7import { generateWebTorrentVideoFilename } from '@server/lib/paths' 7import { generateWebTorrentVideoFilename } from '@server/lib/paths'
8import { Redis } from '@server/lib/redis' 8import { Redis } from '@server/lib/redis'
9import { uploadx } from '@server/lib/uploadx' 9import { uploadx } from '@server/lib/uploadx'
@@ -17,10 +17,10 @@ import {
17import { VideoPathManager } from '@server/lib/video-path-manager' 17import { VideoPathManager } from '@server/lib/video-path-manager'
18import { buildNextVideoState } from '@server/lib/video-state' 18import { buildNextVideoState } from '@server/lib/video-state'
19import { openapiOperationDoc } from '@server/middlewares/doc' 19import { openapiOperationDoc } from '@server/middlewares/doc'
20import { MVideo, MVideoFile, MVideoFullLight } from '@server/types/models' 20import { MVideoFile, MVideoFullLight } from '@server/types/models'
21import { getLowercaseExtension } from '@shared/core-utils' 21import { getLowercaseExtension } from '@shared/core-utils'
22import { isAudioFile, uuidToShort } from '@shared/extra-utils' 22import { isAudioFile, uuidToShort } from '@shared/extra-utils'
23import { HttpStatusCode, VideoCreate, VideoResolution, VideoState } from '@shared/models' 23import { HttpStatusCode, ManageVideoTorrentPayload, VideoCreate, VideoResolution, VideoState } from '@shared/models'
24import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger' 24import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger'
25import { retryTransactionWrapper } from '../../../helpers/database-utils' 25import { retryTransactionWrapper } from '../../../helpers/database-utils'
26import { createReqFiles } from '../../../helpers/express-utils' 26import { createReqFiles } from '../../../helpers/express-utils'
@@ -209,17 +209,22 @@ async function addVideo (options: {
209 // Channel has a new content, set as updated 209 // Channel has a new content, set as updated
210 await videoCreated.VideoChannel.setAsUpdated() 210 await videoCreated.VideoChannel.setAsUpdated()
211 211
212 createTorrentFederate(video, videoFile) 212 createTorrentFederate(videoCreated, videoFile)
213 .then(() => { 213 .catch(err => {
214 if (video.state === VideoState.TO_MOVE_TO_EXTERNAL_STORAGE) { 214 logger.error('Cannot create torrent or federate video for %s.', videoCreated.uuid, { err, ...lTags(videoCreated.uuid) })
215 return addMoveToObjectStorageJob(video) 215
216 return videoCreated
217 }).then(refreshedVideo => {
218 if (!refreshedVideo) return
219
220 if (refreshedVideo.state === VideoState.TO_MOVE_TO_EXTERNAL_STORAGE) {
221 return addMoveToObjectStorageJob(refreshedVideo)
216 } 222 }
217 223
218 if (video.state === VideoState.TO_TRANSCODE) { 224 if (refreshedVideo.state === VideoState.TO_TRANSCODE) {
219 return addOptimizeOrMergeAudioJob(videoCreated, videoFile, user) 225 return addOptimizeOrMergeAudioJob(refreshedVideo, videoFile, user)
220 } 226 }
221 }) 227 }).catch(err => logger.error('Cannot add optimize/merge audio job for %s.', videoCreated.uuid, { err, ...lTags(videoCreated.uuid) }))
222 .catch(err => logger.error('Cannot add optimize/merge audio job for %s.', videoCreated.uuid, { err, ...lTags(videoCreated.uuid) }))
223 228
224 Hooks.runAction('action:api.video.uploaded', { video: videoCreated, req, res }) 229 Hooks.runAction('action:api.video.uploaded', { video: videoCreated, req, res })
225 230
@@ -254,36 +259,23 @@ async function buildNewFile (videoPhysicalFile: express.VideoUploadFile) {
254 return videoFile 259 return videoFile
255} 260}
256 261
257async function createTorrentAndSetInfoHashAsync (video: MVideo, fileArg: MVideoFile) { 262async function createTorrentFederate (video: MVideoFullLight, videoFile: MVideoFile) {
258 await createTorrentAndSetInfoHash(video, fileArg) 263 const payload: ManageVideoTorrentPayload = { videoId: video.id, videoFileId: videoFile.id, action: 'create' }
259 264
260 // Refresh videoFile because the createTorrentAndSetInfoHash could be long 265 const job = await JobQueue.Instance.createJobWithPromise({ type: 'manage-video-torrent', payload })
261 const refreshedFile = await VideoFileModel.loadWithVideo(fileArg.id) 266 await job.finished()
262 // File does not exist anymore, remove the generated torrent
263 if (!refreshedFile) return fileArg.removeTorrent()
264 267
265 refreshedFile.infoHash = fileArg.infoHash 268 const refreshedVideo = await VideoModel.loadAndPopulateAccountAndServerAndTags(video.id)
266 refreshedFile.torrentFilename = fileArg.torrentFilename 269 if (!refreshedVideo) return
267 270
268 return refreshedFile.save() 271 // Only federate and notify after the torrent creation
269} 272 Notifier.Instance.notifyOnNewVideoIfNeeded(refreshedVideo)
270 273
271function createTorrentFederate (video: MVideoFullLight, videoFile: MVideoFile) { 274 await retryTransactionWrapper(() => {
272 // Create the torrent file in async way because it could be long 275 return sequelizeTypescript.transaction(t => federateVideoIfNeeded(refreshedVideo, true, t))
273 return createTorrentAndSetInfoHashAsync(video, videoFile) 276 })
274 .catch(err => logger.error('Cannot create torrent file for video %s', video.url, { err, ...lTags(video.uuid) }))
275 .then(() => VideoModel.loadAndPopulateAccountAndServerAndTags(video.id))
276 .then(refreshedVideo => {
277 if (!refreshedVideo) return
278
279 // Only federate and notify after the torrent creation
280 Notifier.Instance.notifyOnNewVideoIfNeeded(refreshedVideo)
281 277
282 return retryTransactionWrapper(() => { 278 return refreshedVideo
283 return sequelizeTypescript.transaction(t => federateVideoIfNeeded(refreshedVideo, true, t))
284 })
285 })
286 .catch(err => logger.error('Cannot federate or notify video creation %s', video.url, { err, ...lTags(video.uuid) }))
287} 279}
288 280
289async function deleteUploadResumableCache (req: express.Request, res: express.Response, next: express.NextFunction) { 281async function deleteUploadResumableCache (req: express.Request, res: express.Response, next: express.NextFunction) {