aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2021-02-25 13:56:07 +0100
committerChocobozzz <chocobozzz@cpy.re>2021-02-25 15:01:07 +0100
commitd61893f7236abbed30c25b1823e6ecad93a8e8dd (patch)
tree9cf1d47598d9a99f390c6754d289d2573788451d
parentd7df188f23bb3c4773ac26e6fa8b3d82b1229e6d (diff)
downloadPeerTube-d61893f7236abbed30c25b1823e6ecad93a8e8dd.tar.gz
PeerTube-d61893f7236abbed30c25b1823e6ecad93a8e8dd.tar.zst
PeerTube-d61893f7236abbed30c25b1823e6ecad93a8e8dd.zip
Async torrent creation
-rw-r--r--client/src/assets/player/webtorrent/webtorrent-plugin.ts2
-rw-r--r--server/controllers/api/videos/index.ts38
-rw-r--r--server/models/video/video-file.ts8
-rw-r--r--server/models/video/video-format-utils.ts2
-rw-r--r--shared/extra-utils/videos/videos.ts27
5 files changed, 66 insertions, 11 deletions
diff --git a/client/src/assets/player/webtorrent/webtorrent-plugin.ts b/client/src/assets/player/webtorrent/webtorrent-plugin.ts
index 4bb438e57..e557fe722 100644
--- a/client/src/assets/player/webtorrent/webtorrent-plugin.ts
+++ b/client/src/assets/player/webtorrent/webtorrent-plugin.ts
@@ -249,6 +249,8 @@ class WebTorrentPlugin extends Plugin {
249 options: PlayOptions, 249 options: PlayOptions,
250 done: Function 250 done: Function
251 ) { 251 ) {
252 if (!magnetOrTorrentUrl) return this.fallbackToHttp(options, done)
253
252 console.log('Adding ' + magnetOrTorrentUrl + '.') 254 console.log('Adding ' + magnetOrTorrentUrl + '.')
253 255
254 const oldTorrent = this.torrent 256 const oldTorrent = this.torrent
diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts
index e89315930..58ab72370 100644
--- a/server/controllers/api/videos/index.ts
+++ b/server/controllers/api/videos/index.ts
@@ -9,12 +9,12 @@ import { LiveManager } from '@server/lib/live-manager'
9import { addOptimizeOrMergeAudioJob, buildLocalVideoFromReq, buildVideoThumbnailsFromReq, setVideoTags } from '@server/lib/video' 9import { addOptimizeOrMergeAudioJob, buildLocalVideoFromReq, buildVideoThumbnailsFromReq, setVideoTags } from '@server/lib/video'
10import { generateVideoFilename, getVideoFilePath } from '@server/lib/video-paths' 10import { generateVideoFilename, getVideoFilePath } from '@server/lib/video-paths'
11import { getServerActor } from '@server/models/application/application' 11import { getServerActor } from '@server/models/application/application'
12import { MVideoFullLight } from '@server/types/models' 12import { MVideo, MVideoFile, MVideoFullLight } from '@server/types/models'
13import { VideoCreate, VideoState, VideoUpdate } from '../../../../shared' 13import { VideoCreate, VideoState, VideoUpdate } from '../../../../shared'
14import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' 14import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
15import { VideoFilter } from '../../../../shared/models/videos/video-query.type' 15import { VideoFilter } from '../../../../shared/models/videos/video-query.type'
16import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger' 16import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger'
17import { resetSequelizeInstance } from '../../../helpers/database-utils' 17import { resetSequelizeInstance, retryTransactionWrapper } from '../../../helpers/database-utils'
18import { buildNSFWFilter, createReqFiles, getCountVideos } from '../../../helpers/express-utils' 18import { buildNSFWFilter, createReqFiles, getCountVideos } from '../../../helpers/express-utils'
19import { getMetadataFromFile, getVideoFileFPS, getVideoFileResolution } from '../../../helpers/ffprobe-utils' 19import { getMetadataFromFile, getVideoFileFPS, getVideoFileResolution } from '../../../helpers/ffprobe-utils'
20import { logger } from '../../../helpers/logger' 20import { logger } from '../../../helpers/logger'
@@ -221,9 +221,6 @@ async function addVideo (req: express.Request, res: express.Response) {
221 fallback: type => generateVideoMiniature({ video, videoFile, type }) 221 fallback: type => generateVideoMiniature({ video, videoFile, type })
222 }) 222 })
223 223
224 // Create the torrent file
225 await createTorrentAndSetInfoHash(video, videoFile)
226
227 const { videoCreated } = await sequelizeTypescript.transaction(async t => { 224 const { videoCreated } = await sequelizeTypescript.transaction(async t => {
228 const sequelizeOptions = { transaction: t } 225 const sequelizeOptions = { transaction: t }
229 226
@@ -258,7 +255,6 @@ async function addVideo (req: express.Request, res: express.Response) {
258 isNew: true, 255 isNew: true,
259 transaction: t 256 transaction: t
260 }) 257 })
261 await federateVideoIfNeeded(video, true, t)
262 258
263 auditLogger.create(getAuditIdFromRes(res), new VideoAuditView(videoCreated.toFormattedDetailsJSON())) 259 auditLogger.create(getAuditIdFromRes(res), new VideoAuditView(videoCreated.toFormattedDetailsJSON()))
264 logger.info('Video with name %s and uuid %s created.', videoInfo.name, videoCreated.uuid) 260 logger.info('Video with name %s and uuid %s created.', videoInfo.name, videoCreated.uuid)
@@ -266,7 +262,21 @@ async function addVideo (req: express.Request, res: express.Response) {
266 return { videoCreated } 262 return { videoCreated }
267 }) 263 })
268 264
269 Notifier.Instance.notifyOnNewVideoIfNeeded(videoCreated) 265 // Create the torrent file in async way because it could be long
266 createTorrentAndSetInfoHashAsync(video, videoFile)
267 .catch(err => logger.error('Cannot create torrent file for video %s', video.url, { err }))
268 .then(() => VideoModel.loadAndPopulateAccountAndServerAndTags(video.id))
269 .then(refreshedVideo => {
270 if (!refreshedVideo) return
271
272 // Only federate and notify after the torrent creation
273 Notifier.Instance.notifyOnNewVideoIfNeeded(refreshedVideo)
274
275 return retryTransactionWrapper(() => {
276 return sequelizeTypescript.transaction(t => federateVideoIfNeeded(refreshedVideo, true, t))
277 })
278 })
279 .catch(err => logger.error('Cannot federate or notify video creation %s', video.url, { err }))
270 280
271 if (video.state === VideoState.TO_TRANSCODE) { 281 if (video.state === VideoState.TO_TRANSCODE) {
272 await addOptimizeOrMergeAudioJob(videoCreated, videoFile, res.locals.oauth.token.User) 282 await addOptimizeOrMergeAudioJob(videoCreated, videoFile, res.locals.oauth.token.User)
@@ -526,3 +536,17 @@ async function removeVideo (req: express.Request, res: express.Response) {
526 .status(HttpStatusCode.NO_CONTENT_204) 536 .status(HttpStatusCode.NO_CONTENT_204)
527 .end() 537 .end()
528} 538}
539
540async function createTorrentAndSetInfoHashAsync (video: MVideo, fileArg: MVideoFile) {
541 await createTorrentAndSetInfoHash(video, fileArg)
542
543 // Refresh videoFile because the createTorrentAndSetInfoHash could be long
544 const refreshedFile = await VideoFileModel.loadWithVideo(fileArg.id)
545 // File does not exist anymore, remove the generated torrent
546 if (!refreshedFile) return fileArg.removeTorrent()
547
548 refreshedFile.infoHash = fileArg.infoHash
549 refreshedFile.torrentFilename = fileArg.torrentFilename
550
551 return refreshedFile.save()
552}
diff --git a/server/models/video/video-file.ts b/server/models/video/video-file.ts
index 4df2c20bc..1ad796104 100644
--- a/server/models/video/video-file.ts
+++ b/server/models/video/video-file.ts
@@ -457,18 +457,26 @@ export class VideoFileModel extends Model {
457 457
458 // We proxify torrent requests so use a local URL 458 // We proxify torrent requests so use a local URL
459 getTorrentUrl () { 459 getTorrentUrl () {
460 if (!this.torrentFilename) return null
461
460 return WEBSERVER.URL + this.getTorrentStaticPath() 462 return WEBSERVER.URL + this.getTorrentStaticPath()
461 } 463 }
462 464
463 getTorrentStaticPath () { 465 getTorrentStaticPath () {
466 if (!this.torrentFilename) return null
467
464 return join(LAZY_STATIC_PATHS.TORRENTS, this.torrentFilename) 468 return join(LAZY_STATIC_PATHS.TORRENTS, this.torrentFilename)
465 } 469 }
466 470
467 getTorrentDownloadUrl () { 471 getTorrentDownloadUrl () {
472 if (!this.torrentFilename) return null
473
468 return WEBSERVER.URL + join(STATIC_DOWNLOAD_PATHS.TORRENTS, this.torrentFilename) 474 return WEBSERVER.URL + join(STATIC_DOWNLOAD_PATHS.TORRENTS, this.torrentFilename)
469 } 475 }
470 476
471 removeTorrent () { 477 removeTorrent () {
478 if (!this.torrentFilename) return null
479
472 const torrentPath = getTorrentFilePath(this) 480 const torrentPath = getTorrentFilePath(this)
473 return remove(torrentPath) 481 return remove(torrentPath)
474 .catch(err => logger.warn('Cannot delete torrent %s.', torrentPath, { err })) 482 .catch(err => logger.warn('Cannot delete torrent %s.', torrentPath, { err }))
diff --git a/server/models/video/video-format-utils.ts b/server/models/video/video-format-utils.ts
index a6a1a4f0d..bcba90093 100644
--- a/server/models/video/video-format-utils.ts
+++ b/server/models/video/video-format-utils.ts
@@ -205,7 +205,7 @@ function videoFilesModelToFormattedJSON (
205 label: videoFile.resolution + 'p' 205 label: videoFile.resolution + 'p'
206 }, 206 },
207 207
208 magnetUri: includeMagnet 208 magnetUri: includeMagnet && videoFile.torrentFilename
209 ? generateMagnetUri(video, videoFile, trackerUrls) 209 ? generateMagnetUri(video, videoFile, trackerUrls)
210 : undefined, 210 : undefined,
211 211
diff --git a/shared/extra-utils/videos/videos.ts b/shared/extra-utils/videos/videos.ts
index 929eb42ca..0b6a54046 100644
--- a/shared/extra-utils/videos/videos.ts
+++ b/shared/extra-utils/videos/videos.ts
@@ -1,6 +1,5 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/no-floating-promises */ 1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/no-floating-promises */
2 2
3import { HttpStatusCode } from '@shared/core-utils'
4import { expect } from 'chai' 3import { expect } from 'chai'
5import { pathExists, readdir, readFile } from 'fs-extra' 4import { pathExists, readdir, readFile } from 'fs-extra'
6import * as parseTorrent from 'parse-torrent' 5import * as parseTorrent from 'parse-torrent'
@@ -8,9 +7,18 @@ import { extname, join } from 'path'
8import * as request from 'supertest' 7import * as request from 'supertest'
9import { v4 as uuidv4 } from 'uuid' 8import { v4 as uuidv4 } from 'uuid'
10import validator from 'validator' 9import validator from 'validator'
10import { HttpStatusCode } from '@shared/core-utils'
11import { loadLanguages, VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_PRIVACIES } from '../../../server/initializers/constants' 11import { loadLanguages, VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_PRIVACIES } from '../../../server/initializers/constants'
12import { VideoDetails, VideoPrivacy } from '../../models/videos' 12import { VideoDetails, VideoPrivacy } from '../../models/videos'
13import { buildAbsoluteFixturePath, buildServerDirectory, dateIsValid, immutableAssign, testImage, webtorrentAdd } from '../miscs/miscs' 13import {
14 buildAbsoluteFixturePath,
15 buildServerDirectory,
16 dateIsValid,
17 immutableAssign,
18 testImage,
19 wait,
20 webtorrentAdd
21} from '../miscs/miscs'
14import { makeGetRequest, makePutBodyRequest, makeRawRequest, makeUploadRequest } from '../requests/requests' 22import { makeGetRequest, makePutBodyRequest, makeRawRequest, makeUploadRequest } from '../requests/requests'
15import { waitJobs } from '../server/jobs' 23import { waitJobs } from '../server/jobs'
16import { ServerInfo } from '../server/servers' 24import { ServerInfo } from '../server/servers'
@@ -423,8 +431,21 @@ async function uploadVideo (url: string, accessToken: string, videoAttributesArg
423 req.field('originallyPublishedAt', attributes.originallyPublishedAt) 431 req.field('originallyPublishedAt', attributes.originallyPublishedAt)
424 } 432 }
425 433
426 return req.attach('videofile', buildAbsoluteFixturePath(attributes.fixture)) 434 const res = await req.attach('videofile', buildAbsoluteFixturePath(attributes.fixture))
427 .expect(specialStatus) 435 .expect(specialStatus)
436
437 // Wait torrent generation
438 if (specialStatus === HttpStatusCode.OK_200) {
439 let video: VideoDetails
440 do {
441 const resVideo = await getVideoWithToken(url, accessToken, res.body.video.uuid)
442 video = resVideo.body
443
444 await wait(50)
445 } while (!video.files[0].torrentUrl)
446 }
447
448 return res
428} 449}
429 450
430function updateVideo ( 451function updateVideo (