bf3ff78c2ac233569a2815bf8cf6ed91a7dcfe04
[oweals/peertube.git] / server / lib / video-transcoding.ts
1 import { CONFIG } from '../initializers'
2 import { join, extname } from 'path'
3 import { getVideoFileFPS, getVideoFileResolution, transcode } from '../helpers/ffmpeg-utils'
4 import { copy, remove, rename, stat } from 'fs-extra'
5 import { logger } from '../helpers/logger'
6 import { VideoResolution } from '../../shared/models/videos'
7 import { VideoFileModel } from '../models/video/video-file'
8 import { VideoModel } from '../models/video/video'
9
10 async 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
49 async 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
86 async 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
126 export {
127   optimizeOriginalVideofile,
128   transcodeOriginalVideofile,
129   importVideoFile
130 }