From 14d3270f363245d2c83fcc2ac109e39743b5627e Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Mon, 9 Oct 2017 11:06:13 +0200 Subject: [PATCH 1/1] Change how we handle resolution It was an enum before, now we just use video height --- config/production.yaml.example | 6 + server/controllers/api/videos/index.ts | 12 +- server/helpers/custom-validators/videos.ts | 5 +- server/helpers/ffmpeg-utils.ts | 79 +++++++ server/helpers/index.ts | 1 + server/helpers/utils.ts | 2 +- server/initializers/constants.ts | 11 - .../migrations/0075-video-resolutions.ts | 43 ++-- server/middlewares/validators/videos.ts | 5 +- server/models/user/user.ts | 43 ++-- server/models/video/video-interface.ts | 2 - server/models/video/video.ts | 207 +++++++----------- server/tests/api/multiple-pods.ts | 41 ++-- server/tests/api/single-pod.ts | 28 +-- server/tests/api/video-transcoder.ts | 2 +- server/tests/cli/update-host.ts | 4 +- server/tests/utils/videos.ts | 12 +- shared/models/videos/video-resolution.enum.ts | 1 - 18 files changed, 270 insertions(+), 234 deletions(-) create mode 100644 server/helpers/ffmpeg-utils.ts diff --git a/config/production.yaml.example b/config/production.yaml.example index 987da12cc..f99c8927e 100644 --- a/config/production.yaml.example +++ b/config/production.yaml.example @@ -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 diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts index 14c969ec3..2b7ead954 100644 --- a/server/controllers/api/videos/index.ts +++ b/server/controllers/api/videos/index.ts @@ -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 } diff --git a/server/helpers/custom-validators/videos.ts b/server/helpers/custom-validators/videos.ts index 2eb021ae7..a31aca019 100644 --- a/server/helpers/custom-validators/videos.ts +++ b/server/helpers/custom-validators/videos.ts @@ -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 index 000000000..c35125ec1 --- /dev/null +++ b/server/helpers/ffmpeg-utils.ts @@ -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((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((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((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((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 +} diff --git a/server/helpers/index.ts b/server/helpers/index.ts index 78215fe10..846bd796f 100644 --- a/server/helpers/index.ts +++ b/server/helpers/index.ts @@ -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' diff --git a/server/helpers/utils.ts b/server/helpers/utils.ts index b74442ab0..3317dddc3 100644 --- a/server/helpers/utils.ts +++ b/server/helpers/utils.ts @@ -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) } } diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts index f87041a3f..b11575b34 100644 --- a/server/initializers/constants.ts +++ b/server/initializers/constants.ts @@ -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 diff --git a/server/initializers/migrations/0075-video-resolutions.ts b/server/initializers/migrations/0075-video-resolutions.ts index 6bc1e72ab..e1d9fdacb 100644 --- a/server/initializers/migrations/0075-video-resolutions.ts +++ b/server/initializers/migrations/0075-video-resolutions.ts @@ -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[] = [] - 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[] = [] 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) } diff --git a/server/middlewares/validators/videos.ts b/server/middlewares/validators/videos.ts index bc8b7e541..5f213f974 100644 --- a/server/middlewares/validators/videos.ts +++ b/server/middlewares/validators/videos.ts @@ -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) diff --git a/server/models/user/user.ts b/server/models/user/user.ts index 7a21dbefa..0dc52d3cf 100644 --- a/server/models/user/user.ts +++ b/server/models/user/user.ts @@ -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) + }) } diff --git a/server/models/video/video-interface.ts b/server/models/video/video-interface.ts index 340426f45..6a3db4f3e 100644 --- a/server/models/video/video-interface.ts +++ b/server/models/video/video-interface.ts @@ -35,7 +35,6 @@ export namespace VideoMethods { // Return thumbnail name export type GenerateThumbnailFromData = (video: VideoInstance, thumbnailData: string) => Promise - export type GetDurationFromFile = (videoPath: string) => Promise export type List = () => Promise export type ListOwnedAndPopulateAuthorAndTags = () => Promise @@ -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 diff --git a/server/models/video/video.ts b/server/models/video/video.ts index c376d769e..2ba6cf25f 100644 --- a/server/models/video/video.ts +++ b/server/models/video/video.ts @@ -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((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((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((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((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((res, rej) => { - ffmpeg(videoPath) - .on('error', rej) - .on('end', () => res(imageName)) - .thumbnail(options) - }) -} diff --git a/server/tests/api/multiple-pods.ts b/server/tests/api/multiple-pods.ts index 08fa73aa2..8b60ac0f4 100644 --- a/server/tests/api/multiple-pods.ts +++ b/server/tests/api/multiple-pods.ts @@ -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) diff --git a/server/tests/api/single-pod.ts b/server/tests/api/single-pod.ts index 83c981f9b..82bc51a3e 100644 --- a/server/tests/api/single-pod.ts +++ b/server/tests/api/single-pod.ts @@ -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) }) diff --git a/server/tests/api/video-transcoder.ts b/server/tests/api/video-transcoder.ts index b5d84d9e7..22d89724b 100644 --- a/server/tests/api/video-transcoder.ts +++ b/server/tests/api/video-transcoder.ts @@ -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/) diff --git a/server/tests/cli/update-host.ts b/server/tests/cli/update-host.ts index e31a84156..7e1d3f658 100644 --- a/server/tests/cli/update-host.ts +++ b/server/tests/cli/update-host.ts @@ -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') } diff --git a/server/tests/utils/videos.ts b/server/tests/utils/videos.ts index 7f8bd39c0..2a9a236ca 100644 --- a/server/tests/utils/videos.ts +++ b/server/tests/utils/videos.ts @@ -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((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) diff --git a/shared/models/videos/video-resolution.enum.ts b/shared/models/videos/video-resolution.enum.ts index bdce77ed6..100fc0e6e 100644 --- a/shared/models/videos/video-resolution.enum.ts +++ b/shared/models/videos/video-resolution.enum.ts @@ -1,5 +1,4 @@ export enum VideoResolution { - ORIGINAL = 0, H_240P = 240, H_360P = 360, H_480P = 480, -- 2.41.0