Add migration file
[oweals/peertube.git] / server / lib / video-transcoding.ts
1 import { CONFIG } from '../initializers'
2 import { extname, join } from 'path'
3 import { getVideoFileFPS, getVideoFileResolution, transcode } from '../helpers/ffmpeg-utils'
4 import { copy, remove, move, 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 optimizeVideofile (video: VideoModel, inputVideoFileArg?: VideoFileModel) {
11   const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR
12   const newExtname = '.mp4'
13
14   const inputVideoFile = inputVideoFileArg ? inputVideoFileArg : video.getOriginalFile()
15   const videoInputPath = join(videosDirectory, video.getVideoFilename(inputVideoFile))
16   const videoTranscodedPath = join(videosDirectory, video.id + '-transcoded' + newExtname)
17
18   const transcodeOptions = {
19     inputPath: videoInputPath,
20     outputPath: videoTranscodedPath
21   }
22
23   // Could be very long!
24   await transcode(transcodeOptions)
25
26   try {
27     await remove(videoInputPath)
28
29     // Important to do this before getVideoFilename() to take in account the new file extension
30     inputVideoFile.set('extname', newExtname)
31
32     const videoOutputPath = video.getVideoFilePath(inputVideoFile)
33     await move(videoTranscodedPath, videoOutputPath)
34     const stats = await stat(videoOutputPath)
35     const fps = await getVideoFileFPS(videoOutputPath)
36
37     inputVideoFile.set('size', stats.size)
38     inputVideoFile.set('fps', fps)
39
40     await video.createTorrentAndSetInfoHash(inputVideoFile)
41     await inputVideoFile.save()
42   } catch (err) {
43     // Auto destruction...
44     video.destroy().catch(err => logger.error('Cannot destruct video after transcoding failure.', { err }))
45
46     throw err
47   }
48 }
49
50 async function transcodeOriginalVideofile (video: VideoModel, resolution: VideoResolution, isPortraitMode: boolean) {
51   const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR
52   const extname = '.mp4'
53
54   // We are sure it's x264 in mp4 because optimizeOriginalVideofile was already executed
55   const videoInputPath = join(videosDirectory, video.getVideoFilename(video.getOriginalFile()))
56
57   const newVideoFile = new VideoFileModel({
58     resolution,
59     extname,
60     size: 0,
61     videoId: video.id
62   })
63   const videoOutputPath = join(videosDirectory, video.getVideoFilename(newVideoFile))
64
65   const transcodeOptions = {
66     inputPath: videoInputPath,
67     outputPath: videoOutputPath,
68     resolution,
69     isPortraitMode
70   }
71
72   await transcode(transcodeOptions)
73
74   const stats = await stat(videoOutputPath)
75   const fps = await getVideoFileFPS(videoOutputPath)
76
77   newVideoFile.set('size', stats.size)
78   newVideoFile.set('fps', fps)
79
80   await video.createTorrentAndSetInfoHash(newVideoFile)
81
82   await newVideoFile.save()
83
84   video.VideoFiles.push(newVideoFile)
85 }
86
87 async function importVideoFile (video: VideoModel, inputFilePath: string) {
88   const { videoFileResolution } = await getVideoFileResolution(inputFilePath)
89   const { size } = await stat(inputFilePath)
90   const fps = await getVideoFileFPS(inputFilePath)
91
92   let updatedVideoFile = new VideoFileModel({
93     resolution: videoFileResolution,
94     extname: extname(inputFilePath),
95     size,
96     fps,
97     videoId: video.id
98   })
99
100   const currentVideoFile = video.VideoFiles.find(videoFile => videoFile.resolution === updatedVideoFile.resolution)
101
102   if (currentVideoFile) {
103     // Remove old file and old torrent
104     await video.removeFile(currentVideoFile)
105     await video.removeTorrent(currentVideoFile)
106     // Remove the old video file from the array
107     video.VideoFiles = video.VideoFiles.filter(f => f !== currentVideoFile)
108
109     // Update the database
110     currentVideoFile.set('extname', updatedVideoFile.extname)
111     currentVideoFile.set('size', updatedVideoFile.size)
112     currentVideoFile.set('fps', updatedVideoFile.fps)
113
114     updatedVideoFile = currentVideoFile
115   }
116
117   const outputPath = video.getVideoFilePath(updatedVideoFile)
118   await copy(inputFilePath, outputPath)
119
120   await video.createTorrentAndSetInfoHash(updatedVideoFile)
121
122   await updatedVideoFile.save()
123
124   video.VideoFiles.push(updatedVideoFile)
125 }
126
127 export {
128   optimizeVideofile,
129   transcodeOriginalVideofile,
130   importVideoFile
131 }