aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/lib
diff options
context:
space:
mode:
Diffstat (limited to 'server/lib')
-rw-r--r--server/lib/activitypub/videos.ts54
-rw-r--r--server/lib/files-cache/abstract-video-static-file-cache.ts2
-rw-r--r--server/lib/files-cache/videos-torrent-cache.ts54
-rw-r--r--server/lib/hls.ts4
-rw-r--r--server/lib/job-queue/handlers/video-file-import.ts14
-rw-r--r--server/lib/job-queue/handlers/video-import.ts8
-rw-r--r--server/lib/job-queue/handlers/video-live-ending.ts2
-rw-r--r--server/lib/job-queue/handlers/video-transcoding.ts2
-rw-r--r--server/lib/live-manager.ts6
-rw-r--r--server/lib/schedulers/videos-redundancy-scheduler.ts12
-rw-r--r--server/lib/video-paths.ts75
-rw-r--r--server/lib/video-transcoding.ts33
12 files changed, 194 insertions, 72 deletions
diff --git a/server/lib/activitypub/videos.ts b/server/lib/activitypub/videos.ts
index 66981f43f..a5f6537eb 100644
--- a/server/lib/activitypub/videos.ts
+++ b/server/lib/activitypub/videos.ts
@@ -1,7 +1,7 @@
1import * as Bluebird from 'bluebird' 1import * as Bluebird from 'bluebird'
2import { maxBy, minBy } from 'lodash' 2import { maxBy, minBy } from 'lodash'
3import * as magnetUtil from 'magnet-uri' 3import * as magnetUtil from 'magnet-uri'
4import { join } from 'path' 4import { basename, join } from 'path'
5import * as request from 'request' 5import * as request from 'request'
6import * as sequelize from 'sequelize' 6import * as sequelize from 'sequelize'
7import { VideoLiveModel } from '@server/models/video/video-live' 7import { VideoLiveModel } from '@server/models/video/video-live'
@@ -30,11 +30,11 @@ import { doRequest } from '../../helpers/requests'
30import { fetchVideoByUrl, getExtFromMimetype, VideoFetchByUrlType } from '../../helpers/video' 30import { fetchVideoByUrl, getExtFromMimetype, VideoFetchByUrlType } from '../../helpers/video'
31import { 31import {
32 ACTIVITY_PUB, 32 ACTIVITY_PUB,
33 LAZY_STATIC_PATHS,
33 MIMETYPES, 34 MIMETYPES,
34 P2P_MEDIA_LOADER_PEER_VERSION, 35 P2P_MEDIA_LOADER_PEER_VERSION,
35 PREVIEWS_SIZE, 36 PREVIEWS_SIZE,
36 REMOTE_SCHEME, 37 REMOTE_SCHEME,
37 STATIC_PATHS,
38 THUMBNAILS_SIZE 38 THUMBNAILS_SIZE
39} from '../../initializers/constants' 39} from '../../initializers/constants'
40import { sequelizeTypescript } from '../../initializers/database' 40import { sequelizeTypescript } from '../../initializers/database'
@@ -51,6 +51,8 @@ import {
51 MChannelDefault, 51 MChannelDefault,
52 MChannelId, 52 MChannelId,
53 MStreamingPlaylist, 53 MStreamingPlaylist,
54 MStreamingPlaylistFilesVideo,
55 MStreamingPlaylistVideo,
54 MVideo, 56 MVideo,
55 MVideoAccountLight, 57 MVideoAccountLight,
56 MVideoAccountLightBlacklistAllFiles, 58 MVideoAccountLightBlacklistAllFiles,
@@ -61,7 +63,8 @@ import {
61 MVideoFullLight, 63 MVideoFullLight,
62 MVideoId, 64 MVideoId,
63 MVideoImmutable, 65 MVideoImmutable,
64 MVideoThumbnail 66 MVideoThumbnail,
67 MVideoWithHost
65} from '../../types/models' 68} from '../../types/models'
66import { MThumbnail } from '../../types/models/video/thumbnail' 69import { MThumbnail } from '../../types/models/video/thumbnail'
67import { FilteredModelAttributes } from '../../types/sequelize' 70import { FilteredModelAttributes } from '../../types/sequelize'
@@ -72,6 +75,7 @@ import { PeerTubeSocket } from '../peertube-socket'
72import { createPlaceholderThumbnail, createVideoMiniatureFromUrl } from '../thumbnail' 75import { createPlaceholderThumbnail, createVideoMiniatureFromUrl } from '../thumbnail'
73import { setVideoTags } from '../video' 76import { setVideoTags } from '../video'
74import { autoBlacklistVideoIfNeeded } from '../video-blacklist' 77import { autoBlacklistVideoIfNeeded } from '../video-blacklist'
78import { generateTorrentFileName } from '../video-paths'
75import { getOrCreateActorAndServerAndModel } from './actor' 79import { getOrCreateActorAndServerAndModel } from './actor'
76import { crawlCollectionPage } from './crawl' 80import { crawlCollectionPage } from './crawl'
77import { sendCreateVideo, sendUpdateVideo } from './send' 81import { sendCreateVideo, sendUpdateVideo } from './send'
@@ -405,7 +409,8 @@ async function updateVideoFromAP (options: {
405 409
406 for (const playlistAttributes of streamingPlaylistAttributes) { 410 for (const playlistAttributes of streamingPlaylistAttributes) {
407 const streamingPlaylistModel = await VideoStreamingPlaylistModel.upsert(playlistAttributes, { returning: true, transaction: t }) 411 const streamingPlaylistModel = await VideoStreamingPlaylistModel.upsert(playlistAttributes, { returning: true, transaction: t })
408 .then(([ streamingPlaylist ]) => streamingPlaylist) 412 .then(([ streamingPlaylist ]) => streamingPlaylist as MStreamingPlaylistFilesVideo)
413 streamingPlaylistModel.Video = videoUpdated
409 414
410 const newVideoFiles: MVideoFile[] = videoFileActivityUrlToDBAttributes(streamingPlaylistModel, playlistAttributes.tagAPObject) 415 const newVideoFiles: MVideoFile[] = videoFileActivityUrlToDBAttributes(streamingPlaylistModel, playlistAttributes.tagAPObject)
411 .map(a => new VideoFileModel(a)) 416 .map(a => new VideoFileModel(a))
@@ -637,13 +642,14 @@ async function createVideo (videoObject: VideoObject, channel: MChannelAccountLi
637 videoCreated.VideoStreamingPlaylists = [] 642 videoCreated.VideoStreamingPlaylists = []
638 643
639 for (const playlistAttributes of streamingPlaylistsAttributes) { 644 for (const playlistAttributes of streamingPlaylistsAttributes) {
640 const playlistModel = await VideoStreamingPlaylistModel.create(playlistAttributes, { transaction: t }) 645 const playlist = await VideoStreamingPlaylistModel.create(playlistAttributes, { transaction: t }) as MStreamingPlaylistFilesVideo
646 playlist.Video = videoCreated
641 647
642 const playlistFiles = videoFileActivityUrlToDBAttributes(playlistModel, playlistAttributes.tagAPObject) 648 const playlistFiles = videoFileActivityUrlToDBAttributes(playlist, playlistAttributes.tagAPObject)
643 const videoFilePromises = playlistFiles.map(f => VideoFileModel.create(f, { transaction: t })) 649 const videoFilePromises = playlistFiles.map(f => VideoFileModel.create(f, { transaction: t }))
644 playlistModel.VideoFiles = await Promise.all(videoFilePromises) 650 playlist.VideoFiles = await Promise.all(videoFilePromises)
645 651
646 videoCreated.VideoStreamingPlaylists.push(playlistModel) 652 videoCreated.VideoStreamingPlaylists.push(playlist)
647 } 653 }
648 654
649 // Process tags 655 // Process tags
@@ -766,7 +772,7 @@ function videoActivityObjectToDBAttributes (videoChannel: MChannelId, videoObjec
766} 772}
767 773
768function videoFileActivityUrlToDBAttributes ( 774function videoFileActivityUrlToDBAttributes (
769 videoOrPlaylist: MVideo | MStreamingPlaylist, 775 videoOrPlaylist: MVideo | MStreamingPlaylistVideo,
770 urls: (ActivityTagObject | ActivityUrlObject)[] 776 urls: (ActivityTagObject | ActivityUrlObject)[]
771) { 777) {
772 const fileUrls = urls.filter(u => isAPVideoUrlObject(u)) as ActivityVideoUrlObject[] 778 const fileUrls = urls.filter(u => isAPVideoUrlObject(u)) as ActivityVideoUrlObject[]
@@ -786,6 +792,10 @@ function videoFileActivityUrlToDBAttributes (
786 throw new Error('Cannot parse magnet URI ' + magnet.href) 792 throw new Error('Cannot parse magnet URI ' + magnet.href)
787 } 793 }
788 794
795 const torrentUrl = Array.isArray(parsed.xs)
796 ? parsed.xs[0]
797 : parsed.xs
798
789 // Fetch associated metadata url, if any 799 // Fetch associated metadata url, if any
790 const metadata = urls.filter(isAPVideoFileMetadataObject) 800 const metadata = urls.filter(isAPVideoFileMetadataObject)
791 .find(u => { 801 .find(u => {
@@ -794,18 +804,30 @@ function videoFileActivityUrlToDBAttributes (
794 u.rel.includes(fileUrl.mediaType) 804 u.rel.includes(fileUrl.mediaType)
795 }) 805 })
796 806
797 const mediaType = fileUrl.mediaType 807 const extname = getExtFromMimetype(MIMETYPES.VIDEO.MIMETYPE_EXT, fileUrl.mediaType)
808 const resolution = fileUrl.height
809 const videoId = (videoOrPlaylist as MStreamingPlaylist).playlistUrl ? null : videoOrPlaylist.id
810 const videoStreamingPlaylistId = (videoOrPlaylist as MStreamingPlaylist).playlistUrl ? videoOrPlaylist.id : null
811
798 const attribute = { 812 const attribute = {
799 extname: getExtFromMimetype(MIMETYPES.VIDEO.MIMETYPE_EXT, mediaType), 813 extname,
800 infoHash: parsed.infoHash, 814 infoHash: parsed.infoHash,
801 resolution: fileUrl.height, 815 resolution,
802 size: fileUrl.size, 816 size: fileUrl.size,
803 fps: fileUrl.fps || -1, 817 fps: fileUrl.fps || -1,
804 metadataUrl: metadata?.href, 818 metadataUrl: metadata?.href,
805 819
820 // Use the name of the remote file because we don't proxify video file requests
821 filename: basename(fileUrl.href),
822 fileUrl: fileUrl.href,
823
824 torrentUrl,
825 // Use our own torrent name since we proxify torrent requests
826 torrentFilename: generateTorrentFileName(videoOrPlaylist, resolution),
827
806 // This is a video file owned by a video or by a streaming playlist 828 // This is a video file owned by a video or by a streaming playlist
807 videoId: (videoOrPlaylist as MStreamingPlaylist).playlistUrl ? null : videoOrPlaylist.id, 829 videoId,
808 videoStreamingPlaylistId: (videoOrPlaylist as MStreamingPlaylist).playlistUrl ? videoOrPlaylist.id : null 830 videoStreamingPlaylistId
809 } 831 }
810 832
811 attributes.push(attribute) 833 attributes.push(attribute)
@@ -862,8 +884,8 @@ function getPreviewFromIcons (videoObject: VideoObject) {
862 return maxBy(validIcons, 'width') 884 return maxBy(validIcons, 'width')
863} 885}
864 886
865function getPreviewUrl (previewIcon: ActivityIconObject, video: MVideoAccountLight) { 887function getPreviewUrl (previewIcon: ActivityIconObject, video: MVideoWithHost) {
866 return previewIcon 888 return previewIcon
867 ? previewIcon.url 889 ? previewIcon.url
868 : buildRemoteVideoBaseUrl(video, join(STATIC_PATHS.PREVIEWS, video.generatePreviewName())) 890 : buildRemoteVideoBaseUrl(video, join(LAZY_STATIC_PATHS.PREVIEWS, video.generatePreviewName()))
869} 891}
diff --git a/server/lib/files-cache/abstract-video-static-file-cache.ts b/server/lib/files-cache/abstract-video-static-file-cache.ts
index c06355446..af66689a0 100644
--- a/server/lib/files-cache/abstract-video-static-file-cache.ts
+++ b/server/lib/files-cache/abstract-video-static-file-cache.ts
@@ -2,7 +2,7 @@ import { remove } from 'fs-extra'
2import { logger } from '../../helpers/logger' 2import { logger } from '../../helpers/logger'
3import * as memoizee from 'memoizee' 3import * as memoizee from 'memoizee'
4 4
5type GetFilePathResult = { isOwned: boolean, path: string } | undefined 5type GetFilePathResult = { isOwned: boolean, path: string, downloadName?: string } | undefined
6 6
7export abstract class AbstractVideoStaticFileCache <T> { 7export abstract class AbstractVideoStaticFileCache <T> {
8 8
diff --git a/server/lib/files-cache/videos-torrent-cache.ts b/server/lib/files-cache/videos-torrent-cache.ts
new file mode 100644
index 000000000..ca0e1770d
--- /dev/null
+++ b/server/lib/files-cache/videos-torrent-cache.ts
@@ -0,0 +1,54 @@
1import { join } from 'path'
2import { doRequestAndSaveToFile } from '@server/helpers/requests'
3import { VideoFileModel } from '@server/models/video/video-file'
4import { CONFIG } from '../../initializers/config'
5import { FILES_CACHE } from '../../initializers/constants'
6import { VideoModel } from '../../models/video/video'
7import { AbstractVideoStaticFileCache } from './abstract-video-static-file-cache'
8
9class VideosTorrentCache extends AbstractVideoStaticFileCache <string> {
10
11 private static instance: VideosTorrentCache
12
13 private constructor () {
14 super()
15 }
16
17 static get Instance () {
18 return this.instance || (this.instance = new this())
19 }
20
21 async getFilePathImpl (filename: string) {
22 const file = await VideoFileModel.loadWithVideoOrPlaylistByTorrentFilename(filename)
23 if (!file) return undefined
24
25 if (file.getVideo().isOwned()) return { isOwned: true, path: join(CONFIG.STORAGE.TORRENTS_DIR, file.torrentFilename) }
26
27 return this.loadRemoteFile(filename)
28 }
29
30 // Key is the torrent filename
31 protected async loadRemoteFile (key: string) {
32 const file = await VideoFileModel.loadWithVideoOrPlaylistByTorrentFilename(key)
33 if (!file) return undefined
34
35 if (file.getVideo().isOwned()) throw new Error('Cannot load remote file of owned video.')
36
37 // Used to fetch the path
38 const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(file.getVideo().id)
39 if (!video) return undefined
40
41 const remoteUrl = file.getRemoteTorrentUrl(video)
42 const destPath = join(FILES_CACHE.TORRENTS.DIRECTORY, file.torrentFilename)
43
44 await doRequestAndSaveToFile({ uri: remoteUrl }, destPath)
45
46 const downloadName = `${video.name}-${file.resolution}p.torrent`
47
48 return { isOwned: false, path: destPath, downloadName }
49 }
50}
51
52export {
53 VideosTorrentCache
54}
diff --git a/server/lib/hls.ts b/server/lib/hls.ts
index ef489097a..04187668c 100644
--- a/server/lib/hls.ts
+++ b/server/lib/hls.ts
@@ -12,7 +12,7 @@ import { HLS_STREAMING_PLAYLIST_DIRECTORY, P2P_MEDIA_LOADER_PEER_VERSION } from
12import { sequelizeTypescript } from '../initializers/database' 12import { sequelizeTypescript } from '../initializers/database'
13import { VideoFileModel } from '../models/video/video-file' 13import { VideoFileModel } from '../models/video/video-file'
14import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-playlist' 14import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-playlist'
15import { getVideoFilename, getVideoFilePath } from './video-paths' 15import { getVideoFilePath } from './video-paths'
16 16
17async function updateStreamingPlaylistsInfohashesIfNeeded () { 17async function updateStreamingPlaylistsInfohashesIfNeeded () {
18 const playlistsToUpdate = await VideoStreamingPlaylistModel.listByIncorrectPeerVersion() 18 const playlistsToUpdate = await VideoStreamingPlaylistModel.listByIncorrectPeerVersion()
@@ -93,7 +93,7 @@ async function updateSha256VODSegments (video: MVideoWithFile) {
93 } 93 }
94 await close(fd) 94 await close(fd)
95 95
96 const videoFilename = getVideoFilename(hlsPlaylist, file) 96 const videoFilename = file.filename
97 json[videoFilename] = rangeHashes 97 json[videoFilename] = rangeHashes
98 } 98 }
99 99
diff --git a/server/lib/job-queue/handlers/video-file-import.ts b/server/lib/job-queue/handlers/video-file-import.ts
index cd95aa075..86c9b5c29 100644
--- a/server/lib/job-queue/handlers/video-file-import.ts
+++ b/server/lib/job-queue/handlers/video-file-import.ts
@@ -2,9 +2,9 @@ import * as Bull from 'bull'
2import { copy, stat } from 'fs-extra' 2import { copy, stat } from 'fs-extra'
3import { extname } from 'path' 3import { extname } from 'path'
4import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent' 4import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent'
5import { getVideoFilePath } from '@server/lib/video-paths' 5import { generateVideoFilename, getVideoFilePath } from '@server/lib/video-paths'
6import { UserModel } from '@server/models/account/user' 6import { UserModel } from '@server/models/account/user'
7import { MVideoFile, MVideoWithFile } from '@server/types/models' 7import { MVideoFile, MVideoFullLight } from '@server/types/models'
8import { VideoFileImportPayload } from '@shared/models' 8import { VideoFileImportPayload } from '@shared/models'
9import { getVideoFileFPS, getVideoFileResolution } from '../../../helpers/ffprobe-utils' 9import { getVideoFileFPS, getVideoFileResolution } from '../../../helpers/ffprobe-utils'
10import { logger } from '../../../helpers/logger' 10import { logger } from '../../../helpers/logger'
@@ -50,14 +50,16 @@ export {
50 50
51// --------------------------------------------------------------------------- 51// ---------------------------------------------------------------------------
52 52
53async function updateVideoFile (video: MVideoWithFile, inputFilePath: string) { 53async function updateVideoFile (video: MVideoFullLight, inputFilePath: string) {
54 const { videoFileResolution } = await getVideoFileResolution(inputFilePath) 54 const { videoFileResolution } = await getVideoFileResolution(inputFilePath)
55 const { size } = await stat(inputFilePath) 55 const { size } = await stat(inputFilePath)
56 const fps = await getVideoFileFPS(inputFilePath) 56 const fps = await getVideoFileFPS(inputFilePath)
57 57
58 const fileExt = extname(inputFilePath)
58 let updatedVideoFile = new VideoFileModel({ 59 let updatedVideoFile = new VideoFileModel({
59 resolution: videoFileResolution, 60 resolution: videoFileResolution,
60 extname: extname(inputFilePath), 61 extname: fileExt,
62 filename: generateVideoFilename(video, false, videoFileResolution, fileExt),
61 size, 63 size,
62 fps, 64 fps,
63 videoId: video.id 65 videoId: video.id
@@ -68,7 +70,7 @@ async function updateVideoFile (video: MVideoWithFile, inputFilePath: string) {
68 if (currentVideoFile) { 70 if (currentVideoFile) {
69 // Remove old file and old torrent 71 // Remove old file and old torrent
70 await video.removeFile(currentVideoFile) 72 await video.removeFile(currentVideoFile)
71 await video.removeTorrent(currentVideoFile) 73 await currentVideoFile.removeTorrent()
72 // Remove the old video file from the array 74 // Remove the old video file from the array
73 video.VideoFiles = video.VideoFiles.filter(f => f !== currentVideoFile) 75 video.VideoFiles = video.VideoFiles.filter(f => f !== currentVideoFile)
74 76
@@ -83,7 +85,7 @@ async function updateVideoFile (video: MVideoWithFile, inputFilePath: string) {
83 const outputPath = getVideoFilePath(video, updatedVideoFile) 85 const outputPath = getVideoFilePath(video, updatedVideoFile)
84 await copy(inputFilePath, outputPath) 86 await copy(inputFilePath, outputPath)
85 87
86 await createTorrentAndSetInfoHash(video, updatedVideoFile) 88 await createTorrentAndSetInfoHash(video, video, updatedVideoFile)
87 89
88 await updatedVideoFile.save() 90 await updatedVideoFile.save()
89 91
diff --git a/server/lib/job-queue/handlers/video-import.ts b/server/lib/job-queue/handlers/video-import.ts
index 0d00c1b9d..8fa024105 100644
--- a/server/lib/job-queue/handlers/video-import.ts
+++ b/server/lib/job-queue/handlers/video-import.ts
@@ -6,7 +6,7 @@ import { isPostImportVideoAccepted } from '@server/lib/moderation'
6import { Hooks } from '@server/lib/plugins/hooks' 6import { Hooks } from '@server/lib/plugins/hooks'
7import { isAbleToUploadVideo } from '@server/lib/user' 7import { isAbleToUploadVideo } from '@server/lib/user'
8import { addOptimizeOrMergeAudioJob } from '@server/lib/video' 8import { addOptimizeOrMergeAudioJob } from '@server/lib/video'
9import { getVideoFilePath } from '@server/lib/video-paths' 9import { generateVideoFilename, getVideoFilePath } from '@server/lib/video-paths'
10import { ThumbnailModel } from '@server/models/video/thumbnail' 10import { ThumbnailModel } from '@server/models/video/thumbnail'
11import { MVideoImportDefault, MVideoImportDefaultFiles, MVideoImportVideo } from '@server/types/models/video/video-import' 11import { MVideoImportDefault, MVideoImportDefaultFiles, MVideoImportVideo } from '@server/types/models/video/video-import'
12import { 12import {
@@ -116,10 +116,12 @@ async function processFile (downloader: () => Promise<string>, videoImport: MVid
116 const duration = await getDurationFromVideoFile(tempVideoPath) 116 const duration = await getDurationFromVideoFile(tempVideoPath)
117 117
118 // Prepare video file object for creation in database 118 // Prepare video file object for creation in database
119 const fileExt = extname(tempVideoPath)
119 const videoFileData = { 120 const videoFileData = {
120 extname: extname(tempVideoPath), 121 extname: fileExt,
121 resolution: videoFileResolution, 122 resolution: videoFileResolution,
122 size: stats.size, 123 size: stats.size,
124 filename: generateVideoFilename(videoImport.Video, false, videoFileResolution, fileExt),
123 fps, 125 fps,
124 videoId: videoImport.videoId 126 videoId: videoImport.videoId
125 } 127 }
@@ -183,7 +185,7 @@ async function processFile (downloader: () => Promise<string>, videoImport: MVid
183 } 185 }
184 186
185 // Create torrent 187 // Create torrent
186 await createTorrentAndSetInfoHash(videoImportWithFiles.Video, videoFile) 188 await createTorrentAndSetInfoHash(videoImportWithFiles.Video, videoImportWithFiles.Video, videoFile)
187 189
188 const videoFileSave = videoFile.toJSON() 190 const videoFileSave = videoFile.toJSON()
189 191
diff --git a/server/lib/job-queue/handlers/video-live-ending.ts b/server/lib/job-queue/handlers/video-live-ending.ts
index 6d50635bb..d57202ca5 100644
--- a/server/lib/job-queue/handlers/video-live-ending.ts
+++ b/server/lib/job-queue/handlers/video-live-ending.ts
@@ -85,7 +85,7 @@ async function saveLive (video: MVideo, live: MVideoLive) {
85 await video.save() 85 await video.save()
86 86
87 // Remove old HLS playlist video files 87 // Remove old HLS playlist video files
88 const videoWithFiles = await VideoModel.loadWithFiles(video.id) 88 const videoWithFiles = await VideoModel.loadAndPopulateAccountAndServerAndTags(video.id)
89 89
90 const hlsPlaylist = videoWithFiles.getHLSPlaylist() 90 const hlsPlaylist = videoWithFiles.getHLSPlaylist()
91 await VideoFileModel.removeHLSFilesOfVideoId(hlsPlaylist.id) 91 await VideoFileModel.removeHLSFilesOfVideoId(hlsPlaylist.id)
diff --git a/server/lib/job-queue/handlers/video-transcoding.ts b/server/lib/job-queue/handlers/video-transcoding.ts
index e248b645e..8573d4d12 100644
--- a/server/lib/job-queue/handlers/video-transcoding.ts
+++ b/server/lib/job-queue/handlers/video-transcoding.ts
@@ -128,7 +128,7 @@ async function onHlsPlaylistGeneration (video: MVideoFullLight, user: MUser, pay
128 // Remove webtorrent files if not enabled 128 // Remove webtorrent files if not enabled
129 for (const file of video.VideoFiles) { 129 for (const file of video.VideoFiles) {
130 await video.removeFile(file) 130 await video.removeFile(file)
131 await video.removeTorrent(file) 131 await file.removeTorrent()
132 await file.destroy() 132 await file.destroy()
133 } 133 }
134 134
diff --git a/server/lib/live-manager.ts b/server/lib/live-manager.ts
index 9f17b8820..b549c189f 100644
--- a/server/lib/live-manager.ts
+++ b/server/lib/live-manager.ts
@@ -16,7 +16,7 @@ import { VideoModel } from '@server/models/video/video'
16import { VideoFileModel } from '@server/models/video/video-file' 16import { VideoFileModel } from '@server/models/video/video-file'
17import { VideoLiveModel } from '@server/models/video/video-live' 17import { VideoLiveModel } from '@server/models/video/video-live'
18import { VideoStreamingPlaylistModel } from '@server/models/video/video-streaming-playlist' 18import { VideoStreamingPlaylistModel } from '@server/models/video/video-streaming-playlist'
19import { MStreamingPlaylist, MUserId, MVideoLive, MVideoLiveVideo } from '@server/types/models' 19import { MStreamingPlaylist, MStreamingPlaylistVideo, MUserId, MVideoLive, MVideoLiveVideo } from '@server/types/models'
20import { VideoState, VideoStreamingPlaylistType } from '@shared/models' 20import { VideoState, VideoStreamingPlaylistType } from '@shared/models'
21import { federateVideoIfNeeded } from './activitypub/videos' 21import { federateVideoIfNeeded } from './activitypub/videos'
22import { buildSha256Segment } from './hls' 22import { buildSha256Segment } from './hls'
@@ -277,7 +277,7 @@ class LiveManager {
277 return this.runMuxing({ 277 return this.runMuxing({
278 sessionId, 278 sessionId,
279 videoLive, 279 videoLive,
280 playlist: videoStreamingPlaylist, 280 playlist: Object.assign(videoStreamingPlaylist, { Video: video }),
281 rtmpUrl, 281 rtmpUrl,
282 fps, 282 fps,
283 allResolutions 283 allResolutions
@@ -287,7 +287,7 @@ class LiveManager {
287 private async runMuxing (options: { 287 private async runMuxing (options: {
288 sessionId: string 288 sessionId: string
289 videoLive: MVideoLiveVideo 289 videoLive: MVideoLiveVideo
290 playlist: MStreamingPlaylist 290 playlist: MStreamingPlaylistVideo
291 rtmpUrl: string 291 rtmpUrl: string
292 fps: number 292 fps: number
293 allResolutions: number[] 293 allResolutions: number[]
diff --git a/server/lib/schedulers/videos-redundancy-scheduler.ts b/server/lib/schedulers/videos-redundancy-scheduler.ts
index 93e76626c..60008e695 100644
--- a/server/lib/schedulers/videos-redundancy-scheduler.ts
+++ b/server/lib/schedulers/videos-redundancy-scheduler.ts
@@ -18,14 +18,14 @@ import { VideosRedundancyStrategy } from '../../../shared/models/redundancy'
18import { logger } from '../../helpers/logger' 18import { logger } from '../../helpers/logger'
19import { downloadWebTorrentVideo, generateMagnetUri } from '../../helpers/webtorrent' 19import { downloadWebTorrentVideo, generateMagnetUri } from '../../helpers/webtorrent'
20import { CONFIG } from '../../initializers/config' 20import { CONFIG } from '../../initializers/config'
21import { HLS_REDUNDANCY_DIRECTORY, REDUNDANCY, VIDEO_IMPORT_TIMEOUT, WEBSERVER } from '../../initializers/constants' 21import { HLS_REDUNDANCY_DIRECTORY, REDUNDANCY, VIDEO_IMPORT_TIMEOUT } from '../../initializers/constants'
22import { VideoRedundancyModel } from '../../models/redundancy/video-redundancy' 22import { VideoRedundancyModel } from '../../models/redundancy/video-redundancy'
23import { sendCreateCacheFile, sendUpdateCacheFile } from '../activitypub/send' 23import { sendCreateCacheFile, sendUpdateCacheFile } from '../activitypub/send'
24import { getLocalVideoCacheFileActivityPubUrl, getLocalVideoCacheStreamingPlaylistActivityPubUrl } from '../activitypub/url' 24import { getLocalVideoCacheFileActivityPubUrl, getLocalVideoCacheStreamingPlaylistActivityPubUrl } from '../activitypub/url'
25import { getOrCreateVideoAndAccountAndChannel } from '../activitypub/videos' 25import { getOrCreateVideoAndAccountAndChannel } from '../activitypub/videos'
26import { downloadPlaylistSegments } from '../hls' 26import { downloadPlaylistSegments } from '../hls'
27import { removeVideoRedundancy } from '../redundancy' 27import { removeVideoRedundancy } from '../redundancy'
28import { getVideoFilename } from '../video-paths' 28import { generateHLSRedundancyUrl, generateWebTorrentRedundancyUrl } from '../video-paths'
29import { AbstractScheduler } from './abstract-scheduler' 29import { AbstractScheduler } from './abstract-scheduler'
30 30
31type CandidateToDuplicate = { 31type CandidateToDuplicate = {
@@ -222,17 +222,17 @@ export class VideosRedundancyScheduler extends AbstractScheduler {
222 logger.info('Duplicating %s - %d in videos redundancy with "%s" strategy.', video.url, file.resolution, strategy) 222 logger.info('Duplicating %s - %d in videos redundancy with "%s" strategy.', video.url, file.resolution, strategy)
223 223
224 const { baseUrlHttp, baseUrlWs } = video.getBaseUrls() 224 const { baseUrlHttp, baseUrlWs } = video.getBaseUrls()
225 const magnetUri = generateMagnetUri(video, file, baseUrlHttp, baseUrlWs) 225 const magnetUri = generateMagnetUri(video, video, file, baseUrlHttp, baseUrlWs)
226 226
227 const tmpPath = await downloadWebTorrentVideo({ magnetUri }, VIDEO_IMPORT_TIMEOUT) 227 const tmpPath = await downloadWebTorrentVideo({ magnetUri }, VIDEO_IMPORT_TIMEOUT)
228 228
229 const destPath = join(CONFIG.STORAGE.REDUNDANCY_DIR, getVideoFilename(video, file)) 229 const destPath = join(CONFIG.STORAGE.REDUNDANCY_DIR, file.filename)
230 await move(tmpPath, destPath, { overwrite: true }) 230 await move(tmpPath, destPath, { overwrite: true })
231 231
232 const createdModel: MVideoRedundancyFileVideo = await VideoRedundancyModel.create({ 232 const createdModel: MVideoRedundancyFileVideo = await VideoRedundancyModel.create({
233 expiresOn, 233 expiresOn,
234 url: getLocalVideoCacheFileActivityPubUrl(file), 234 url: getLocalVideoCacheFileActivityPubUrl(file),
235 fileUrl: video.getVideoRedundancyUrl(file, WEBSERVER.URL), 235 fileUrl: generateWebTorrentRedundancyUrl(file),
236 strategy, 236 strategy,
237 videoFileId: file.id, 237 videoFileId: file.id,
238 actorId: serverActor.id 238 actorId: serverActor.id
@@ -271,7 +271,7 @@ export class VideosRedundancyScheduler extends AbstractScheduler {
271 const createdModel: MVideoRedundancyStreamingPlaylistVideo = await VideoRedundancyModel.create({ 271 const createdModel: MVideoRedundancyStreamingPlaylistVideo = await VideoRedundancyModel.create({
272 expiresOn, 272 expiresOn,
273 url: getLocalVideoCacheStreamingPlaylistActivityPubUrl(video, playlist), 273 url: getLocalVideoCacheStreamingPlaylistActivityPubUrl(video, playlist),
274 fileUrl: playlist.getVideoRedundancyUrl(WEBSERVER.URL), 274 fileUrl: generateHLSRedundancyUrl(video, playlistArg),
275 strategy, 275 strategy,
276 videoStreamingPlaylistId: playlist.id, 276 videoStreamingPlaylistId: playlist.id,
277 actorId: serverActor.id 277 actorId: serverActor.id
diff --git a/server/lib/video-paths.ts b/server/lib/video-paths.ts
index 53fc8e81d..0385e89cc 100644
--- a/server/lib/video-paths.ts
+++ b/server/lib/video-paths.ts
@@ -1,19 +1,23 @@
1import { isStreamingPlaylist, MStreamingPlaylistVideo, MVideo, MVideoFile, MVideoUUID } from '@server/types/models'
2import { join } from 'path' 1import { join } from 'path'
3import { CONFIG } from '@server/initializers/config'
4import { HLS_REDUNDANCY_DIRECTORY, HLS_STREAMING_PLAYLIST_DIRECTORY } from '@server/initializers/constants'
5import { extractVideo } from '@server/helpers/video' 2import { extractVideo } from '@server/helpers/video'
3import { CONFIG } from '@server/initializers/config'
4import { HLS_REDUNDANCY_DIRECTORY, HLS_STREAMING_PLAYLIST_DIRECTORY, STATIC_PATHS, WEBSERVER } from '@server/initializers/constants'
5import { isStreamingPlaylist, MStreamingPlaylist, MStreamingPlaylistVideo, MVideo, MVideoFile, MVideoUUID } from '@server/types/models'
6 6
7// ################## Video file name ################## 7// ################## Video file name ##################
8 8
9function getVideoFilename (videoOrPlaylist: MVideo | MStreamingPlaylistVideo, videoFile: MVideoFile) { 9function generateVideoFilename (videoOrPlaylist: MVideo | MStreamingPlaylistVideo, isHls: boolean, resolution: number, extname: string) {
10 const video = extractVideo(videoOrPlaylist) 10 const video = extractVideo(videoOrPlaylist)
11 11
12 if (videoFile.isHLS()) { 12 // FIXME: use a generated uuid instead, that will break compatibility with PeerTube < 3.2
13 return generateVideoStreamingPlaylistName(video.uuid, videoFile.resolution) 13 // const uuid = uuidv4()
14 const uuid = video.uuid
15
16 if (isHls) {
17 return generateVideoStreamingPlaylistName(uuid, resolution)
14 } 18 }
15 19
16 return generateWebTorrentVideoName(video.uuid, videoFile.resolution, videoFile.extname) 20 return generateWebTorrentVideoName(uuid, resolution, extname)
17} 21}
18 22
19function generateVideoStreamingPlaylistName (uuid: string, resolution: number) { 23function generateVideoStreamingPlaylistName (uuid: string, resolution: number) {
@@ -28,36 +32,64 @@ function getVideoFilePath (videoOrPlaylist: MVideo | MStreamingPlaylistVideo, vi
28 if (videoFile.isHLS()) { 32 if (videoFile.isHLS()) {
29 const video = extractVideo(videoOrPlaylist) 33 const video = extractVideo(videoOrPlaylist)
30 34
31 return join(getHLSDirectory(video), getVideoFilename(videoOrPlaylist, videoFile)) 35 return join(getHLSDirectory(video), videoFile.filename)
32 } 36 }
33 37
34 const baseDir = isRedundancy ? CONFIG.STORAGE.REDUNDANCY_DIR : CONFIG.STORAGE.VIDEOS_DIR 38 const baseDir = isRedundancy
35 return join(baseDir, getVideoFilename(videoOrPlaylist, videoFile)) 39 ? CONFIG.STORAGE.REDUNDANCY_DIR
40 : CONFIG.STORAGE.VIDEOS_DIR
41
42 return join(baseDir, videoFile.filename)
43}
44
45// ################## Redundancy ##################
46
47function generateHLSRedundancyUrl (video: MVideo, playlist: MStreamingPlaylist) {
48 // Base URL used by our HLS player
49 return WEBSERVER.URL + STATIC_PATHS.REDUNDANCY + playlist.getStringType() + '/' + video.uuid
50}
51
52function generateWebTorrentRedundancyUrl (file: MVideoFile) {
53 return WEBSERVER.URL + STATIC_PATHS.REDUNDANCY + file.filename
36} 54}
37 55
38// ################## Streaming playlist ################## 56// ################## Streaming playlist ##################
39 57
40function getHLSDirectory (video: MVideoUUID, isRedundancy = false) { 58function getHLSDirectory (video: MVideoUUID, isRedundancy = false) {
41 const baseDir = isRedundancy ? HLS_REDUNDANCY_DIRECTORY : HLS_STREAMING_PLAYLIST_DIRECTORY 59 const baseDir = isRedundancy
60 ? HLS_REDUNDANCY_DIRECTORY
61 : HLS_STREAMING_PLAYLIST_DIRECTORY
42 62
43 return join(baseDir, video.uuid) 63 return join(baseDir, video.uuid)
44} 64}
45 65
46// ################## Torrents ################## 66// ################## Torrents ##################
47 67
48function getTorrentFileName (videoOrPlaylist: MVideo | MStreamingPlaylistVideo, videoFile: MVideoFile) { 68function generateTorrentFileName (videoOrPlaylist: MVideo | MStreamingPlaylistVideo, resolution: number) {
49 const video = extractVideo(videoOrPlaylist) 69 const video = extractVideo(videoOrPlaylist)
50 const extension = '.torrent' 70 const extension = '.torrent'
51 71
72 // FIXME: use a generated uuid instead, that will break compatibility with PeerTube < 3.2
73 // const uuid = uuidv4()
74 const uuid = video.uuid
75
52 if (isStreamingPlaylist(videoOrPlaylist)) { 76 if (isStreamingPlaylist(videoOrPlaylist)) {
53 return `${video.uuid}-${videoFile.resolution}-${videoOrPlaylist.getStringType()}${extension}` 77 return `${uuid}-${resolution}-${videoOrPlaylist.getStringType()}${extension}`
54 } 78 }
55 79
56 return video.uuid + '-' + videoFile.resolution + extension 80 return uuid + '-' + resolution + extension
81}
82
83function getTorrentFilePath (videoFile: MVideoFile) {
84 return join(CONFIG.STORAGE.TORRENTS_DIR, videoFile.torrentFilename)
57} 85}
58 86
59function getTorrentFilePath (videoOrPlaylist: MVideo | MStreamingPlaylistVideo, videoFile: MVideoFile) { 87// ################## Meta data ##################
60 return join(CONFIG.STORAGE.TORRENTS_DIR, getTorrentFileName(videoOrPlaylist, videoFile)) 88
89function getLocalVideoFileMetadataUrl (video: MVideoUUID, videoFile: MVideoFile) {
90 const path = '/api/v1/videos/'
91
92 return WEBSERVER.URL + path + video.uuid + '/metadata/' + videoFile.id
61} 93}
62 94
63// --------------------------------------------------------------------------- 95// ---------------------------------------------------------------------------
@@ -65,11 +97,16 @@ function getTorrentFilePath (videoOrPlaylist: MVideo | MStreamingPlaylistVideo,
65export { 97export {
66 generateVideoStreamingPlaylistName, 98 generateVideoStreamingPlaylistName,
67 generateWebTorrentVideoName, 99 generateWebTorrentVideoName,
68 getVideoFilename, 100 generateVideoFilename,
69 getVideoFilePath, 101 getVideoFilePath,
70 102
71 getTorrentFileName, 103 generateTorrentFileName,
72 getTorrentFilePath, 104 getTorrentFilePath,
73 105
74 getHLSDirectory 106 getHLSDirectory,
107
108 getLocalVideoFileMetadataUrl,
109
110 generateWebTorrentRedundancyUrl,
111 generateHLSRedundancyUrl
75} 112}
diff --git a/server/lib/video-transcoding.ts b/server/lib/video-transcoding.ts
index a58c9dd20..b366e2e44 100644
--- a/server/lib/video-transcoding.ts
+++ b/server/lib/video-transcoding.ts
@@ -2,7 +2,7 @@ import { Job } from 'bull'
2import { copyFile, ensureDir, move, remove, stat } from 'fs-extra' 2import { copyFile, ensureDir, move, remove, stat } from 'fs-extra'
3import { basename, extname as extnameUtil, join } from 'path' 3import { basename, extname as extnameUtil, join } from 'path'
4import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent' 4import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent'
5import { MStreamingPlaylistFilesVideo, MVideoFile, MVideoWithAllFiles, MVideoWithFile } from '@server/types/models' 5import { MStreamingPlaylistFilesVideo, MVideoFile, MVideoFullLight } from '@server/types/models'
6import { VideoResolution } from '../../shared/models/videos' 6import { VideoResolution } from '../../shared/models/videos'
7import { VideoStreamingPlaylistType } from '../../shared/models/videos/video-streaming-playlist.type' 7import { VideoStreamingPlaylistType } from '../../shared/models/videos/video-streaming-playlist.type'
8import { transcode, TranscodeOptions, TranscodeOptionsType } from '../helpers/ffmpeg-utils' 8import { transcode, TranscodeOptions, TranscodeOptionsType } from '../helpers/ffmpeg-utils'
@@ -13,7 +13,7 @@ import { HLS_STREAMING_PLAYLIST_DIRECTORY, P2P_MEDIA_LOADER_PEER_VERSION, WEBSER
13import { VideoFileModel } from '../models/video/video-file' 13import { VideoFileModel } from '../models/video/video-file'
14import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-playlist' 14import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-playlist'
15import { updateMasterHLSPlaylist, updateSha256VODSegments } from './hls' 15import { updateMasterHLSPlaylist, updateSha256VODSegments } from './hls'
16import { generateVideoStreamingPlaylistName, getVideoFilename, getVideoFilePath } from './video-paths' 16import { generateVideoFilename, generateVideoStreamingPlaylistName, getVideoFilePath } from './video-paths'
17import { VideoTranscodingProfilesManager } from './video-transcoding-profiles' 17import { VideoTranscodingProfilesManager } from './video-transcoding-profiles'
18 18
19/** 19/**
@@ -24,7 +24,7 @@ import { VideoTranscodingProfilesManager } from './video-transcoding-profiles'
24 */ 24 */
25 25
26// Optimize the original video file and replace it. The resolution is not changed. 26// Optimize the original video file and replace it. The resolution is not changed.
27async function optimizeOriginalVideofile (video: MVideoWithFile, inputVideoFile: MVideoFile, job?: Job) { 27async function optimizeOriginalVideofile (video: MVideoFullLight, inputVideoFile: MVideoFile, job?: Job) {
28 const transcodeDirectory = CONFIG.STORAGE.TMP_DIR 28 const transcodeDirectory = CONFIG.STORAGE.TMP_DIR
29 const newExtname = '.mp4' 29 const newExtname = '.mp4'
30 30
@@ -55,8 +55,9 @@ async function optimizeOriginalVideofile (video: MVideoWithFile, inputVideoFile:
55 try { 55 try {
56 await remove(videoInputPath) 56 await remove(videoInputPath)
57 57
58 // Important to do this before getVideoFilename() to take in account the new file extension 58 // Important to do this before getVideoFilename() to take in account the new filename
59 inputVideoFile.extname = newExtname 59 inputVideoFile.extname = newExtname
60 inputVideoFile.filename = generateVideoFilename(video, false, inputVideoFile.resolution, newExtname)
60 61
61 const videoOutputPath = getVideoFilePath(video, inputVideoFile) 62 const videoOutputPath = getVideoFilePath(video, inputVideoFile)
62 63
@@ -72,7 +73,7 @@ async function optimizeOriginalVideofile (video: MVideoWithFile, inputVideoFile:
72} 73}
73 74
74// Transcode the original video file to a lower resolution. 75// Transcode the original video file to a lower resolution.
75async function transcodeNewWebTorrentResolution (video: MVideoWithFile, resolution: VideoResolution, isPortrait: boolean, job: Job) { 76async function transcodeNewWebTorrentResolution (video: MVideoFullLight, resolution: VideoResolution, isPortrait: boolean, job: Job) {
76 const transcodeDirectory = CONFIG.STORAGE.TMP_DIR 77 const transcodeDirectory = CONFIG.STORAGE.TMP_DIR
77 const extname = '.mp4' 78 const extname = '.mp4'
78 79
@@ -82,11 +83,13 @@ async function transcodeNewWebTorrentResolution (video: MVideoWithFile, resoluti
82 const newVideoFile = new VideoFileModel({ 83 const newVideoFile = new VideoFileModel({
83 resolution, 84 resolution,
84 extname, 85 extname,
86 filename: generateVideoFilename(video, false, resolution, extname),
85 size: 0, 87 size: 0,
86 videoId: video.id 88 videoId: video.id
87 }) 89 })
90
88 const videoOutputPath = getVideoFilePath(video, newVideoFile) 91 const videoOutputPath = getVideoFilePath(video, newVideoFile)
89 const videoTranscodedPath = join(transcodeDirectory, getVideoFilename(video, newVideoFile)) 92 const videoTranscodedPath = join(transcodeDirectory, newVideoFile.filename)
90 93
91 const transcodeOptions = resolution === VideoResolution.H_NOVIDEO 94 const transcodeOptions = resolution === VideoResolution.H_NOVIDEO
92 ? { 95 ? {
@@ -122,7 +125,7 @@ async function transcodeNewWebTorrentResolution (video: MVideoWithFile, resoluti
122} 125}
123 126
124// Merge an image with an audio file to create a video 127// Merge an image with an audio file to create a video
125async function mergeAudioVideofile (video: MVideoWithAllFiles, resolution: VideoResolution, job: Job) { 128async function mergeAudioVideofile (video: MVideoFullLight, resolution: VideoResolution, job: Job) {
126 const transcodeDirectory = CONFIG.STORAGE.TMP_DIR 129 const transcodeDirectory = CONFIG.STORAGE.TMP_DIR
127 const newExtname = '.mp4' 130 const newExtname = '.mp4'
128 131
@@ -175,7 +178,7 @@ async function mergeAudioVideofile (video: MVideoWithAllFiles, resolution: Video
175 178
176// Concat TS segments from a live video to a fragmented mp4 HLS playlist 179// Concat TS segments from a live video to a fragmented mp4 HLS playlist
177async function generateHlsPlaylistResolutionFromTS (options: { 180async function generateHlsPlaylistResolutionFromTS (options: {
178 video: MVideoWithFile 181 video: MVideoFullLight
179 concatenatedTsFilePath: string 182 concatenatedTsFilePath: string
180 resolution: VideoResolution 183 resolution: VideoResolution
181 isPortraitMode: boolean 184 isPortraitMode: boolean
@@ -193,7 +196,7 @@ async function generateHlsPlaylistResolutionFromTS (options: {
193 196
194// Generate an HLS playlist from an input file, and update the master playlist 197// Generate an HLS playlist from an input file, and update the master playlist
195function generateHlsPlaylistResolution (options: { 198function generateHlsPlaylistResolution (options: {
196 video: MVideoWithFile 199 video: MVideoFullLight
197 videoInputPath: string 200 videoInputPath: string
198 resolution: VideoResolution 201 resolution: VideoResolution
199 copyCodecs: boolean 202 copyCodecs: boolean
@@ -235,7 +238,7 @@ export {
235// --------------------------------------------------------------------------- 238// ---------------------------------------------------------------------------
236 239
237async function onWebTorrentVideoFileTranscoding ( 240async function onWebTorrentVideoFileTranscoding (
238 video: MVideoWithFile, 241 video: MVideoFullLight,
239 videoFile: MVideoFile, 242 videoFile: MVideoFile,
240 transcodingPath: string, 243 transcodingPath: string,
241 outputPath: string 244 outputPath: string
@@ -250,7 +253,7 @@ async function onWebTorrentVideoFileTranscoding (
250 videoFile.fps = fps 253 videoFile.fps = fps
251 videoFile.metadata = metadata 254 videoFile.metadata = metadata
252 255
253 await createTorrentAndSetInfoHash(video, videoFile) 256 await createTorrentAndSetInfoHash(video, video, videoFile)
254 257
255 await VideoFileModel.customUpsert(videoFile, 'video', undefined) 258 await VideoFileModel.customUpsert(videoFile, 'video', undefined)
256 video.VideoFiles = await video.$get('VideoFiles') 259 video.VideoFiles = await video.$get('VideoFiles')
@@ -260,7 +263,7 @@ async function onWebTorrentVideoFileTranscoding (
260 263
261async function generateHlsPlaylistCommon (options: { 264async function generateHlsPlaylistCommon (options: {
262 type: 'hls' | 'hls-from-ts' 265 type: 'hls' | 'hls-from-ts'
263 video: MVideoWithFile 266 video: MVideoFullLight
264 inputPath: string 267 inputPath: string
265 resolution: VideoResolution 268 resolution: VideoResolution
266 copyCodecs?: boolean 269 copyCodecs?: boolean
@@ -318,10 +321,12 @@ async function generateHlsPlaylistCommon (options: {
318 videoStreamingPlaylist.Video = video 321 videoStreamingPlaylist.Video = video
319 322
320 // Build the new playlist file 323 // Build the new playlist file
324 const extname = extnameUtil(videoFilename)
321 const newVideoFile = new VideoFileModel({ 325 const newVideoFile = new VideoFileModel({
322 resolution, 326 resolution,
323 extname: extnameUtil(videoFilename), 327 extname,
324 size: 0, 328 size: 0,
329 filename: generateVideoFilename(video, true, resolution, extname),
325 fps: -1, 330 fps: -1,
326 videoStreamingPlaylistId: videoStreamingPlaylist.id 331 videoStreamingPlaylistId: videoStreamingPlaylist.id
327 }) 332 })
@@ -344,7 +349,7 @@ async function generateHlsPlaylistCommon (options: {
344 newVideoFile.fps = await getVideoFileFPS(videoFilePath) 349 newVideoFile.fps = await getVideoFileFPS(videoFilePath)
345 newVideoFile.metadata = await getMetadataFromFile(videoFilePath) 350 newVideoFile.metadata = await getMetadataFromFile(videoFilePath)
346 351
347 await createTorrentAndSetInfoHash(videoStreamingPlaylist, newVideoFile) 352 await createTorrentAndSetInfoHash(videoStreamingPlaylist, video, newVideoFile)
348 353
349 await VideoFileModel.customUpsert(newVideoFile, 'streaming-playlist', undefined) 354 await VideoFileModel.customUpsert(newVideoFile, 'streaming-playlist', undefined)
350 videoStreamingPlaylist.VideoFiles = await videoStreamingPlaylist.$get('VideoFiles') 355 videoStreamingPlaylist.VideoFiles = await videoStreamingPlaylist.$get('VideoFiles')