aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/models/video/video.ts
diff options
context:
space:
mode:
authorChocobozzz <florian.bigard@gmail.com>2017-10-02 12:20:26 +0200
committerChocobozzz <florian.bigard@gmail.com>2017-10-03 15:31:26 +0200
commit40298b02546e8225dd21bf6048fe7f224aefc32a (patch)
tree0a0b981dbeb2af47810adff6553a0df995a03734 /server/models/video/video.ts
parentf0adb2701c1cf404ff63095f71e542bfe6d025ae (diff)
downloadPeerTube-40298b02546e8225dd21bf6048fe7f224aefc32a.tar.gz
PeerTube-40298b02546e8225dd21bf6048fe7f224aefc32a.tar.zst
PeerTube-40298b02546e8225dd21bf6048fe7f224aefc32a.zip
Implement video transcoding on server side
Diffstat (limited to 'server/models/video/video.ts')
-rw-r--r--server/models/video/video.ts105
1 files changed, 96 insertions, 9 deletions
diff --git a/server/models/video/video.ts b/server/models/video/video.ts
index e011c3b4d..28df91a7b 100644
--- a/server/models/video/video.ts
+++ b/server/models/video/video.ts
@@ -22,7 +22,8 @@ import {
22 unlinkPromise, 22 unlinkPromise,
23 renamePromise, 23 renamePromise,
24 writeFilePromise, 24 writeFilePromise,
25 createTorrentPromise 25 createTorrentPromise,
26 statPromise
26} from '../../helpers' 27} from '../../helpers'
27import { 28import {
28 CONFIG, 29 CONFIG,
@@ -35,7 +36,8 @@ import {
35 VIDEO_FILE_RESOLUTIONS 36 VIDEO_FILE_RESOLUTIONS
36} from '../../initializers' 37} from '../../initializers'
37import { removeVideoToFriends } from '../../lib' 38import { removeVideoToFriends } from '../../lib'
38import { VideoFileInstance } from './video-file-interface' 39import { VideoResolution } from '../../../shared'
40import { VideoFileInstance, VideoFileModel } from './video-file-interface'
39 41
40import { addMethodsToModel, getSort } from '../utils' 42import { addMethodsToModel, getSort } from '../utils'
41import { 43import {
@@ -46,6 +48,7 @@ import {
46} from './video-interface' 48} from './video-interface'
47 49
48let Video: Sequelize.Model<VideoInstance, VideoAttributes> 50let Video: Sequelize.Model<VideoInstance, VideoAttributes>
51let getOriginalFile: VideoMethods.GetOriginalFile
49let generateMagnetUri: VideoMethods.GenerateMagnetUri 52let generateMagnetUri: VideoMethods.GenerateMagnetUri
50let getVideoFilename: VideoMethods.GetVideoFilename 53let getVideoFilename: VideoMethods.GetVideoFilename
51let getThumbnailName: VideoMethods.GetThumbnailName 54let getThumbnailName: VideoMethods.GetThumbnailName
@@ -55,11 +58,13 @@ let isOwned: VideoMethods.IsOwned
55let toFormattedJSON: VideoMethods.ToFormattedJSON 58let toFormattedJSON: VideoMethods.ToFormattedJSON
56let toAddRemoteJSON: VideoMethods.ToAddRemoteJSON 59let toAddRemoteJSON: VideoMethods.ToAddRemoteJSON
57let toUpdateRemoteJSON: VideoMethods.ToUpdateRemoteJSON 60let toUpdateRemoteJSON: VideoMethods.ToUpdateRemoteJSON
58let transcodeVideofile: VideoMethods.TranscodeVideofile 61let optimizeOriginalVideofile: VideoMethods.OptimizeOriginalVideofile
62let transcodeOriginalVideofile: VideoMethods.TranscodeOriginalVideofile
59let createPreview: VideoMethods.CreatePreview 63let createPreview: VideoMethods.CreatePreview
60let createThumbnail: VideoMethods.CreateThumbnail 64let createThumbnail: VideoMethods.CreateThumbnail
61let getVideoFilePath: VideoMethods.GetVideoFilePath 65let getVideoFilePath: VideoMethods.GetVideoFilePath
62let createTorrentAndSetInfoHash: VideoMethods.CreateTorrentAndSetInfoHash 66let createTorrentAndSetInfoHash: VideoMethods.CreateTorrentAndSetInfoHash
67let getOriginalFileHeight: VideoMethods.GetOriginalFileHeight
63 68
64let generateThumbnailFromData: VideoMethods.GenerateThumbnailFromData 69let generateThumbnailFromData: VideoMethods.GenerateThumbnailFromData
65let getDurationFromFile: VideoMethods.GetDurationFromFile 70let getDurationFromFile: VideoMethods.GetDurationFromFile
@@ -251,6 +256,7 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da
251 getTorrentFileName, 256 getTorrentFileName,
252 getVideoFilename, 257 getVideoFilename,
253 getVideoFilePath, 258 getVideoFilePath,
259 getOriginalFile,
254 isOwned, 260 isOwned,
255 removeFile, 261 removeFile,
256 removePreview, 262 removePreview,
@@ -259,7 +265,9 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da
259 toAddRemoteJSON, 265 toAddRemoteJSON,
260 toFormattedJSON, 266 toFormattedJSON,
261 toUpdateRemoteJSON, 267 toUpdateRemoteJSON,
262 transcodeVideofile 268 optimizeOriginalVideofile,
269 transcodeOriginalVideofile,
270 getOriginalFileHeight
263 ] 271 ]
264 addMethodsToModel(Video, classMethods, instanceMethods) 272 addMethodsToModel(Video, classMethods, instanceMethods)
265 273
@@ -327,9 +335,14 @@ function afterDestroy (video: VideoInstance, options: { transaction: Sequelize.T
327 return Promise.all(tasks) 335 return Promise.all(tasks)
328} 336}
329 337
338getOriginalFile = function (this: VideoInstance) {
339 if (Array.isArray(this.VideoFiles) === false) return undefined
340
341 return this.VideoFiles.find(file => file.resolution === VideoResolution.ORIGINAL)
342}
343
330getVideoFilename = function (this: VideoInstance, videoFile: VideoFileInstance) { 344getVideoFilename = function (this: VideoInstance, videoFile: VideoFileInstance) {
331 // return this.uuid + '-' + VIDEO_FILE_RESOLUTIONS[videoFile.resolution] + videoFile.extname 345 return this.uuid + '-' + VIDEO_FILE_RESOLUTIONS[videoFile.resolution] + videoFile.extname
332 return this.uuid + videoFile.extname
333} 346}
334 347
335getThumbnailName = function (this: VideoInstance) { 348getThumbnailName = function (this: VideoInstance) {
@@ -345,8 +358,7 @@ getPreviewName = function (this: VideoInstance) {
345 358
346getTorrentFileName = function (this: VideoInstance, videoFile: VideoFileInstance) { 359getTorrentFileName = function (this: VideoInstance, videoFile: VideoFileInstance) {
347 const extension = '.torrent' 360 const extension = '.torrent'
348 // return this.uuid + '-' + VIDEO_FILE_RESOLUTIONS[videoFile.resolution] + extension 361 return this.uuid + '-' + VIDEO_FILE_RESOLUTIONS[videoFile.resolution] + extension
349 return this.uuid + extension
350} 362}
351 363
352isOwned = function (this: VideoInstance) { 364isOwned = function (this: VideoInstance) {
@@ -552,9 +564,10 @@ toUpdateRemoteJSON = function (this: VideoInstance) {
552 return json 564 return json
553} 565}
554 566
555transcodeVideofile = function (this: VideoInstance, inputVideoFile: VideoFileInstance) { 567optimizeOriginalVideofile = function (this: VideoInstance) {
556 const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR 568 const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR
557 const newExtname = '.mp4' 569 const newExtname = '.mp4'
570 const inputVideoFile = this.getOriginalFile()
558 const videoInputPath = join(videosDirectory, this.getVideoFilename(inputVideoFile)) 571 const videoInputPath = join(videosDirectory, this.getVideoFilename(inputVideoFile))
559 const videoOutputPath = join(videosDirectory, this.id + '-transcoded' + newExtname) 572 const videoOutputPath = join(videosDirectory, this.id + '-transcoded' + newExtname)
560 573
@@ -575,6 +588,12 @@ transcodeVideofile = function (this: VideoInstance, inputVideoFile: VideoFileIns
575 return renamePromise(videoOutputPath, this.getVideoFilePath(inputVideoFile)) 588 return renamePromise(videoOutputPath, this.getVideoFilePath(inputVideoFile))
576 }) 589 })
577 .then(() => { 590 .then(() => {
591 return statPromise(this.getVideoFilePath(inputVideoFile))
592 })
593 .then(stats => {
594 return inputVideoFile.set('size', stats.size)
595 })
596 .then(() => {
578 return this.createTorrentAndSetInfoHash(inputVideoFile) 597 return this.createTorrentAndSetInfoHash(inputVideoFile)
579 }) 598 })
580 .then(() => { 599 .then(() => {
@@ -594,6 +613,74 @@ transcodeVideofile = function (this: VideoInstance, inputVideoFile: VideoFileIns
594 }) 613 })
595} 614}
596 615
616transcodeOriginalVideofile = function (this: VideoInstance, resolution: VideoResolution) {
617 const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR
618 const extname = '.mp4'
619
620 // We are sure it's x264 in mp4 because optimizeOriginalVideofile was already executed
621 const videoInputPath = join(videosDirectory, this.getVideoFilename(this.getOriginalFile()))
622
623 const newVideoFile = (Video['sequelize'].models.VideoFile as VideoFileModel).build({
624 resolution,
625 extname,
626 size: 0,
627 videoId: this.id
628 })
629 const videoOutputPath = join(videosDirectory, this.getVideoFilename(newVideoFile))
630 const resolutionWidthSizes = {
631 1: '240x?',
632 2: '360x?',
633 3: '480x?',
634 4: '720x?',
635 5: '1080x?'
636 }
637
638 return new Promise<void>((res, rej) => {
639 ffmpeg(videoInputPath)
640 .output(videoOutputPath)
641 .videoCodec('libx264')
642 .size(resolutionWidthSizes[resolution])
643 .outputOption('-threads ' + CONFIG.TRANSCODING.THREADS)
644 .outputOption('-movflags faststart')
645 .on('error', rej)
646 .on('end', () => {
647 return statPromise(videoOutputPath)
648 .then(stats => {
649 newVideoFile.set('size', stats.size)
650
651 return undefined
652 })
653 .then(() => {
654 return this.createTorrentAndSetInfoHash(newVideoFile)
655 })
656 .then(() => {
657 return newVideoFile.save()
658 })
659 .then(() => {
660 return this.VideoFiles.push(newVideoFile)
661 })
662 .then(() => {
663 return res()
664 })
665 .catch(rej)
666 })
667 .run()
668 })
669}
670
671getOriginalFileHeight = function (this: VideoInstance) {
672 const originalFilePath = this.getVideoFilePath(this.getOriginalFile())
673
674 return new Promise<number>((res, rej) => {
675 ffmpeg.ffprobe(originalFilePath, (err, metadata) => {
676 if (err) return rej(err)
677
678 const videoStream = metadata.streams.find(s => s.codec_type === 'video')
679 return res(videoStream.height)
680 })
681 })
682}
683
597removeThumbnail = function (this: VideoInstance) { 684removeThumbnail = function (this: VideoInstance) {
598 const thumbnailPath = join(CONFIG.STORAGE.THUMBNAILS_DIR, this.getThumbnailName()) 685 const thumbnailPath = join(CONFIG.STORAGE.THUMBNAILS_DIR, this.getThumbnailName())
599 return unlinkPromise(thumbnailPath) 686 return unlinkPromise(thumbnailPath)