Change how we handle resolution
authorChocobozzz <florian.bigard@gmail.com>
Mon, 9 Oct 2017 09:06:13 +0000 (11:06 +0200)
committerChocobozzz <florian.bigard@gmail.com>
Mon, 9 Oct 2017 09:17:36 +0000 (11:17 +0200)
It was an enum before, now we just use video height

18 files changed:
config/production.yaml.example
server/controllers/api/videos/index.ts
server/helpers/custom-validators/videos.ts
server/helpers/ffmpeg-utils.ts [new file with mode: 0644]
server/helpers/index.ts
server/helpers/utils.ts
server/initializers/constants.ts
server/initializers/migrations/0075-video-resolutions.ts
server/middlewares/validators/videos.ts
server/models/user/user.ts
server/models/video/video-interface.ts
server/models/video/video.ts
server/tests/api/multiple-pods.ts
server/tests/api/single-pod.ts
server/tests/api/video-transcoder.ts
server/tests/cli/update-host.ts
server/tests/utils/videos.ts
shared/models/videos/video-resolution.enum.ts

index 987da12cce519d3c5226e5b0e17d749525ba3876..f99c8927ecaf4b5c6c5f3310108447117ef105b0 100644 (file)
@@ -46,3 +46,9 @@ user:
 transcoding:
   enabled: false
   threads: 2
+  resolutions: # Only created if the original video has a higher resolution
+    240p: true
+    360p: true
+    480p: true
+    720p: true
+    1080p: true
index 14c969ec3de8497f56b5408b42fb20eab3a356a1..2b7ead95438ed37842c8eddf178249c0fa4e9597 100644 (file)
@@ -37,10 +37,11 @@ import {
   retryTransactionWrapper,
   generateRandomString,
   getFormattedObjects,
-  renamePromise
+  renamePromise,
+  getVideoFileHeight
 } from '../../../helpers'
 import { TagInstance, VideoInstance } from '../../../models'
-import { VideoCreate, VideoUpdate, VideoResolution } from '../../../../shared'
+import { VideoCreate, VideoUpdate } from '../../../../shared'
 
 import { abuseVideoRouter } from './abuse'
 import { blacklistRouter } from './blacklist'
