aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/helpers
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2019-11-15 15:06:03 +0100
committerChocobozzz <me@florianbigard.com>2019-11-25 10:59:43 +0100
commitd7a25329f9e607894d29ab342b9cb66638b56dc0 (patch)
tree6cd6bc4f2689f78944238b313c93427423a932ac /server/helpers
parent14981d7331da3f63fe6cfaf020ccb7c910006eaf (diff)
downloadPeerTube-d7a25329f9e607894d29ab342b9cb66638b56dc0.tar.gz
PeerTube-d7a25329f9e607894d29ab342b9cb66638b56dc0.tar.zst
PeerTube-d7a25329f9e607894d29ab342b9cb66638b56dc0.zip
Add ability to disable webtorrent
In favour of HLS
Diffstat (limited to 'server/helpers')
-rw-r--r--server/helpers/custom-validators/activitypub/videos.ts36
-rw-r--r--server/helpers/database-utils.ts12
-rw-r--r--server/helpers/ffmpeg-utils.ts6
-rw-r--r--server/helpers/video.ts5
-rw-r--r--server/helpers/webtorrent.ts69
5 files changed, 105 insertions, 23 deletions
diff --git a/server/helpers/custom-validators/activitypub/videos.ts b/server/helpers/custom-validators/activitypub/videos.ts
index 02f914326..a28bebf2d 100644
--- a/server/helpers/custom-validators/activitypub/videos.ts
+++ b/server/helpers/custom-validators/activitypub/videos.ts
@@ -12,6 +12,7 @@ import {
12} from '../videos' 12} from '../videos'
13import { isActivityPubUrlValid, isBaseActivityValid, setValidAttributedTo } from './misc' 13import { isActivityPubUrlValid, isBaseActivityValid, setValidAttributedTo } from './misc'
14import { VideoState } from '../../../../shared/models/videos' 14import { VideoState } from '../../../../shared/models/videos'
15import { logger } from '@server/helpers/logger'
15 16
16function sanitizeAndCheckVideoTorrentUpdateActivity (activity: any) { 17function sanitizeAndCheckVideoTorrentUpdateActivity (activity: any) {
17 return isBaseActivityValid(activity, 'Update') && 18 return isBaseActivityValid(activity, 'Update') &&
@@ -30,11 +31,26 @@ function isActivityPubVideoDurationValid (value: string) {
30function sanitizeAndCheckVideoTorrentObject (video: any) { 31function sanitizeAndCheckVideoTorrentObject (video: any) {
31 if (!video || video.type !== 'Video') return false 32 if (!video || video.type !== 'Video') return false
32 33
33 if (!setValidRemoteTags(video)) return false 34 if (!setValidRemoteTags(video)) {
34 if (!setValidRemoteVideoUrls(video)) return false 35 logger.debug('Video has invalid tags', { video })
35 if (!setRemoteVideoTruncatedContent(video)) return false 36 return false
36 if (!setValidAttributedTo(video)) return false 37 }
37 if (!setValidRemoteCaptions(video)) return false 38 if (!setValidRemoteVideoUrls(video)) {
39 logger.debug('Video has invalid urls', { video })
40 return false
41 }
42 if (!setRemoteVideoTruncatedContent(video)) {
43 logger.debug('Video has invalid content', { video })
44 return false
45 }
46 if (!setValidAttributedTo(video)) {
47 logger.debug('Video has invalid attributedTo', { video })
48 return false
49 }
50 if (!setValidRemoteCaptions(video)) {
51 logger.debug('Video has invalid captions', { video })
52 return false
53 }
38 54
39 // Default attributes 55 // Default attributes
40 if (!isVideoStateValid(video.state)) video.state = VideoState.PUBLISHED 56 if (!isVideoStateValid(video.state)) video.state = VideoState.PUBLISHED
@@ -62,25 +78,21 @@ function sanitizeAndCheckVideoTorrentObject (video: any) {
62} 78}
63 79
64function isRemoteVideoUrlValid (url: any) { 80function isRemoteVideoUrlValid (url: any) {
65 // FIXME: Old bug, we used the width to represent the resolution. Remove it in a few release (currently beta.11)
66 if (url.width && !url.height) url.height = url.width
67
68 return url.type === 'Link' && 81 return url.type === 'Link' &&
69 ( 82 (
70 // TODO: remove mimeType (backward compatibility, introduced in v1.1.0) 83 ACTIVITY_PUB.URL_MIME_TYPES.VIDEO.indexOf(url.mediaType) !== -1 &&
71 ACTIVITY_PUB.URL_MIME_TYPES.VIDEO.indexOf(url.mediaType || url.mimeType) !== -1 &&
72 isActivityPubUrlValid(url.href) && 84 isActivityPubUrlValid(url.href) &&
73 validator.isInt(url.height + '', { min: 0 }) && 85 validator.isInt(url.height + '', { min: 0 }) &&
74 validator.isInt(url.size + '', { min: 0 }) && 86 validator.isInt(url.size + '', { min: 0 }) &&
75 (!url.fps || validator.isInt(url.fps + '', { min: -1 })) 87 (!url.fps || validator.isInt(url.fps + '', { min: -1 }))
76 ) || 88 ) ||
77 ( 89 (
78 ACTIVITY_PUB.URL_MIME_TYPES.TORRENT.indexOf(url.mediaType || url.mimeType) !== -1 && 90 ACTIVITY_PUB.URL_MIME_TYPES.TORRENT.indexOf(url.mediaType) !== -1 &&
79 isActivityPubUrlValid(url.href) && 91 isActivityPubUrlValid(url.href) &&
80 validator.isInt(url.height + '', { min: 0 }) 92 validator.isInt(url.height + '', { min: 0 })
81 ) || 93 ) ||
82 ( 94 (
83 ACTIVITY_PUB.URL_MIME_TYPES.MAGNET.indexOf(url.mediaType || url.mimeType) !== -1 && 95 ACTIVITY_PUB.URL_MIME_TYPES.MAGNET.indexOf(url.mediaType) !== -1 &&
84 validator.isLength(url.href, { min: 5 }) && 96 validator.isLength(url.href, { min: 5 }) &&
85 validator.isInt(url.height + '', { min: 0 }) 97 validator.isInt(url.height + '', { min: 0 })
86 ) || 98 ) ||
diff --git a/server/helpers/database-utils.ts b/server/helpers/database-utils.ts
index 6c5068fb0..87f10f913 100644
--- a/server/helpers/database-utils.ts
+++ b/server/helpers/database-utils.ts
@@ -79,6 +79,15 @@ function afterCommitIfTransaction (t: Transaction, fn: Function) {
79 return fn() 79 return fn()
80} 80}
81 81
82function deleteNonExistingModels <T extends { hasSameUniqueKeysThan (other: T): boolean } & Model<T>> (
83 fromDatabase: T[],
84 newModels: T[],
85 t: Transaction
86) {
87 return fromDatabase.filter(f => !newModels.find(newModel => newModel.hasSameUniqueKeysThan(f)))
88 .map(f => f.destroy({ transaction: t }))
89}
90
82// --------------------------------------------------------------------------- 91// ---------------------------------------------------------------------------
83 92
84export { 93export {
@@ -86,5 +95,6 @@ export {
86 retryTransactionWrapper, 95 retryTransactionWrapper,
87 transactionRetryer, 96 transactionRetryer,
88 updateInstanceWithAnother, 97 updateInstanceWithAnother,
89 afterCommitIfTransaction 98 afterCommitIfTransaction,
99 deleteNonExistingModels
90} 100}
diff --git a/server/helpers/ffmpeg-utils.ts b/server/helpers/ffmpeg-utils.ts
index c0e9702a8..7a4ac0970 100644
--- a/server/helpers/ffmpeg-utils.ts
+++ b/server/helpers/ffmpeg-utils.ts
@@ -130,6 +130,7 @@ interface BaseTranscodeOptions {
130 130
131interface HLSTranscodeOptions extends BaseTranscodeOptions { 131interface HLSTranscodeOptions extends BaseTranscodeOptions {
132 type: 'hls' 132 type: 'hls'
133 copyCodecs: boolean
133 hlsPlaylist: { 134 hlsPlaylist: {
134 videoFilename: string 135 videoFilename: string
135 } 136 }
@@ -232,7 +233,7 @@ export {
232 233
233// --------------------------------------------------------------------------- 234// ---------------------------------------------------------------------------
234 235
235async function buildx264Command (command: ffmpeg.FfmpegCommand, options: VideoTranscodeOptions) { 236async function buildx264Command (command: ffmpeg.FfmpegCommand, options: TranscodeOptions) {
236 let fps = await getVideoFileFPS(options.inputPath) 237 let fps = await getVideoFileFPS(options.inputPath)
237 // On small/medium resolutions, limit FPS 238 // On small/medium resolutions, limit FPS
238 if ( 239 if (
@@ -287,7 +288,8 @@ async function buildQuickTranscodeCommand (command: ffmpeg.FfmpegCommand) {
287async function buildHLSCommand (command: ffmpeg.FfmpegCommand, options: HLSTranscodeOptions) { 288async function buildHLSCommand (command: ffmpeg.FfmpegCommand, options: HLSTranscodeOptions) {
288 const videoPath = getHLSVideoPath(options) 289 const videoPath = getHLSVideoPath(options)
289 290
290 command = await presetCopy(command) 291 if (options.copyCodecs) command = await presetCopy(command)
292 else command = await buildx264Command(command, options)
291 293
292 command = command.outputOption('-hls_time 4') 294 command = command.outputOption('-hls_time 4')
293 .outputOption('-hls_list_size 0') 295 .outputOption('-hls_list_size 0')
diff --git a/server/helpers/video.ts b/server/helpers/video.ts
index d066e2b1f..5b9c026b1 100644
--- a/server/helpers/video.ts
+++ b/server/helpers/video.ts
@@ -45,10 +45,6 @@ function fetchVideoByUrl (url: string, fetchType: VideoFetchByUrlType): Bluebird
45 if (fetchType === 'only-video') return VideoModel.loadByUrl(url) 45 if (fetchType === 'only-video') return VideoModel.loadByUrl(url)
46} 46}
47 47
48function getVideo (res: Response) {
49 return res.locals.videoAll || res.locals.onlyVideo || res.locals.onlyVideoWithRights || res.locals.videoId
50}
51
52function getVideoWithAttributes (res: Response) { 48function getVideoWithAttributes (res: Response) {
53 return res.locals.videoAll || res.locals.onlyVideo || res.locals.onlyVideoWithRights 49 return res.locals.videoAll || res.locals.onlyVideo || res.locals.onlyVideoWithRights
54} 50}
@@ -57,7 +53,6 @@ export {
57 VideoFetchType, 53 VideoFetchType,
58 VideoFetchByUrlType, 54 VideoFetchByUrlType,
59 fetchVideo, 55 fetchVideo,
60 getVideo,
61 getVideoWithAttributes, 56 getVideoWithAttributes,
62 fetchVideoByUrl 57 fetchVideoByUrl
63} 58}
diff --git a/server/helpers/webtorrent.ts b/server/helpers/webtorrent.ts
index d2a22e8f0..f3e41f8d6 100644
--- a/server/helpers/webtorrent.ts
+++ b/server/helpers/webtorrent.ts
@@ -1,11 +1,22 @@
1import { logger } from './logger' 1import { logger } from './logger'
2import { generateVideoImportTmpPath } from './utils' 2import { generateVideoImportTmpPath } from './utils'
3import * as WebTorrent from 'webtorrent' 3import * as WebTorrent from 'webtorrent'
4import { createWriteStream, ensureDir, remove } from 'fs-extra' 4import { createWriteStream, ensureDir, remove, writeFile } from 'fs-extra'
5import { CONFIG } from '../initializers/config' 5import { CONFIG } from '../initializers/config'
6import { dirname, join } from 'path' 6import { dirname, join } from 'path'
7import * as createTorrent from 'create-torrent' 7import * as createTorrent from 'create-torrent'
8import { promisify2 } from './core-utils' 8import { promisify2 } from './core-utils'
9import { MVideo } from '@server/typings/models/video/video'
10import { MVideoFile, MVideoFileRedundanciesOpt } from '@server/typings/models/video/video-file'
11import { isStreamingPlaylist, MStreamingPlaylistVideo } from '@server/typings/models/video/video-streaming-playlist'
12import { STATIC_PATHS, WEBSERVER } from '@server/initializers/constants'
13import * as parseTorrent from 'parse-torrent'
14import * as magnetUtil from 'magnet-uri'
15import { isArray } from '@server/helpers/custom-validators/misc'
16import { extractVideo } from '@server/lib/videos'
17import { getTorrentFileName, getVideoFilename, getVideoFilePath } from '@server/lib/video-paths'
18
19const createTorrentPromise = promisify2<string, any, any>(createTorrent)
9 20
10async function downloadWebTorrentVideo (target: { magnetUri: string, torrentName?: string }, timeout: number) { 21async function downloadWebTorrentVideo (target: { magnetUri: string, torrentName?: string }, timeout: number) {
11 const id = target.magnetUri || target.torrentName 22 const id = target.magnetUri || target.torrentName
@@ -59,12 +70,64 @@ async function downloadWebTorrentVideo (target: { magnetUri: string, torrentName
59 }) 70 })
60} 71}
61 72
62const createTorrentPromise = promisify2<string, any, any>(createTorrent) 73async function createTorrentAndSetInfoHash (videoOrPlaylist: MVideo | MStreamingPlaylistVideo, videoFile: MVideoFile) {
74 const video = extractVideo(videoOrPlaylist)
75
76 const options = {
77 // Keep the extname, it's used by the client to stream the file inside a web browser
78 name: `${video.name} ${videoFile.resolution}p${videoFile.extname}`,
79 createdBy: 'PeerTube',
80 announceList: [
81 [ WEBSERVER.WS + '://' + WEBSERVER.HOSTNAME + ':' + WEBSERVER.PORT + '/tracker/socket' ],
82 [ WEBSERVER.URL + '/tracker/announce' ]
83 ],
84 urlList: [ WEBSERVER.URL + STATIC_PATHS.WEBSEED + getVideoFilename(videoOrPlaylist, videoFile) ]
85 }
86
87 const torrent = await createTorrentPromise(getVideoFilePath(videoOrPlaylist, videoFile), options)
88
89 const filePath = join(CONFIG.STORAGE.TORRENTS_DIR, getTorrentFileName(videoOrPlaylist, videoFile))
90 logger.info('Creating torrent %s.', filePath)
91
92 await writeFile(filePath, torrent)
93
94 const parsedTorrent = parseTorrent(torrent)
95 videoFile.infoHash = parsedTorrent.infoHash
96}
97
98function generateMagnetUri (
99 videoOrPlaylist: MVideo | MStreamingPlaylistVideo,
100 videoFile: MVideoFileRedundanciesOpt,
101 baseUrlHttp: string,
102 baseUrlWs: string
103) {
104 const video = isStreamingPlaylist(videoOrPlaylist)
105 ? videoOrPlaylist.Video
106 : videoOrPlaylist
107
108 const xs = videoOrPlaylist.getTorrentUrl(videoFile, baseUrlHttp)
109 const announce = videoOrPlaylist.getTrackerUrls(baseUrlHttp, baseUrlWs)
110 let urlList = [ videoOrPlaylist.getVideoFileUrl(videoFile, baseUrlHttp) ]
111
112 const redundancies = videoFile.RedundancyVideos
113 if (isArray(redundancies)) urlList = urlList.concat(redundancies.map(r => r.fileUrl))
114
115 const magnetHash = {
116 xs,
117 announce,
118 urlList,
119 infoHash: videoFile.infoHash,
120 name: video.name
121 }
122
123 return magnetUtil.encode(magnetHash)
124}
63 125
64// --------------------------------------------------------------------------- 126// ---------------------------------------------------------------------------
65 127
66export { 128export {
67 createTorrentPromise, 129 createTorrentAndSetInfoHash,
130 generateMagnetUri,
68 downloadWebTorrentVideo 131 downloadWebTorrentVideo
69} 132}
70 133