aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/lib
diff options
context:
space:
mode:
Diffstat (limited to 'server/lib')
-rw-r--r--server/lib/client-html.ts3
-rw-r--r--server/lib/job-queue/handlers/video-file.ts7
-rw-r--r--server/lib/video-transcoding.ts130
3 files changed, 136 insertions, 4 deletions
diff --git a/server/lib/client-html.ts b/server/lib/client-html.ts
index a69e09c32..b1088c096 100644
--- a/server/lib/client-html.ts
+++ b/server/lib/client-html.ts
@@ -8,6 +8,7 @@ import { VideoModel } from '../models/video/video'
8import * as validator from 'validator' 8import * as validator from 'validator'
9import { VideoPrivacy } from '../../shared/models/videos' 9import { VideoPrivacy } from '../../shared/models/videos'
10import { readFile } from 'fs-extra' 10import { readFile } from 'fs-extra'
11import { getActivityStreamDuration } from '../models/video/video-format-utils'
11 12
12export class ClientHtml { 13export class ClientHtml {
13 14
@@ -150,7 +151,7 @@ export class ClientHtml {
150 description: videoDescriptionEscaped, 151 description: videoDescriptionEscaped,
151 thumbnailUrl: previewUrl, 152 thumbnailUrl: previewUrl,
152 uploadDate: video.createdAt.toISOString(), 153 uploadDate: video.createdAt.toISOString(),
153 duration: video.getActivityStreamDuration(), 154 duration: getActivityStreamDuration(video.duration),
154 contentUrl: videoUrl, 155 contentUrl: videoUrl,
155 embedUrl: embedUrl, 156 embedUrl: embedUrl,
156 interactionCount: video.views 157 interactionCount: video.views
diff --git a/server/lib/job-queue/handlers/video-file.ts b/server/lib/job-queue/handlers/video-file.ts
index c6308f7a6..2c9ca8e12 100644
--- a/server/lib/job-queue/handlers/video-file.ts
+++ b/server/lib/job-queue/handlers/video-file.ts
@@ -8,6 +8,7 @@ import { retryTransactionWrapper } from '../../../helpers/database-utils'
8import { sequelizeTypescript } from '../../../initializers' 8import { sequelizeTypescript } from '../../../initializers'
9import * as Bluebird from 'bluebird' 9import * as Bluebird from 'bluebird'
10import { computeResolutionsToTranscode } from '../../../helpers/ffmpeg-utils' 10import { computeResolutionsToTranscode } from '../../../helpers/ffmpeg-utils'
11import { importVideoFile, transcodeOriginalVideofile, optimizeOriginalVideofile } from '../../video-transcoding'
11 12
12export type VideoFilePayload = { 13export type VideoFilePayload = {
13 videoUUID: string 14 videoUUID: string
@@ -32,7 +33,7 @@ async function processVideoFileImport (job: Bull.Job) {
32 return undefined 33 return undefined
33 } 34 }
34 35
35 await video.importVideoFile(payload.filePath) 36 await importVideoFile(video, payload.filePath)
36 37
37 await onVideoFileTranscoderOrImportSuccess(video) 38 await onVideoFileTranscoderOrImportSuccess(video)
38 return video 39 return video
@@ -51,11 +52,11 @@ async function processVideoFile (job: Bull.Job) {
51 52
52 // Transcoding in other resolution 53 // Transcoding in other resolution
53 if (payload.resolution) { 54 if (payload.resolution) {
54 await video.transcodeOriginalVideofile(payload.resolution, payload.isPortraitMode || false) 55 await transcodeOriginalVideofile(video, payload.resolution, payload.isPortraitMode || false)
55 56
56 await retryTransactionWrapper(onVideoFileTranscoderOrImportSuccess, video) 57 await retryTransactionWrapper(onVideoFileTranscoderOrImportSuccess, video)
57 } else { 58 } else {
58 await video.optimizeOriginalVideofile() 59 await optimizeOriginalVideofile(video)
59 60
60 await retryTransactionWrapper(onVideoFileOptimizerSuccess, video, payload.isNewVideo) 61 await retryTransactionWrapper(onVideoFileOptimizerSuccess, video, payload.isNewVideo)
61 } 62 }
diff --git a/server/lib/video-transcoding.ts b/server/lib/video-transcoding.ts
new file mode 100644
index 000000000..bf3ff78c2
--- /dev/null
+++ b/server/lib/video-transcoding.ts
@@ -0,0 +1,130 @@
1import { CONFIG } from '../initializers'
2import { join, extname } from 'path'
3import { getVideoFileFPS, getVideoFileResolution, transcode } from '../helpers/ffmpeg-utils'
4import { copy, remove, rename, stat } from 'fs-extra'
5import { logger } from '../helpers/logger'
6import { VideoResolution } from '../../shared/models/videos'
7import { VideoFileModel } from '../models/video/video-file'
8import { VideoModel } from '../models/video/video'
9
10async function optimizeOriginalVideofile (video: VideoModel) {
11 const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR
12 const newExtname = '.mp4'
13 const inputVideoFile = video.getOriginalFile()
14 const videoInputPath = join(videosDirectory, video.getVideoFilename(inputVideoFile))
15 const videoTranscodedPath = join(videosDirectory, video.id + '-transcoded' + newExtname)
16
17 const transcodeOptions = {
18 inputPath: videoInputPath,
19 outputPath: videoTranscodedPath
20 }
21
22 // Could be very long!
23 await transcode(transcodeOptions)
24
25 try {
26 await remove(videoInputPath)
27
28 // Important to do this before getVideoFilename() to take in account the new file extension
29 inputVideoFile.set('extname', newExtname)
30
31 const videoOutputPath = video.getVideoFilePath(inputVideoFile)
32 await rename(videoTranscodedPath, videoOutputPath)
33 const stats = await stat(videoOutputPath)
34 const fps = await getVideoFileFPS(videoOutputPath)
35
36 inputVideoFile.set('size', stats.size)
37 inputVideoFile.set('fps', fps)
38
39 await video.createTorrentAndSetInfoHash(inputVideoFile)
40 await inputVideoFile.save()
41 } catch (err) {
42 // Auto destruction...
43 video.destroy().catch(err => logger.error('Cannot destruct video after transcoding failure.', { err }))
44
45 throw err
46 }
47}
48
49async function transcodeOriginalVideofile (video: VideoModel, resolution: VideoResolution, isPortraitMode: boolean) {
50 const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR
51 const extname = '.mp4'
52
53 // We are sure it's x264 in mp4 because optimizeOriginalVideofile was already executed
54 const videoInputPath = join(videosDirectory, video.getVideoFilename(video.getOriginalFile()))
55
56 const newVideoFile = new VideoFileModel({
57 resolution,
58 extname,
59 size: 0,
60 videoId: video.id
61 })
62 const videoOutputPath = join(videosDirectory, video.getVideoFilename(newVideoFile))
63
64 const transcodeOptions = {
65 inputPath: videoInputPath,
66 outputPath: videoOutputPath,
67 resolution,
68 isPortraitMode
69 }
70
71 await transcode(transcodeOptions)
72
73 const stats = await stat(videoOutputPath)
74 const fps = await getVideoFileFPS(videoOutputPath)
75
76 newVideoFile.set('size', stats.size)
77 newVideoFile.set('fps', fps)
78
79 await video.createTorrentAndSetInfoHash(newVideoFile)
80
81 await newVideoFile.save()
82
83 video.VideoFiles.push(newVideoFile)
84}
85
86async function importVideoFile (video: VideoModel, inputFilePath: string) {
87 const { videoFileResolution } = await getVideoFileResolution(inputFilePath)
88 const { size } = await stat(inputFilePath)
89 const fps = await getVideoFileFPS(inputFilePath)
90
91 let updatedVideoFile = new VideoFileModel({
92 resolution: videoFileResolution,
93 extname: extname(inputFilePath),
94 size,
95 fps,
96 videoId: video.id
97 })
98
99 const currentVideoFile = video.VideoFiles.find(videoFile => videoFile.resolution === updatedVideoFile.resolution)
100
101 if (currentVideoFile) {
102 // Remove old file and old torrent
103 await video.removeFile(currentVideoFile)
104 await video.removeTorrent(currentVideoFile)
105 // Remove the old video file from the array
106 video.VideoFiles = video.VideoFiles.filter(f => f !== currentVideoFile)
107
108 // Update the database
109 currentVideoFile.set('extname', updatedVideoFile.extname)
110 currentVideoFile.set('size', updatedVideoFile.size)
111 currentVideoFile.set('fps', updatedVideoFile.fps)
112
113 updatedVideoFile = currentVideoFile
114 }
115
116 const outputPath = video.getVideoFilePath(updatedVideoFile)
117 await copy(inputFilePath, outputPath)
118
119 await video.createTorrentAndSetInfoHash(updatedVideoFile)
120
121 await updatedVideoFile.save()
122
123 video.VideoFiles.push(updatedVideoFile)
124}
125
126export {
127 optimizeOriginalVideofile,
128 transcodeOriginalVideofile,
129 importVideoFile
130}