@@ -192,9 +193,14 @@ function addVideo (req: express.Request, res: express.Response, videoPhysicalFil
         return { author, tagInstances, video }
       })
       .then(({ author, tagInstances, video }) => {
+        const videoFilePath = join(CONFIG.STORAGE.VIDEOS_DIR, videoPhysicalFile.filename)
+        return getVideoFileHeight(videoFilePath)
+          .then(height => ({ author, tagInstances, video, videoFileHeight: height }))
+      })
+      .then(({ author, tagInstances, video, videoFileHeight }) => {
         const videoFileData = {
           extname: extname(videoPhysicalFile.filename),
-          resolution: VideoResolution.ORIGINAL,
+          resolution: videoFileHeight,
           size: videoPhysicalFile.size
         }
 
index 2eb021ae7ba4e096ee0a0fcc60c57f9275b9f6f0..a31aca0197d33c7ad21fbec6541ca67ad3253495 100644 (file)
@@ -8,8 +8,7 @@ import {
   VIDEO_CATEGORIES,
   VIDEO_LICENCES,
   VIDEO_LANGUAGES,
-  VIDEO_RATE_TYPES,
-  VIDEO_FILE_RESOLUTIONS
+  VIDEO_RATE_TYPES
 } from '../../initializers'
 import { isUserUsernameValid } from './users'
 import { isArray, exists } from './misc'
@@ -128,7 +127,7 @@ function isVideoFileSizeValid (value: string) {
 }
 
 function isVideoFileResolutionValid (value: string) {
-  return VIDEO_FILE_RESOLUTIONS[value] !== undefined
+  return exists(value) && validator.isInt(value + '')
 }
 
 function isVideoFileExtnameValid (value: string) {
diff --git a/server/helpers/ffmpeg-utils.ts b/server/helpers/ffmpeg-utils.ts
new file mode 100644 (file)
index 0000000..c35125e
--- /dev/null
@@ -0,0 +1,79 @@
+import * as Promise from 'bluebird'
+import * as ffmpeg from 'fluent-ffmpeg'
+
+import { CONFIG } from '../initializers'
+import { VideoResolution } from '../../shared/models/videos/video-resolution.enum'
+
+function getVideoFileHeight (path: string) {
+  return new Promise<number>((res, rej) => {
+    ffmpeg.ffprobe(path, (err, metadata) => {
+      if (err) return rej(err)
+
+      const videoStream = metadata.streams.find(s => s.codec_type === 'video')
+      return res(videoStream.height)
+    })
+  })
+}
+
+function getDurationFromVideoFile (path: string) {
+  return new Promise<number>((res, rej) => {
+    ffmpeg.ffprobe(path, (err, metadata) => {
+      if (err) return rej(err)
+
+      return res(Math.floor(metadata.format.duration))
+    })
+  })
+}
+
+function generateImageFromVideoFile (fromPath: string, folder: string, imageName: string, size?: string) {
+  const options = {
+    filename: imageName,
+    count: 1,
+    folder
+  }
+
+  if (size !== undefined) {
+    options['size'] = size
+  }
+
+  return new Promise<string>((res, rej) => {
+    ffmpeg(fromPath)
+      .on('error', rej)
+      .on('end', () => res(imageName))
+      .thumbnail(options)
+  })
+}
+
+type TranscodeOptions = {
+  inputPath: string
+  outputPath: string
+  resolution?: VideoResolution
+}
+
+function transcode (options: TranscodeOptions) {
+  return new Promise<void>((res, rej) => {
+    let command = ffmpeg(options.inputPath)
+                    .output(options.outputPath)
+                    .videoCodec('libx264')
+                    .outputOption('-threads ' + CONFIG.TRANSCODING.THREADS)
+                    .outputOption('-movflags faststart')
+
+    if (options.resolution !== undefined) {
+      const size = `${options.resolution}x?` // '720x?' for example
+      command = command.size(size)
+    }
+
+    command.on('error', rej)
+           .on('end', res)
+           .run()
+  })
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+  getVideoFileHeight,
+  getDurationFromVideoFile,
+  generateImageFromVideoFile,
+  transcode
+}
index 78215fe1002fef43ae148db16933b7bc9bc26e14..846bd796f5ee9172e434ead2a09bc6156a101f8c 100644 (file)
@@ -1,6 +1,7 @@
 export * from './core-utils'
 export * from './logger'
 export * from './custom-validators'
+export * from './ffmpeg-utils'
 export * from './database-utils'
 export * from './peertube-crypto'
 export * from './requests'
index b74442ab0b1dae708ea96bc3fb2317cce1832fee..3317dddc33de5cfea04bcbc2081004b33e9f55bc 100644 (file)
@@ -61,7 +61,7 @@ function computeResolutionsToTranscode (videoFileHeight: number) {
   ]
 
   for (const resolution of resolutions) {
-    if (configResolutions[resolution.toString()] === true && videoFileHeight >= resolution) {
+    if (configResolutions[resolution.toString()] === true && videoFileHeight > resolution) {
       resolutionsEnabled.push(resolution)
     }
   }
index f87041a3ffe127e9ffbfda527f4fe9b2669a4e21..b11575b346ebbb15c845403ba15c4980ed0951db 100644 (file)
@@ -189,16 +189,6 @@ const VIDEO_LANGUAGES = {
   14: 'Italian'
 }
 
-// TODO: use VideoResolution when https://github.com/Microsoft/TypeScript/issues/13042 is fixed
-const VIDEO_FILE_RESOLUTIONS: { [ id: number ]: string } = {
-  0: 'original',
-  240: '240p',
-  360: '360p',
-  480: '480p',
-  720: '720p',
-  1080: '1080p'
-}
-
 // ---------------------------------------------------------------------------
 
 // Score a pod has when we create it as a friend
@@ -385,7 +375,6 @@ export {
   THUMBNAILS_SIZE,
   USER_ROLES,
   VIDEO_CATEGORIES,
-  VIDEO_FILE_RESOLUTIONS,
   VIDEO_LANGUAGES,
   VIDEO_LICENCES,
   VIDEO_RATE_TYPES
index 6bc1e72abf7dd63f5b751a777c95fefff1e693b8..e1d9fdacbd27579f2e7a72421ddb859de6ee211a 100644 (file)
@@ -4,6 +4,7 @@ import { join } from 'path'
 
 import { readdirPromise, renamePromise } from '../../helpers/core-utils'
 import { CONFIG } from '../../initializers/constants'
+import { getVideoFileHeight } from '../../helpers/ffmpeg-utils'
 
 function up (utils: {
   transaction: Sequelize.Transaction,
@@ -14,26 +15,7 @@ function up (utils: {
   const torrentDir = CONFIG.STORAGE.TORRENTS_DIR
   const videoFileDir = CONFIG.STORAGE.VIDEOS_DIR
 
-  return readdirPromise(torrentDir)
-    .then(torrentFiles => {
-      const tasks: Promise<any>[] = []
-      for (const torrentFile of torrentFiles) {
-        const matches = /^([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\.torrent/.exec(torrentFile)
-        if (matches === null) {
-          console.log('Invalid torrent file name %s.', torrentFile)
-          continue
-        }
-
-        const newTorrentName = matches[1] + '-original.torrent'
-        const p = renamePromise(join(torrentDir, torrentFile), join(torrentDir, newTorrentName))
-        tasks.push(p)
-      }
-
-      return Promise.all(tasks)
-    })
-    .then(() => {
-      return readdirPromise(videoFileDir)
-    })
+  return readdirPromise(videoFileDir)
     .then(videoFiles => {
       const tasks: Promise<any>[] = []
       for (const videoFile of videoFiles) {
@@ -43,8 +25,25 @@ function up (utils: {
           continue
         }
 
-        const newVideoFileName = matches[1] + '-original.' + matches[2]
-        const p = renamePromise(join(videoFileDir, videoFile), join(videoFileDir, newVideoFileName))
+        const uuid = matches[1]
+        const ext = matches[2]
+
+        const p = getVideoFileHeight(join(videoFileDir, videoFile))
+          .then(height => {
+            const oldTorrentName = uuid + '.torrent'
+            const newTorrentName = uuid + '-' + height + '.torrent'
+            return renamePromise(join(torrentDir, oldTorrentName), join(torrentDir, newTorrentName)).then(() => height)
+          })
+          .then(height => {
+            const newVideoFileName = uuid + '-' + height + '.' + ext
+            return renamePromise(join(videoFileDir, videoFile), join(videoFileDir, newVideoFileName)).then(() => height)
+          })
+          .then(height => {
+            const query = 'UPDATE "VideoFiles" SET "resolution" = ' + height +
+                          ' WHERE "videoId" = (SELECT "id" FROM "Videos" WHERE "uuid" = \'' + uuid + '\')'
+            return utils.sequelize.query(query)
+          })
+
         tasks.push(p)
       }
 
index bc8b7e5413b2c515a401653da76772a127dacf45..5f213f974d7cb38ca1d50959991983e237b3d6d3 100644 (file)
@@ -19,7 +19,8 @@ import {
   isVideoNSFWValid,
   isVideoIdOrUUIDValid,
   isVideoAbuseReasonValid,
-  isVideoRatingTypeValid
+  isVideoRatingTypeValid,
+  getDurationFromVideoFile
 } from '../../helpers'
 import { VideoInstance } from '../../models'
 
@@ -50,7 +51,7 @@ const videosAddValidator = [
             return undefined
           }
 
-          return db.Video.getDurationFromFile(videoFile.path)
+          return getDurationFromVideoFile(videoFile.path)
             .catch(err => {
               logger.error('Invalid input file in videosAddValidator.', err)
               res.status(400)
index 7a21dbefaffb80947d52d4df4606cd9ebd137c41..0dc52d3cfdaa3d19c2e0397b098111719e205a3c 100644 (file)
@@ -12,7 +12,6 @@ import {
   isUserDisplayNSFWValid,
   isUserVideoQuotaValid
 } from '../../helpers'
-import { VideoResolution } from '../../../shared'
 
 import { addMethodsToModel } from '../utils'
 import {
@@ -243,33 +242,21 @@ loadByUsernameOrEmail = function (username: string, email: string) {
 // ---------------------------------------------------------------------------
 
 function getOriginalVideoFileTotalFromUser (user: UserInstance) {
-  // attributes = [] because we don't want other fields than the sum
-  const query = {
-    where: {
-      resolution: VideoResolution.ORIGINAL
-    },
-    include: [
-      {
-        attributes: [],
-        model: User['sequelize'].models.Video,
-        include: [
-          {
-            attributes: [],
-            model: User['sequelize'].models.Author,
-            include: [
-              {
-                attributes: [],
-                model: User['sequelize'].models.User,
-                where: {
-                  id: user.id
-                }
-              }
-            ]
-          }
-        ]
-      }
-    ]
+  // Don't use sequelize because we need to use a subquery
+  const query = 'SELECT SUM("size") AS "total" FROM ' +
+                '(SELECT MAX("VideoFiles"."size") AS "size" FROM "VideoFiles" ' +
+                'INNER JOIN "Videos" ON "VideoFiles"."videoId" = "Videos"."id" ' +
+                'INNER JOIN "Authors" ON "Videos"."authorId" = "Authors"."id" ' +
+                'INNER JOIN "Users" ON "Authors"."userId" = "Users"."id" ' +
+                'WHERE "Users"."id" = $userId GROUP BY "Videos"."id") t'
+
+  const options = {
+    bind: { userId: user.id },
+    type: Sequelize.QueryTypes.SELECT
   }
+  return User['sequelize'].query(query, options).then(([ { total } ]) => {
+    if (total === null) return 0
 
-  return User['sequelize'].models.VideoFile.sum('size', query)
+    return parseInt(total, 10)
+  })
 }
index 340426f456fa14a1543a23d2fc78eef431504e49..6a3db4f3efcf5957055054a81842a88a1c276b87 100644 (file)
@@ -35,7 +35,6 @@ export namespace VideoMethods {
 
   // Return thumbnail name
   export type GenerateThumbnailFromData = (video: VideoInstance, thumbnailData: string) => Promise<string>
-  export type GetDurationFromFile = (videoPath: string) => Promise<number>
 
   export type List = () => Promise<VideoInstance[]>
   export type ListOwnedAndPopulateAuthorAndTags = () => Promise<VideoInstance[]>
@@ -65,7 +64,6 @@ export namespace VideoMethods {
 
 export interface VideoClass {
   generateThumbnailFromData: VideoMethods.GenerateThumbnailFromData
-  getDurationFromFile: VideoMethods.GetDurationFromFile
   list: VideoMethods.List
   listForApi: VideoMethods.ListForApi
   listOwnedAndPopulateAuthorAndTags: VideoMethods.ListOwnedAndPopulateAuthorAndTags
index c376d769ed989ca7bdb56d77fafe672afd661085..2ba6cf25f0cc4daa8b137f0c6e8045a506633f64 100644 (file)
@@ -1,12 +1,12 @@
 import * as safeBuffer from 'safe-buffer'
 const Buffer = safeBuffer.Buffer
-import * as ffmpeg from 'fluent-ffmpeg'
 import * as magnetUtil from 'magnet-uri'
 import { map } from 'lodash'
 import * as parseTorrent from 'parse-torrent'
 import { join } from 'path'
 import * as Sequelize from 'sequelize'
 import * as Promise from 'bluebird'
+import { maxBy } from 'lodash'
 
 import { TagInstance } from './tag-interface'
 import {
@@ -23,7 +23,10 @@ import {
   renamePromise,
   writeFilePromise,
   createTorrentPromise,
-  statPromise
+  statPromise,
+  generateImageFromVideoFile,
+  transcode,
+  getVideoFileHeight
 } from '../../helpers'
 import {
   CONFIG,
@@ -32,8 +35,7 @@ import {
   VIDEO_CATEGORIES,
   VIDEO_LICENCES,
   VIDEO_LANGUAGES,
-  THUMBNAILS_SIZE,
-  VIDEO_FILE_RESOLUTIONS
+  THUMBNAILS_SIZE
 } from '../../initializers'
 import { removeVideoToFriends } from '../../lib'
 import { VideoResolution } from '../../../shared'
@@ -67,7 +69,6 @@ let createTorrentAndSetInfoHash: VideoMethods.CreateTorrentAndSetInfoHash
 let getOriginalFileHeight: VideoMethods.GetOriginalFileHeight
 
 let generateThumbnailFromData: VideoMethods.GenerateThumbnailFromData
-let getDurationFromFile: VideoMethods.GetDurationFromFile
 let list: VideoMethods.List
 let listForApi: VideoMethods.ListForApi
 let loadByHostAndUUID: VideoMethods.LoadByHostAndUUID
@@ -233,7 +234,6 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da
     associate,
 
     generateThumbnailFromData,
-    getDurationFromFile,
     list,
     listForApi,
     listOwnedAndPopulateAuthorAndTags,
@@ -338,11 +338,12 @@ function afterDestroy (video: VideoInstance, options: { transaction: Sequelize.T
 getOriginalFile = function (this: VideoInstance) {
   if (Array.isArray(this.VideoFiles) === false) return undefined
 
-  return this.VideoFiles.find(file => file.resolution === VideoResolution.ORIGINAL)
+  // The original file is the file that have the higher resolution
+  return maxBy(this.VideoFiles, file => file.resolution)
 }
 
 getVideoFilename = function (this: VideoInstance, videoFile: VideoFileInstance) {
-  return this.uuid + '-' + VIDEO_FILE_RESOLUTIONS[videoFile.resolution] + videoFile.extname
+  return this.uuid + '-' + videoFile.resolution + videoFile.extname
 }
 
 getThumbnailName = function (this: VideoInstance) {
@@ -358,7 +359,7 @@ getPreviewName = function (this: VideoInstance) {
 
 getTorrentFileName = function (this: VideoInstance, videoFile: VideoFileInstance) {
   const extension = '.torrent'
-  return this.uuid + '-' + VIDEO_FILE_RESOLUTIONS[videoFile.resolution] + extension
+  return this.uuid + '-' + videoFile.resolution + extension
 }
 
 isOwned = function (this: VideoInstance) {
@@ -366,11 +367,20 @@ isOwned = function (this: VideoInstance) {
 }
 
 createPreview = function (this: VideoInstance, videoFile: VideoFileInstance) {
-  return generateImage(this, this.getVideoFilePath(videoFile), CONFIG.STORAGE.PREVIEWS_DIR, this.getPreviewName(), null)
+  return generateImageFromVideoFile(
+    this.getVideoFilePath(videoFile),
+    CONFIG.STORAGE.PREVIEWS_DIR,
+    this.getPreviewName()
+  )
 }
 
 createThumbnail = function (this: VideoInstance, videoFile: VideoFileInstance) {
-  return generateImage(this, this.getVideoFilePath(videoFile), CONFIG.STORAGE.THUMBNAILS_DIR, this.getThumbnailName(), THUMBNAILS_SIZE)
+  return generateImageFromVideoFile(
+    this.getVideoFilePath(videoFile),
+    CONFIG.STORAGE.THUMBNAILS_DIR,
+    this.getThumbnailName(),
+    THUMBNAILS_SIZE
+  )
 }
 
 getVideoFilePath = function (this: VideoInstance, videoFile: VideoFileInstance) {
@@ -480,8 +490,7 @@ toFormattedJSON = function (this: VideoInstance) {
   // Format and sort video files
   json.files = this.VideoFiles
                    .map(videoFile => {
-                     let resolutionLabel = VIDEO_FILE_RESOLUTIONS[videoFile.resolution]
-                     if (!resolutionLabel) resolutionLabel = 'Unknown'
+                     let resolutionLabel = videoFile.resolution + 'p'
 
                      const videoFileJson = {
                        resolution: videoFile.resolution,
@@ -578,46 +587,42 @@ optimizeOriginalVideofile = function (this: VideoInstance) {
   const videoInputPath = join(videosDirectory, this.getVideoFilename(inputVideoFile))
   const videoOutputPath = join(videosDirectory, this.id + '-transcoded' + newExtname)
 
-  return new Promise<void>((res, rej) => {
-    ffmpeg(videoInputPath)
-      .output(videoOutputPath)
-      .videoCodec('libx264')
-      .outputOption('-threads ' + CONFIG.TRANSCODING.THREADS)
-      .outputOption('-movflags faststart')
-      .on('error', rej)
-      .on('end', () => {
-
-        return unlinkPromise(videoInputPath)
-          .then(() => {
-            // Important to do this before getVideoFilename() to take in account the new file extension
-            inputVideoFile.set('extname', newExtname)
-
-            return renamePromise(videoOutputPath, this.getVideoFilePath(inputVideoFile))
-          })
-          .then(() => {
-            return statPromise(this.getVideoFilePath(inputVideoFile))
-          })
-          .then(stats => {
-            return inputVideoFile.set('size', stats.size)
-          })
-          .then(() => {
-            return this.createTorrentAndSetInfoHash(inputVideoFile)
-          })
-          .then(() => {
-            return inputVideoFile.save()
-          })
-          .then(() => {
-            return res()
-          })
-          .catch(err => {
-            // Auto destruction...
-            this.destroy().catch(err => logger.error('Cannot destruct video after transcoding failure.', err))
-
-            return rej(err)
-          })
-      })
-      .run()
-  })
+  const transcodeOptions = {
+    inputPath: videoInputPath,
+    outputPath: videoOutputPath
+  }
+
+  return transcode(transcodeOptions)
+    .then(() => {
+      return unlinkPromise(videoInputPath)
+    })
+    .then(() => {
+      // Important to do this before getVideoFilename() to take in account the new file extension
+      inputVideoFile.set('extname', newExtname)
+
+      return renamePromise(videoOutputPath, this.getVideoFilePath(inputVideoFile))
+    })
+    .then(() => {
+      return statPromise(this.getVideoFilePath(inputVideoFile))
+    })
+    .then(stats => {
+      return inputVideoFile.set('size', stats.size)
+    })
+    .then(() => {
+      return this.createTorrentAndSetInfoHash(inputVideoFile)
+    })
+    .then(() => {
+      return inputVideoFile.save()
+    })
+    .then(() => {
+      return undefined
+    })
+    .catch(err => {
+      // Auto destruction...
+      this.destroy().catch(err => logger.error('Cannot destruct video after transcoding failure.', err))
+
+      throw err
+    })
 }
 
 transcodeOriginalVideofile = function (this: VideoInstance, resolution: VideoResolution) {
@@ -634,52 +639,37 @@ transcodeOriginalVideofile = function (this: VideoInstance, resolution: VideoRes
     videoId: this.id
   })
   const videoOutputPath = join(videosDirectory, this.getVideoFilename(newVideoFile))
-  const resolutionOption = `${resolution}x?` // '720x?' for example
-
-  return new Promise<void>((res, rej) => {
-    ffmpeg(videoInputPath)
-      .output(videoOutputPath)
-      .videoCodec('libx264')
-      .size(resolutionOption)
-      .outputOption('-threads ' + CONFIG.TRANSCODING.THREADS)
-      .outputOption('-movflags faststart')
-      .on('error', rej)
-      .on('end', () => {
-        return statPromise(videoOutputPath)
-          .then(stats => {
-            newVideoFile.set('size', stats.size)
-
-            return undefined
-          })
-          .then(() => {
-            return this.createTorrentAndSetInfoHash(newVideoFile)
-          })
-          .then(() => {
-            return newVideoFile.save()
-          })
-          .then(() => {
-            return this.VideoFiles.push(newVideoFile)
-          })
-          .then(() => {
-            return res()
-          })
-          .catch(rej)
-      })
-      .run()
-  })
+
+  const transcodeOptions = {
+    inputPath: videoInputPath,
+    outputPath: videoOutputPath,
+    resolution
+  }
+  return transcode(transcodeOptions)
+    .then(() => {
+      return statPromise(videoOutputPath)
+    })
+    .then(stats => {
+      newVideoFile.set('size', stats.size)
+
+      return undefined
+    })
+    .then(() => {
+      return this.createTorrentAndSetInfoHash(newVideoFile)
+    })
+    .then(() => {
+      return newVideoFile.save()
+    })
+    .then(() => {
+      return this.VideoFiles.push(newVideoFile)
+    })
+    .then(() => undefined)
 }
 
 getOriginalFileHeight = function (this: VideoInstance) {
   const originalFilePath = this.getVideoFilePath(this.getOriginalFile())
 
-  return new Promise<number>((res, rej) => {
-    ffmpeg.ffprobe(originalFilePath, (err, metadata) => {
-      if (err) return rej(err)
-
-      const videoStream = metadata.streams.find(s => s.codec_type === 'video')
-      return res(videoStream.height)
-    })
-  })
+  return getVideoFileHeight(originalFilePath)
 }
 
 removeThumbnail = function (this: VideoInstance) {
@@ -714,16 +704,6 @@ generateThumbnailFromData = function (video: VideoInstance, thumbnailData: strin
   })
 }
 
-getDurationFromFile = function (videoPath: string) {
-  return new Promise<number>((res, rej) => {
-    ffmpeg.ffprobe(videoPath, (err, metadata) => {
-      if (err) return rej(err)
-
-      return res(Math.floor(metadata.format.duration))
-    })
-  })
-}
-
 list = function () {
   const query = {
     include: [ Video['sequelize'].models.VideoFile ]
@@ -964,22 +944,3 @@ function createBaseVideosWhere () {
     }
   }
 }
-
-function generateImage (video: VideoInstance, videoPath: string, folder: string, imageName: string, size: string) {
-  const options = {
-    filename: imageName,
-    count: 1,
-    folder
-  }
-
-  if (size) {
-    options['size'] = size
-  }
-
-  return new Promise<string>((res, rej) => {
-    ffmpeg(videoPath)
-      .on('error', rej)
-      .on('end', () => res(imageName))
-      .thumbnail(options)
-  })
-}
index 08fa73aa2df5387729586bea2a277c60b7d3644d..8b60ac0f420548eb9a05a60a5006a854bd99dfec 100644 (file)
@@ -106,8 +106,8 @@ describe('Test multiple pods', function () {
         const file = video.files[0]
         const magnetUri = file.magnetUri
         expect(file.magnetUri).to.have.lengthOf.above(2)
-        expect(file.resolution).to.equal(0)
-        expect(file.resolutionLabel).to.equal('original')
+        expect(file.resolution).to.equal(720)
+        expect(file.resolutionLabel).to.equal('720p')
         expect(file.size).to.equal(572456)
 
         if (server.url !== 'http://localhost:9001') {
@@ -172,7 +172,7 @@ describe('Test multiple pods', function () {
         expect(dateIsValid(video.updatedAt)).to.be.true
         expect(video.author).to.equal('root')
 
-        expect(video.files).to.have.lengthOf(5)
+        expect(video.files).to.have.lengthOf(4)
 
         // Check common attributes
         for (const file of video.files) {
@@ -192,11 +192,6 @@ describe('Test multiple pods', function () {
           }
         }
 
-        const originalFile = video.files.find(f => f.resolution === 0)
-        expect(originalFile).not.to.be.undefined
-        expect(originalFile.resolutionLabel).to.equal('original')
-        expect(originalFile.size).to.be.above(700000).and.below(720000)
-
         const file240p = video.files.find(f => f.resolution === 240)
         expect(file240p).not.to.be.undefined
         expect(file240p.resolutionLabel).to.equal('240p')
@@ -215,7 +210,7 @@ describe('Test multiple pods', function () {
         const file720p = video.files.find(f => f.resolution === 720)
         expect(file720p).not.to.be.undefined
         expect(file720p.resolutionLabel).to.equal('720p')
-        expect(file720p.size).to.be.above(310000).and.below(320000)
+        expect(file720p.size).to.be.above(700000).and.below(7200000)
 
         const test = await testVideoImage(server.url, 'video_short2.webm', video.thumbnailPath)
         expect(test).to.equal(true)
@@ -291,8 +286,8 @@ describe('Test multiple pods', function () {
 
         const file1 = video1.files[0]
         expect(file1.magnetUri).to.have.lengthOf.above(2)
-        expect(file1.resolution).to.equal(0)
-        expect(file1.resolutionLabel).to.equal('original')
+        expect(file1.resolution).to.equal(720)
+        expect(file1.resolutionLabel).to.equal('720p')
         expect(file1.size).to.equal(292677)
 
         expect(video2.name).to.equal('my super name for pod 3-2')
@@ -316,8 +311,8 @@ describe('Test multiple pods', function () {
         const file2 = video2.files[0]
         const magnetUri2 = file2.magnetUri
         expect(file2.magnetUri).to.have.lengthOf.above(2)
-        expect(file2.resolution).to.equal(0)
-        expect(file2.resolutionLabel).to.equal('original')
+        expect(file2.resolution).to.equal(720)
+        expect(file2.resolutionLabel).to.equal('720p')
         expect(file2.size).to.equal(218910)
 
         if (server.url !== 'http://localhost:9003') {
@@ -402,6 +397,22 @@ describe('Test multiple pods', function () {
       expect(torrent.files.length).to.equal(1)
       expect(torrent.files[0].path).to.exist.and.to.not.equal('')
     })
+
+    it('Should add the file 2 in 360p by asking pod 1', async function () {
+      // Yes, this could be long
+      this.timeout(200000)
+
+      const res = await getVideosList(servers[0].url)
+
+      const video = res.body.data.find(v => v.name === 'my super name for pod 2')
+      const file = video.files.find(f => f.resolution === 360)
+      expect(file).not.to.be.undefined
+
+      const torrent = await webtorrentAdd(file.magnetUri)
+      expect(torrent.files).to.be.an('array')
+      expect(torrent.files.length).to.equal(1)
+      expect(torrent.files[0].path).to.exist.and.to.not.equal('')
+    })
   })
 
   describe('Should update video views, likes and dislikes', function () {
@@ -562,8 +573,8 @@ describe('Test multiple pods', function () {
 
         const file = videoUpdated.files[0]
         expect(file.magnetUri).to.have.lengthOf.above(2)
-        expect(file.resolution).to.equal(0)
-        expect(file.resolutionLabel).to.equal('original')
+        expect(file.resolution).to.equal(720)
+        expect(file.resolutionLabel).to.equal('720p')
         expect(file.size).to.equal(292677)
 
         const test = await testVideoImage(server.url, 'video_short3.webm', videoUpdated.thumbnailPath)
index 83c981f9b764e720be2fb20f2cec06343e9d65f7..82bc51a3ebabce52f77b6b303f0d817296e0e031 100644 (file)
@@ -127,8 +127,8 @@ describe('Test a single pod', function () {
     const file = video.files[0]
     const magnetUri = file.magnetUri
     expect(file.magnetUri).to.have.lengthOf.above(2)
-    expect(file.resolution).to.equal(0)
-    expect(file.resolutionLabel).to.equal('original')
+    expect(file.resolution).to.equal(720)
+    expect(file.resolutionLabel).to.equal('720p')
     expect(file.size).to.equal(218910)
 
     const test = await testVideoImage(server.url, 'video_short.webm', video.thumbnailPath)
@@ -170,8 +170,8 @@ describe('Test a single pod', function () {
 
     const file = video.files[0]
     expect(file.magnetUri).to.have.lengthOf.above(2)
-    expect(file.resolution).to.equal(0)
-    expect(file.resolutionLabel).to.equal('original')
+    expect(file.resolution).to.equal(720)
+    expect(file.resolutionLabel).to.equal('720p')
     expect(file.size).to.equal(218910)
 
     const test = await testVideoImage(server.url, 'video_short.webm', video.thumbnailPath)
@@ -229,8 +229,8 @@ describe('Test a single pod', function () {
 
     const file = video.files[0]
     expect(file.magnetUri).to.have.lengthOf.above(2)
-    expect(file.resolution).to.equal(0)
-    expect(file.resolutionLabel).to.equal('original')
+    expect(file.resolution).to.equal(720)
+    expect(file.resolutionLabel).to.equal('720p')
     expect(file.size).to.equal(218910)
 
     const test = await testVideoImage(server.url, 'video_short.webm', video.thumbnailPath)
@@ -291,8 +291,8 @@ describe('Test a single pod', function () {
 
     const file = video.files[0]
     expect(file.magnetUri).to.have.lengthOf.above(2)
-    expect(file.resolution).to.equal(0)
-    expect(file.resolutionLabel).to.equal('original')
+    expect(file.resolution).to.equal(720)
+    expect(file.resolutionLabel).to.equal('720p')
     expect(file.size).to.equal(218910)
 
     const test = await testVideoImage(server.url, 'video_short.webm', video.thumbnailPath)
@@ -569,8 +569,8 @@ describe('Test a single pod', function () {
     const file = video.files[0]
     const magnetUri = file.magnetUri
     expect(file.magnetUri).to.have.lengthOf.above(2)
-    expect(file.resolution).to.equal(0)
-    expect(file.resolutionLabel).to.equal('original')
+    expect(file.resolution).to.equal(720)
+    expect(file.resolutionLabel).to.equal('720p')
     expect(file.size).to.equal(292677)
 
     const test = await testVideoImage(server.url, 'video_short3.webm', video.thumbnailPath)
@@ -612,8 +612,8 @@ describe('Test a single pod', function () {
 
     const file = video.files[0]
     expect(file.magnetUri).to.have.lengthOf.above(2)
-    expect(file.resolution).to.equal(0)
-    expect(file.resolutionLabel).to.equal('original')
+    expect(file.resolution).to.equal(720)
+    expect(file.resolutionLabel).to.equal('720p')
     expect(file.size).to.equal(292677)
   })
 
@@ -647,8 +647,8 @@ describe('Test a single pod', function () {
 
     const file = video.files[0]
     expect(file.magnetUri).to.have.lengthOf.above(2)
-    expect(file.resolution).to.equal(0)
-    expect(file.resolutionLabel).to.equal('original')
+    expect(file.resolution).to.equal(720)
+    expect(file.resolutionLabel).to.equal('720p')
     expect(file.size).to.equal(292677)
   })
 
index b5d84d9e73e8c24cc8503173cf0970976c7ab7fc..22d89724bc75b541436b210f557b1a4f3a476777 100644 (file)
@@ -68,7 +68,7 @@ describe('Test video transcoding', function () {
     const res = await getVideosList(servers[1].url)
 
     const video = res.body.data[0]
-    expect(video.files).to.have.lengthOf(5)
+    expect(video.files).to.have.lengthOf(4)
 
     const magnetUri = video.files[0].magnetUri
     expect(magnetUri).to.match(/\.mp4/)
index e31a8415682f7727c4b002b105828fd972b4cf40..7e1d3f658d54b9b6283f86d6765652fe223197c3 100644 (file)
@@ -55,13 +55,13 @@ describe('Test update host scripts', function () {
     expect(videos).to.have.lengthOf(2)
 
     for (const video of videos) {
-      expect(video.files).to.have.lengthOf(5)
+      expect(video.files).to.have.lengthOf(4)
 
       for (const file of video.files) {
         expect(file.magnetUri).to.contain('localhost%3A9002%2Ftracker%2Fsocket')
         expect(file.magnetUri).to.contain('localhost%3A9002%2Fstatic%2Fwebseed%2F')
 
-        const torrent = await parseTorrentVideo(server, video.uuid, file.resolutionLabel)
+        const torrent = await parseTorrentVideo(server, video.uuid, file.resolution)
         expect(torrent.announce[0]).to.equal('ws://localhost:9002/tracker/socket')
         expect(torrent.urlList[0]).to.contain('http://localhost:9002/static/webseed')
       }
index 7f8bd39c094d2be5b61372fc3a3d80afc98f6228..2a9a236ca50259f9b550ba4a21680cb04cb601e0 100644 (file)
@@ -196,14 +196,14 @@ function uploadVideo (url: string, accessToken: string, videoAttributesArg: Vide
     req.field('tags[' + i + ']', attributes.tags[i])
   }
 
-  let filepath = ''
+  let filePath = ''
   if (isAbsolute(attributes.fixture)) {
-    filepath = attributes.fixture
+    filePath = attributes.fixture
   } else {
-    filepath = join(__dirname, '..', 'api', 'fixtures', attributes.fixture)
+    filePath = join(__dirname, '..', 'api', 'fixtures', attributes.fixture)
   }
 
-  return req.attach('videofile', filepath)
+  return req.attach('videofile', filePath)
             .expect(specialStatus)
 }
 
@@ -238,9 +238,9 @@ function rateVideo (url: string, accessToken: string, id: number, rating: string
           .expect(specialStatus)
 }
 
-function parseTorrentVideo (server: ServerInfo, videoUUID: string, resolutionLabel: string) {
+function parseTorrentVideo (server: ServerInfo, videoUUID: string, resolution: number) {
   return new Promise<any>((res, rej) => {
-    const torrentName = videoUUID + '-' + resolutionLabel + '.torrent'
+    const torrentName = videoUUID + '-' + resolution + '.torrent'
     const torrentPath = join(__dirname, '..', '..', '..', 'test' + server.serverNumber, 'torrents', torrentName)
     readFile(torrentPath, (err, data) => {
       if (err) return rej(err)
index bdce77ed6530e943e04377af9632667ce9ed02e0..100fc0e6ebd2ded2d2234f1ef8bebc98094d2e88 100644 (file)
@@ -1,5 +1,4 @@
 export enum VideoResolution {
-  ORIGINAL = 0,
   H_240P = 240,
   H_360P = 360,
   H_480P = 480,