diff options
-rw-r--r-- | config/production.yaml.example | 6 | ||||
-rw-r--r-- | server/controllers/api/videos/index.ts | 12 | ||||
-rw-r--r-- | server/helpers/custom-validators/videos.ts | 5 | ||||
-rw-r--r-- | server/helpers/ffmpeg-utils.ts | 79 | ||||
-rw-r--r-- | server/helpers/index.ts | 1 | ||||
-rw-r--r-- | server/helpers/utils.ts | 2 | ||||
-rw-r--r-- | server/initializers/constants.ts | 11 | ||||
-rw-r--r-- | server/initializers/migrations/0075-video-resolutions.ts | 43 | ||||
-rw-r--r-- | server/middlewares/validators/videos.ts | 5 | ||||
-rw-r--r-- | server/models/user/user.ts | 43 | ||||
-rw-r--r-- | server/models/video/video-interface.ts | 2 | ||||
-rw-r--r-- | server/models/video/video.ts | 207 | ||||
-rw-r--r-- | server/tests/api/multiple-pods.ts | 41 | ||||
-rw-r--r-- | server/tests/api/single-pod.ts | 28 | ||||
-rw-r--r-- | server/tests/api/video-transcoder.ts | 2 | ||||
-rw-r--r-- | server/tests/cli/update-host.ts | 4 | ||||
-rw-r--r-- | server/tests/utils/videos.ts | 12 | ||||
-rw-r--r-- | shared/models/videos/video-resolution.enum.ts | 1 |
18 files changed, 270 insertions, 234 deletions
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: | |||
46 | transcoding: | 46 | transcoding: |
47 | enabled: false | 47 | enabled: false |
48 | threads: 2 | 48 | threads: 2 |
49 | resolutions: # Only created if the original video has a higher resolution | ||
50 | 240p: true | ||
51 | 360p: true | ||
52 | 480p: true | ||
53 | 720p: true | ||
54 | 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 { | |||
37 | retryTransactionWrapper, | 37 | retryTransactionWrapper, |
38 | generateRandomString, | 38 | generateRandomString, |
39 | getFormattedObjects, | 39 | getFormattedObjects, |
40 | renamePromise | 40 | renamePromise, |
41 | getVideoFileHeight | ||
41 | } from '../../../helpers' | 42 | } from '../../../helpers' |
42 | import { TagInstance, VideoInstance } from '../../../models' | 43 | import { TagInstance, VideoInstance } from '../../../models' |
43 | import { VideoCreate, VideoUpdate, VideoResolution } from '../../../../shared' | 44 | import { VideoCreate, VideoUpdate } from '../../../../shared' |
44 | 45 | ||
45 | import { abuseVideoRouter } from './abuse' | 46 | import { abuseVideoRouter } from './abuse' |
46 | import { blacklistRouter } from './blacklist' | 47 | import { blacklistRouter } from './blacklist' |
@@ -192,9 +193,14 @@ function addVideo (req: express.Request, res: express.Response, videoPhysicalFil | |||
192 | return { author, tagInstances, video } | 193 | return { author, tagInstances, video } |
193 | }) | 194 | }) |
194 | .then(({ author, tagInstances, video }) => { | 195 | .then(({ author, tagInstances, video }) => { |
196 | const videoFilePath = join(CONFIG.STORAGE.VIDEOS_DIR, videoPhysicalFile.filename) | ||
197 | return getVideoFileHeight(videoFilePath) | ||
198 | .then(height => ({ author, tagInstances, video, videoFileHeight: height })) | ||
199 | }) | ||
200 | .then(({ author, tagInstances, video, videoFileHeight }) => { | ||
195 | const videoFileData = { | 201 | const videoFileData = { |
196 | extname: extname(videoPhysicalFile.filename), | 202 | extname: extname(videoPhysicalFile.filename), |
197 | resolution: VideoResolution.ORIGINAL, | 203 | resolution: videoFileHeight, |
198 | size: videoPhysicalFile.size | 204 | size: videoPhysicalFile.size |
199 | } | 205 | } |
200 | 206 | ||
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 { | |||
8 | VIDEO_CATEGORIES, | 8 | VIDEO_CATEGORIES, |
9 | VIDEO_LICENCES, | 9 | VIDEO_LICENCES, |
10 | VIDEO_LANGUAGES, | 10 | VIDEO_LANGUAGES, |
11 | VIDEO_RATE_TYPES, | 11 | VIDEO_RATE_TYPES |
12 | VIDEO_FILE_RESOLUTIONS | ||
13 | } from '../../initializers' | 12 | } from '../../initializers' |
14 | import { isUserUsernameValid } from './users' | 13 | import { isUserUsernameValid } from './users' |
15 | import { isArray, exists } from './misc' | 14 | import { isArray, exists } from './misc' |
@@ -128,7 +127,7 @@ function isVideoFileSizeValid (value: string) { | |||
128 | } | 127 | } |
129 | 128 | ||
130 | function isVideoFileResolutionValid (value: string) { | 129 | function isVideoFileResolutionValid (value: string) { |
131 | return VIDEO_FILE_RESOLUTIONS[value] !== undefined | 130 | return exists(value) && validator.isInt(value + '') |
132 | } | 131 | } |
133 | 132 | ||
134 | function isVideoFileExtnameValid (value: string) { | 133 | 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 @@ | |||
1 | import * as Promise from 'bluebird' | ||
2 | import * as ffmpeg from 'fluent-ffmpeg' | ||
3 | |||
4 | import { CONFIG } from '../initializers' | ||
5 | import { VideoResolution } from '../../shared/models/videos/video-resolution.enum' | ||
6 | |||
7 | function getVideoFileHeight (path: string) { | ||
8 | return new Promise<number>((res, rej) => { | ||
9 | ffmpeg.ffprobe(path, (err, metadata) => { | ||
10 | if (err) return rej(err) | ||
11 | |||
12 | const videoStream = metadata.streams.find(s => s.codec_type === 'video') | ||
13 | return res(videoStream.height) | ||
14 | }) | ||
15 | }) | ||
16 | } | ||
17 | |||
18 | function getDurationFromVideoFile (path: string) { | ||
19 | return new Promise<number>((res, rej) => { | ||
20 | ffmpeg.ffprobe(path, (err, metadata) => { | ||
21 | if (err) return rej(err) | ||
22 | |||
23 | return res(Math.floor(metadata.format.duration)) | ||
24 | }) | ||
25 | }) | ||
26 | } | ||
27 | |||
28 | function generateImageFromVideoFile (fromPath: string, folder: string, imageName: string, size?: string) { | ||
29 | const options = { | ||
30 | filename: imageName, | ||
31 | count: 1, | ||
32 | folder | ||
33 | } | ||
34 | |||
35 | if (size !== undefined) { | ||
36 | options['size'] = size | ||
37 | } | ||
38 | |||
39 | return new Promise<string>((res, rej) => { | ||
40 | ffmpeg(fromPath) | ||
41 | .on('error', rej) | ||
42 | .on('end', () => res(imageName)) | ||
43 | .thumbnail(options) | ||
44 | }) | ||
45 | } | ||
46 | |||
47 | type TranscodeOptions = { | ||
48 | inputPath: string | ||
49 | outputPath: string | ||
50 | resolution?: VideoResolution | ||
51 | } | ||
52 | |||
53 | function transcode (options: TranscodeOptions) { | ||
54 | return new Promise<void>((res, rej) => { | ||
55 | let command = ffmpeg(options.inputPath) | ||
56 | .output(options.outputPath) | ||
57 | .videoCodec('libx264') | ||
58 | .outputOption('-threads ' + CONFIG.TRANSCODING.THREADS) | ||
59 | .outputOption('-movflags faststart') | ||
60 | |||
61 | if (options.resolution !== undefined) { | ||
62 | const size = `${options.resolution}x?` // '720x?' for example | ||
63 | command = command.size(size) | ||
64 | } | ||
65 | |||
66 | command.on('error', rej) | ||
67 | .on('end', res) | ||
68 | .run() | ||
69 | }) | ||
70 | } | ||
71 | |||
72 | // --------------------------------------------------------------------------- | ||
73 | |||
74 | export { | ||
75 | getVideoFileHeight, | ||
76 | getDurationFromVideoFile, | ||
77 | generateImageFromVideoFile, | ||
78 | transcode | ||
79 | } | ||
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 @@ | |||
1 | export * from './core-utils' | 1 | export * from './core-utils' |
2 | export * from './logger' | 2 | export * from './logger' |
3 | export * from './custom-validators' | 3 | export * from './custom-validators' |
4 | export * from './ffmpeg-utils' | ||
4 | export * from './database-utils' | 5 | export * from './database-utils' |
5 | export * from './peertube-crypto' | 6 | export * from './peertube-crypto' |
6 | export * from './requests' | 7 | 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) { | |||
61 | ] | 61 | ] |
62 | 62 | ||
63 | for (const resolution of resolutions) { | 63 | for (const resolution of resolutions) { |
64 | if (configResolutions[resolution.toString()] === true && videoFileHeight >= resolution) { | 64 | if (configResolutions[resolution.toString()] === true && videoFileHeight > resolution) { |
65 | resolutionsEnabled.push(resolution) | 65 | resolutionsEnabled.push(resolution) |
66 | } | 66 | } |
67 | } | 67 | } |
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 = { | |||
189 | 14: 'Italian' | 189 | 14: 'Italian' |
190 | } | 190 | } |
191 | 191 | ||
192 | // TODO: use VideoResolution when https://github.com/Microsoft/TypeScript/issues/13042 is fixed | ||
193 | const VIDEO_FILE_RESOLUTIONS: { [ id: number ]: string } = { | ||
194 | 0: 'original', | ||
195 | 240: '240p', | ||
196 | 360: '360p', | ||
197 | 480: '480p', | ||
198 | 720: '720p', | ||
199 | 1080: '1080p' | ||
200 | } | ||
201 | |||
202 | // --------------------------------------------------------------------------- | 192 | // --------------------------------------------------------------------------- |
203 | 193 | ||
204 | // Score a pod has when we create it as a friend | 194 | // Score a pod has when we create it as a friend |
@@ -385,7 +375,6 @@ export { | |||
385 | THUMBNAILS_SIZE, | 375 | THUMBNAILS_SIZE, |
386 | USER_ROLES, | 376 | USER_ROLES, |
387 | VIDEO_CATEGORIES, | 377 | VIDEO_CATEGORIES, |
388 | VIDEO_FILE_RESOLUTIONS, | ||
389 | VIDEO_LANGUAGES, | 378 | VIDEO_LANGUAGES, |
390 | VIDEO_LICENCES, | 379 | VIDEO_LICENCES, |
391 | VIDEO_RATE_TYPES | 380 | 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' | |||
4 | 4 | ||
5 | import { readdirPromise, renamePromise } from '../../helpers/core-utils' | 5 | import { readdirPromise, renamePromise } from '../../helpers/core-utils' |
6 | import { CONFIG } from '../../initializers/constants' | 6 | import { CONFIG } from '../../initializers/constants' |
7 | import { getVideoFileHeight } from '../../helpers/ffmpeg-utils' | ||
7 | 8 | ||
8 | function up (utils: { | 9 | function up (utils: { |
9 | transaction: Sequelize.Transaction, | 10 | transaction: Sequelize.Transaction, |
@@ -14,26 +15,7 @@ function up (utils: { | |||
14 | const torrentDir = CONFIG.STORAGE.TORRENTS_DIR | 15 | const torrentDir = CONFIG.STORAGE.TORRENTS_DIR |
15 | const videoFileDir = CONFIG.STORAGE.VIDEOS_DIR | 16 | const videoFileDir = CONFIG.STORAGE.VIDEOS_DIR |
16 | 17 | ||
17 | return readdirPromise(torrentDir) | 18 | return readdirPromise(videoFileDir) |
18 | .then(torrentFiles => { | ||
19 | const tasks: Promise<any>[] = [] | ||
20 | for (const torrentFile of torrentFiles) { | ||
21 | 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) | ||
22 | if (matches === null) { | ||
23 | console.log('Invalid torrent file name %s.', torrentFile) | ||
24 | continue | ||
25 | } | ||
26 | |||
27 | const newTorrentName = matches[1] + '-original.torrent' | ||
28 | const p = renamePromise(join(torrentDir, torrentFile), join(torrentDir, newTorrentName)) | ||
29 | tasks.push(p) | ||
30 | } | ||
31 | |||
32 | return Promise.all(tasks) | ||
33 | }) | ||
34 | .then(() => { | ||
35 | return readdirPromise(videoFileDir) | ||
36 | }) | ||
37 | .then(videoFiles => { | 19 | .then(videoFiles => { |
38 | const tasks: Promise<any>[] = [] | 20 | const tasks: Promise<any>[] = [] |
39 | for (const videoFile of videoFiles) { | 21 | for (const videoFile of videoFiles) { |
@@ -43,8 +25,25 @@ function up (utils: { | |||
43 | continue | 25 | continue |
44 | } | 26 | } |
45 | 27 | ||
46 | const newVideoFileName = matches[1] + '-original.' + matches[2] | 28 | const uuid = matches[1] |
47 | const p = renamePromise(join(videoFileDir, videoFile), join(videoFileDir, newVideoFileName)) | 29 | const ext = matches[2] |
30 | |||
31 | const p = getVideoFileHeight(join(videoFileDir, videoFile)) | ||
32 | .then(height => { | ||
33 | const oldTorrentName = uuid + '.torrent' | ||
34 | const newTorrentName = uuid + '-' + height + '.torrent' | ||
35 | return renamePromise(join(torrentDir, oldTorrentName), join(torrentDir, newTorrentName)).then(() => height) | ||
36 | }) | ||
37 | .then(height => { | ||
38 | const newVideoFileName = uuid + '-' + height + '.' + ext | ||
39 | return renamePromise(join(videoFileDir, videoFile), join(videoFileDir, newVideoFileName)).then(() => height) | ||
40 | }) | ||
41 | .then(height => { | ||
42 | const query = 'UPDATE "VideoFiles" SET "resolution" = ' + height + | ||
43 | ' WHERE "videoId" = (SELECT "id" FROM "Videos" WHERE "uuid" = \'' + uuid + '\')' | ||
44 | return utils.sequelize.query(query) | ||
45 | }) | ||
46 | |||
48 | tasks.push(p) | 47 | tasks.push(p) |
49 | } | 48 | } |
50 | 49 | ||
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 { | |||
19 | isVideoNSFWValid, | 19 | isVideoNSFWValid, |
20 | isVideoIdOrUUIDValid, | 20 | isVideoIdOrUUIDValid, |
21 | isVideoAbuseReasonValid, | 21 | isVideoAbuseReasonValid, |
22 | isVideoRatingTypeValid | 22 | isVideoRatingTypeValid, |
23 | getDurationFromVideoFile | ||
23 | } from '../../helpers' | 24 | } from '../../helpers' |
24 | import { VideoInstance } from '../../models' | 25 | import { VideoInstance } from '../../models' |
25 | 26 | ||
@@ -50,7 +51,7 @@ const videosAddValidator = [ | |||
50 | return undefined | 51 | return undefined |
51 | } | 52 | } |
52 | 53 | ||
53 | return db.Video.getDurationFromFile(videoFile.path) | 54 | return getDurationFromVideoFile(videoFile.path) |
54 | .catch(err => { | 55 | .catch(err => { |
55 | logger.error('Invalid input file in videosAddValidator.', err) | 56 | logger.error('Invalid input file in videosAddValidator.', err) |
56 | res.status(400) | 57 | 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 { | |||
12 | isUserDisplayNSFWValid, | 12 | isUserDisplayNSFWValid, |
13 | isUserVideoQuotaValid | 13 | isUserVideoQuotaValid |
14 | } from '../../helpers' | 14 | } from '../../helpers' |
15 | import { VideoResolution } from '../../../shared' | ||
16 | 15 | ||
17 | import { addMethodsToModel } from '../utils' | 16 | import { addMethodsToModel } from '../utils' |
18 | import { | 17 | import { |
@@ -243,33 +242,21 @@ loadByUsernameOrEmail = function (username: string, email: string) { | |||
243 | // --------------------------------------------------------------------------- | 242 | // --------------------------------------------------------------------------- |
244 | 243 | ||
245 | function getOriginalVideoFileTotalFromUser (user: UserInstance) { | 244 | function getOriginalVideoFileTotalFromUser (user: UserInstance) { |
246 | // attributes = [] because we don't want other fields than the sum | 245 | // Don't use sequelize because we need to use a subquery |
247 | const query = { | 246 | const query = 'SELECT SUM("size") AS "total" FROM ' + |
248 | where: { | 247 | '(SELECT MAX("VideoFiles"."size") AS "size" FROM "VideoFiles" ' + |
249 | resolution: VideoResolution.ORIGINAL | 248 | 'INNER JOIN "Videos" ON "VideoFiles"."videoId" = "Videos"."id" ' + |
250 | }, | 249 | 'INNER JOIN "Authors" ON "Videos"."authorId" = "Authors"."id" ' + |
251 | include: [ | 250 | 'INNER JOIN "Users" ON "Authors"."userId" = "Users"."id" ' + |
252 | { | 251 | 'WHERE "Users"."id" = $userId GROUP BY "Videos"."id") t' |
253 | attributes: [], | 252 | |
254 | model: User['sequelize'].models.Video, | 253 | const options = { |
255 | include: [ | 254 | bind: { userId: user.id }, |
256 | { | 255 | type: Sequelize.QueryTypes.SELECT |
257 | attributes: [], | ||
258 | model: User['sequelize'].models.Author, | ||
259 | include: [ | ||
260 | { | ||
261 | attributes: [], | ||
262 | model: User['sequelize'].models.User, | ||
263 | where: { | ||
264 | id: user.id | ||
265 | } | ||
266 | } | ||
267 | ] | ||
268 | } | ||
269 | ] | ||
270 | } | ||
271 | ] | ||
272 | } | 256 | } |
257 | return User['sequelize'].query(query, options).then(([ { total } ]) => { | ||
258 | if (total === null) return 0 | ||
273 | 259 | ||
274 | return User['sequelize'].models.VideoFile.sum('size', query) | 260 | return parseInt(total, 10) |
261 | }) | ||
275 | } | 262 | } |
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 { | |||
35 | 35 | ||
36 | // Return thumbnail name | 36 | // Return thumbnail name |
37 | export type GenerateThumbnailFromData = (video: VideoInstance, thumbnailData: string) => Promise<string> | 37 | export type GenerateThumbnailFromData = (video: VideoInstance, thumbnailData: string) => Promise<string> |
38 | export type GetDurationFromFile = (videoPath: string) => Promise<number> | ||
39 | 38 | ||
40 | export type List = () => Promise<VideoInstance[]> | 39 | export type List = () => Promise<VideoInstance[]> |
41 | export type ListOwnedAndPopulateAuthorAndTags = () => Promise<VideoInstance[]> | 40 | export type ListOwnedAndPopulateAuthorAndTags = () => Promise<VideoInstance[]> |
@@ -65,7 +64,6 @@ export namespace VideoMethods { | |||
65 | 64 | ||
66 | export interface VideoClass { | 65 | export interface VideoClass { |
67 | generateThumbnailFromData: VideoMethods.GenerateThumbnailFromData | 66 | generateThumbnailFromData: VideoMethods.GenerateThumbnailFromData |
68 | getDurationFromFile: VideoMethods.GetDurationFromFile | ||
69 | list: VideoMethods.List | 67 | list: VideoMethods.List |
70 | listForApi: VideoMethods.ListForApi | 68 | listForApi: VideoMethods.ListForApi |
71 | listOwnedAndPopulateAuthorAndTags: VideoMethods.ListOwnedAndPopulateAuthorAndTags | 69 | 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 @@ | |||
1 | import * as safeBuffer from 'safe-buffer' | 1 | import * as safeBuffer from 'safe-buffer' |
2 | const Buffer = safeBuffer.Buffer | 2 | const Buffer = safeBuffer.Buffer |
3 | import * as ffmpeg from 'fluent-ffmpeg' | ||
4 | import * as magnetUtil from 'magnet-uri' | 3 | import * as magnetUtil from 'magnet-uri' |
5 | import { map } from 'lodash' | 4 | import { map } from 'lodash' |
6 | import * as parseTorrent from 'parse-torrent' | 5 | import * as parseTorrent from 'parse-torrent' |
7 | import { join } from 'path' | 6 | import { join } from 'path' |
8 | import * as Sequelize from 'sequelize' | 7 | import * as Sequelize from 'sequelize' |
9 | import * as Promise from 'bluebird' | 8 | import * as Promise from 'bluebird' |
9 | import { maxBy } from 'lodash' | ||
10 | 10 | ||
11 | import { TagInstance } from './tag-interface' | 11 | import { TagInstance } from './tag-interface' |
12 | import { | 12 | import { |
@@ -23,7 +23,10 @@ import { | |||
23 | renamePromise, | 23 | renamePromise, |
24 | writeFilePromise, | 24 | writeFilePromise, |
25 | createTorrentPromise, | 25 | createTorrentPromise, |
26 | statPromise | 26 | statPromise, |
27 | generateImageFromVideoFile, | ||
28 | transcode, | ||
29 | getVideoFileHeight | ||
27 | } from '../../helpers' | 30 | } from '../../helpers' |
28 | import { | 31 | import { |
29 | CONFIG, | 32 | CONFIG, |
@@ -32,8 +35,7 @@ import { | |||
32 | VIDEO_CATEGORIES, | 35 | VIDEO_CATEGORIES, |
33 | VIDEO_LICENCES, | 36 | VIDEO_LICENCES, |
34 | VIDEO_LANGUAGES, | 37 | VIDEO_LANGUAGES, |
35 | THUMBNAILS_SIZE, | 38 | THUMBNAILS_SIZE |
36 | VIDEO_FILE_RESOLUTIONS | ||
37 | } from '../../initializers' | 39 | } from '../../initializers' |
38 | import { removeVideoToFriends } from '../../lib' | 40 | import { removeVideoToFriends } from '../../lib' |
39 | import { VideoResolution } from '../../../shared' | 41 | import { VideoResolution } from '../../../shared' |
@@ -67,7 +69,6 @@ let createTorrentAndSetInfoHash: VideoMethods.CreateTorrentAndSetInfoHash | |||
67 | let getOriginalFileHeight: VideoMethods.GetOriginalFileHeight | 69 | let getOriginalFileHeight: VideoMethods.GetOriginalFileHeight |
68 | 70 | ||
69 | let generateThumbnailFromData: VideoMethods.GenerateThumbnailFromData | 71 | let generateThumbnailFromData: VideoMethods.GenerateThumbnailFromData |
70 | let getDurationFromFile: VideoMethods.GetDurationFromFile | ||
71 | let list: VideoMethods.List | 72 | let list: VideoMethods.List |
72 | let listForApi: VideoMethods.ListForApi | 73 | let listForApi: VideoMethods.ListForApi |
73 | let loadByHostAndUUID: VideoMethods.LoadByHostAndUUID | 74 | let loadByHostAndUUID: VideoMethods.LoadByHostAndUUID |
@@ -233,7 +234,6 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da | |||
233 | associate, | 234 | associate, |
234 | 235 | ||
235 | generateThumbnailFromData, | 236 | generateThumbnailFromData, |
236 | getDurationFromFile, | ||
237 | list, | 237 | list, |
238 | listForApi, | 238 | listForApi, |
239 | listOwnedAndPopulateAuthorAndTags, | 239 | listOwnedAndPopulateAuthorAndTags, |
@@ -338,11 +338,12 @@ function afterDestroy (video: VideoInstance, options: { transaction: Sequelize.T | |||
338 | getOriginalFile = function (this: VideoInstance) { | 338 | getOriginalFile = function (this: VideoInstance) { |
339 | if (Array.isArray(this.VideoFiles) === false) return undefined | 339 | if (Array.isArray(this.VideoFiles) === false) return undefined |
340 | 340 | ||
341 | return this.VideoFiles.find(file => file.resolution === VideoResolution.ORIGINAL) | 341 | // The original file is the file that have the higher resolution |
342 | return maxBy(this.VideoFiles, file => file.resolution) | ||
342 | } | 343 | } |
343 | 344 | ||
344 | getVideoFilename = function (this: VideoInstance, videoFile: VideoFileInstance) { | 345 | getVideoFilename = function (this: VideoInstance, videoFile: VideoFileInstance) { |
345 | return this.uuid + '-' + VIDEO_FILE_RESOLUTIONS[videoFile.resolution] + videoFile.extname | 346 | return this.uuid + '-' + videoFile.resolution + videoFile.extname |
346 | } | 347 | } |
347 | 348 | ||
348 | getThumbnailName = function (this: VideoInstance) { | 349 | getThumbnailName = function (this: VideoInstance) { |
@@ -358,7 +359,7 @@ getPreviewName = function (this: VideoInstance) { | |||
358 | 359 | ||
359 | getTorrentFileName = function (this: VideoInstance, videoFile: VideoFileInstance) { | 360 | getTorrentFileName = function (this: VideoInstance, videoFile: VideoFileInstance) { |
360 | const extension = '.torrent' | 361 | const extension = '.torrent' |
361 | return this.uuid + '-' + VIDEO_FILE_RESOLUTIONS[videoFile.resolution] + extension | 362 | return this.uuid + '-' + videoFile.resolution + extension |
362 | } | 363 | } |
363 | 364 | ||
364 | isOwned = function (this: VideoInstance) { | 365 | isOwned = function (this: VideoInstance) { |
@@ -366,11 +367,20 @@ isOwned = function (this: VideoInstance) { | |||
366 | } | 367 | } |
367 | 368 | ||
368 | createPreview = function (this: VideoInstance, videoFile: VideoFileInstance) { | 369 | createPreview = function (this: VideoInstance, videoFile: VideoFileInstance) { |
369 | return generateImage(this, this.getVideoFilePath(videoFile), CONFIG.STORAGE.PREVIEWS_DIR, this.getPreviewName(), null) | 370 | return generateImageFromVideoFile( |
371 | this.getVideoFilePath(videoFile), | ||
372 | CONFIG.STORAGE.PREVIEWS_DIR, | ||
373 | this.getPreviewName() | ||
374 | ) | ||
370 | } | 375 | } |
371 | 376 | ||
372 | createThumbnail = function (this: VideoInstance, videoFile: VideoFileInstance) { | 377 | createThumbnail = function (this: VideoInstance, videoFile: VideoFileInstance) { |
373 | return generateImage(this, this.getVideoFilePath(videoFile), CONFIG.STORAGE.THUMBNAILS_DIR, this.getThumbnailName(), THUMBNAILS_SIZE) | 378 | return generateImageFromVideoFile( |
379 | this.getVideoFilePath(videoFile), | ||
380 | CONFIG.STORAGE.THUMBNAILS_DIR, | ||
381 | this.getThumbnailName(), | ||
382 | THUMBNAILS_SIZE | ||
383 | ) | ||
374 | } | 384 | } |
375 | 385 | ||
376 | getVideoFilePath = function (this: VideoInstance, videoFile: VideoFileInstance) { | 386 | getVideoFilePath = function (this: VideoInstance, videoFile: VideoFileInstance) { |
@@ -480,8 +490,7 @@ toFormattedJSON = function (this: VideoInstance) { | |||
480 | // Format and sort video files | 490 | // Format and sort video files |
481 | json.files = this.VideoFiles | 491 | json.files = this.VideoFiles |
482 | .map(videoFile => { | 492 | .map(videoFile => { |
483 | let resolutionLabel = VIDEO_FILE_RESOLUTIONS[videoFile.resolution] | 493 | let resolutionLabel = videoFile.resolution + 'p' |
484 | if (!resolutionLabel) resolutionLabel = 'Unknown' | ||
485 | 494 | ||
486 | const videoFileJson = { | 495 | const videoFileJson = { |
487 | resolution: videoFile.resolution, | 496 | resolution: videoFile.resolution, |
@@ -578,46 +587,42 @@ optimizeOriginalVideofile = function (this: VideoInstance) { | |||
578 | const videoInputPath = join(videosDirectory, this.getVideoFilename(inputVideoFile)) | 587 | const videoInputPath = join(videosDirectory, this.getVideoFilename(inputVideoFile)) |
579 | const videoOutputPath = join(videosDirectory, this.id + '-transcoded' + newExtname) | 588 | const videoOutputPath = join(videosDirectory, this.id + '-transcoded' + newExtname) |
580 | 589 | ||
581 | return new Promise<void>((res, rej) => { | 590 | const transcodeOptions = { |
582 | ffmpeg(videoInputPath) | 591 | inputPath: videoInputPath, |
583 | .output(videoOutputPath) | 592 | outputPath: videoOutputPath |
584 | .videoCodec('libx264') | 593 | } |
585 | .outputOption('-threads ' + CONFIG.TRANSCODING.THREADS) | 594 | |
586 | .outputOption('-movflags faststart') | 595 | return transcode(transcodeOptions) |
587 | .on('error', rej) | 596 | .then(() => { |
588 | .on('end', () => { | 597 | return unlinkPromise(videoInputPath) |
589 | 598 | }) | |
590 | return unlinkPromise(videoInputPath) | 599 | .then(() => { |
591 | .then(() => { | 600 | // Important to do this before getVideoFilename() to take in account the new file extension |
592 | // Important to do this before getVideoFilename() to take in account the new file extension | 601 | inputVideoFile.set('extname', newExtname) |
593 | inputVideoFile.set('extname', newExtname) | 602 | |
594 | 603 | return renamePromise(videoOutputPath, this.getVideoFilePath(inputVideoFile)) | |
595 | return renamePromise(videoOutputPath, this.getVideoFilePath(inputVideoFile)) | 604 | }) |
596 | }) | 605 | .then(() => { |
597 | .then(() => { | 606 | return statPromise(this.getVideoFilePath(inputVideoFile)) |
598 | return statPromise(this.getVideoFilePath(inputVideoFile)) | 607 | }) |
599 | }) | 608 | .then(stats => { |
600 | .then(stats => { | 609 | return inputVideoFile.set('size', stats.size) |
601 | return inputVideoFile.set('size', stats.size) | 610 | }) |
602 | }) | 611 | .then(() => { |
603 | .then(() => { | 612 | return this.createTorrentAndSetInfoHash(inputVideoFile) |
604 | return this.createTorrentAndSetInfoHash(inputVideoFile) | 613 | }) |
605 | }) | 614 | .then(() => { |
606 | .then(() => { | 615 | return inputVideoFile.save() |
607 | return inputVideoFile.save() | 616 | }) |
608 | }) | 617 | .then(() => { |
609 | .then(() => { | 618 | return undefined |
610 | return res() | 619 | }) |
611 | }) | 620 | .catch(err => { |
612 | .catch(err => { | 621 | // Auto destruction... |
613 | // Auto destruction... | 622 | this.destroy().catch(err => logger.error('Cannot destruct video after transcoding failure.', err)) |
614 | this.destroy().catch(err => logger.error('Cannot destruct video after transcoding failure.', err)) | 623 | |
615 | 624 | throw err | |
616 | return rej(err) | 625 | }) |
617 | }) | ||
618 | }) | ||
619 | .run() | ||
620 | }) | ||
621 | } | 626 | } |
622 | 627 | ||
623 | transcodeOriginalVideofile = function (this: VideoInstance, resolution: VideoResolution) { | 628 | transcodeOriginalVideofile = function (this: VideoInstance, resolution: VideoResolution) { |
@@ -634,52 +639,37 @@ transcodeOriginalVideofile = function (this: VideoInstance, resolution: VideoRes | |||
634 | videoId: this.id | 639 | videoId: this.id |
635 | }) | 640 | }) |
636 | const videoOutputPath = join(videosDirectory, this.getVideoFilename(newVideoFile)) | 641 | const videoOutputPath = join(videosDirectory, this.getVideoFilename(newVideoFile)) |
637 | const resolutionOption = `${resolution}x?` // '720x?' for example | 642 | |
638 | 643 | const transcodeOptions = { | |
639 | return new Promise<void>((res, rej) => { | 644 | inputPath: videoInputPath, |
640 | ffmpeg(videoInputPath) | 645 | outputPath: videoOutputPath, |
641 | .output(videoOutputPath) | 646 | resolution |
642 | .videoCodec('libx264') | 647 | } |
643 | .size(resolutionOption) | 648 | return transcode(transcodeOptions) |
644 | .outputOption('-threads ' + CONFIG.TRANSCODING.THREADS) | 649 | .then(() => { |
645 | .outputOption('-movflags faststart') | 650 | return statPromise(videoOutputPath) |
646 | .on('error', rej) | 651 | }) |
647 | .on('end', () => { | 652 | .then(stats => { |
648 | return statPromise(videoOutputPath) | 653 | newVideoFile.set('size', stats.size) |
649 | .then(stats => { | 654 | |
650 | newVideoFile.set('size', stats.size) | 655 | return undefined |
651 | 656 | }) | |
652 | return undefined | 657 | .then(() => { |
653 | }) | 658 | return this.createTorrentAndSetInfoHash(newVideoFile) |
654 | .then(() => { | 659 | }) |
655 | return this.createTorrentAndSetInfoHash(newVideoFile) | 660 | .then(() => { |
656 | }) | 661 | return newVideoFile.save() |
657 | .then(() => { | 662 | }) |
658 | return newVideoFile.save() | 663 | .then(() => { |
659 | }) | 664 | return this.VideoFiles.push(newVideoFile) |
660 | .then(() => { | 665 | }) |
661 | return this.VideoFiles.push(newVideoFile) | 666 | .then(() => undefined) |
662 | }) | ||
663 | .then(() => { | ||
664 | return res() | ||
665 | }) | ||
666 | .catch(rej) | ||
667 | }) | ||
668 | .run() | ||
669 | }) | ||
670 | } | 667 | } |
671 | 668 | ||
672 | getOriginalFileHeight = function (this: VideoInstance) { | 669 | getOriginalFileHeight = function (this: VideoInstance) { |
673 | const originalFilePath = this.getVideoFilePath(this.getOriginalFile()) | 670 | const originalFilePath = this.getVideoFilePath(this.getOriginalFile()) |
674 | 671 | ||
675 | return new Promise<number>((res, rej) => { | 672 | return getVideoFileHeight(originalFilePath) |
676 | ffmpeg.ffprobe(originalFilePath, (err, metadata) => { | ||
677 | if (err) return rej(err) | ||
678 | |||
679 | const videoStream = metadata.streams.find(s => s.codec_type === 'video') | ||
680 | return res(videoStream.height) | ||
681 | }) | ||
682 | }) | ||
683 | } | 673 | } |
684 | 674 | ||
685 | removeThumbnail = function (this: VideoInstance) { | 675 | removeThumbnail = function (this: VideoInstance) { |
@@ -714,16 +704,6 @@ generateThumbnailFromData = function (video: VideoInstance, thumbnailData: strin | |||
714 | }) | 704 | }) |
715 | } | 705 | } |
716 | 706 | ||
717 | getDurationFromFile = function (videoPath: string) { | ||
718 | return new Promise<number>((res, rej) => { | ||
719 | ffmpeg.ffprobe(videoPath, (err, metadata) => { | ||
720 | if (err) return rej(err) | ||
721 | |||
722 | return res(Math.floor(metadata.format.duration)) | ||
723 | }) | ||
724 | }) | ||
725 | } | ||
726 | |||
727 | list = function () { | 707 | list = function () { |
728 | const query = { | 708 | const query = { |
729 | include: [ Video['sequelize'].models.VideoFile ] | 709 | include: [ Video['sequelize'].models.VideoFile ] |
@@ -964,22 +944,3 @@ function createBaseVideosWhere () { | |||
964 | } | 944 | } |
965 | } | 945 | } |
966 | } | 946 | } |
967 | |||
968 | function generateImage (video: VideoInstance, videoPath: string, folder: string, imageName: string, size: string) { | ||
969 | const options = { | ||
970 | filename: imageName, | ||
971 | count: 1, | ||
972 | folder | ||
973 | } | ||
974 | |||
975 | if (size) { | ||
976 | options['size'] = size | ||
977 | } | ||
978 | |||
979 | return new Promise<string>((res, rej) => { | ||
980 | ffmpeg(videoPath) | ||
981 | .on('error', rej) | ||
982 | .on('end', () => res(imageName)) | ||
983 | .thumbnail(options) | ||
984 | }) | ||
985 | } | ||
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 () { | |||
106 | const file = video.files[0] | 106 | const file = video.files[0] |
107 | const magnetUri = file.magnetUri | 107 | const magnetUri = file.magnetUri |
108 | expect(file.magnetUri).to.have.lengthOf.above(2) | 108 | expect(file.magnetUri).to.have.lengthOf.above(2) |
109 | expect(file.resolution).to.equal(0) | 109 | expect(file.resolution).to.equal(720) |
110 | expect(file.resolutionLabel).to.equal('original') | 110 | expect(file.resolutionLabel).to.equal('720p') |
111 | expect(file.size).to.equal(572456) | 111 | expect(file.size).to.equal(572456) |
112 | 112 | ||
113 | if (server.url !== 'http://localhost:9001') { | 113 | if (server.url !== 'http://localhost:9001') { |
@@ -172,7 +172,7 @@ describe('Test multiple pods', function () { | |||
172 | expect(dateIsValid(video.updatedAt)).to.be.true | 172 | expect(dateIsValid(video.updatedAt)).to.be.true |
173 | expect(video.author).to.equal('root') | 173 | expect(video.author).to.equal('root') |
174 | 174 | ||
175 | expect(video.files).to.have.lengthOf(5) | 175 | expect(video.files).to.have.lengthOf(4) |
176 | 176 | ||
177 | // Check common attributes | 177 | // Check common attributes |
178 | for (const file of video.files) { | 178 | for (const file of video.files) { |
@@ -192,11 +192,6 @@ describe('Test multiple pods', function () { | |||
192 | } | 192 | } |
193 | } | 193 | } |
194 | 194 | ||
195 | const originalFile = video.files.find(f => f.resolution === 0) | ||
196 | expect(originalFile).not.to.be.undefined | ||
197 | expect(originalFile.resolutionLabel).to.equal('original') | ||
198 | expect(originalFile.size).to.be.above(700000).and.below(720000) | ||
199 | |||
200 | const file240p = video.files.find(f => f.resolution === 240) | 195 | const file240p = video.files.find(f => f.resolution === 240) |
201 | expect(file240p).not.to.be.undefined | 196 | expect(file240p).not.to.be.undefined |
202 | expect(file240p.resolutionLabel).to.equal('240p') | 197 | expect(file240p.resolutionLabel).to.equal('240p') |
@@ -215,7 +210,7 @@ describe('Test multiple pods', function () { | |||
215 | const file720p = video.files.find(f => f.resolution === 720) | 210 | const file720p = video.files.find(f => f.resolution === 720) |
216 | expect(file720p).not.to.be.undefined | 211 | expect(file720p).not.to.be.undefined |
217 | expect(file720p.resolutionLabel).to.equal('720p') | 212 | expect(file720p.resolutionLabel).to.equal('720p') |
218 | expect(file720p.size).to.be.above(310000).and.below(320000) | 213 | expect(file720p.size).to.be.above(700000).and.below(7200000) |
219 | 214 | ||
220 | const test = await testVideoImage(server.url, 'video_short2.webm', video.thumbnailPath) | 215 | const test = await testVideoImage(server.url, 'video_short2.webm', video.thumbnailPath) |
221 | expect(test).to.equal(true) | 216 | expect(test).to.equal(true) |
@@ -291,8 +286,8 @@ describe('Test multiple pods', function () { | |||
291 | 286 | ||
292 | const file1 = video1.files[0] | 287 | const file1 = video1.files[0] |
293 | expect(file1.magnetUri).to.have.lengthOf.above(2) | 288 | expect(file1.magnetUri).to.have.lengthOf.above(2) |
294 | expect(file1.resolution).to.equal(0) | 289 | expect(file1.resolution).to.equal(720) |
295 | expect(file1.resolutionLabel).to.equal('original') | 290 | expect(file1.resolutionLabel).to.equal('720p') |
296 | expect(file1.size).to.equal(292677) | 291 | expect(file1.size).to.equal(292677) |
297 | 292 | ||
298 | expect(video2.name).to.equal('my super name for pod 3-2') | 293 | expect(video2.name).to.equal('my super name for pod 3-2') |
@@ -316,8 +311,8 @@ describe('Test multiple pods', function () { | |||
316 | const file2 = video2.files[0] | 311 | const file2 = video2.files[0] |
317 | const magnetUri2 = file2.magnetUri | 312 | const magnetUri2 = file2.magnetUri |
318 | expect(file2.magnetUri).to.have.lengthOf.above(2) | 313 | expect(file2.magnetUri).to.have.lengthOf.above(2) |
319 | expect(file2.resolution).to.equal(0) | 314 | expect(file2.resolution).to.equal(720) |
320 | expect(file2.resolutionLabel).to.equal('original') | 315 | expect(file2.resolutionLabel).to.equal('720p') |
321 | expect(file2.size).to.equal(218910) | 316 | expect(file2.size).to.equal(218910) |
322 | 317 | ||
323 | if (server.url !== 'http://localhost:9003') { | 318 | if (server.url !== 'http://localhost:9003') { |
@@ -402,6 +397,22 @@ describe('Test multiple pods', function () { | |||
402 | expect(torrent.files.length).to.equal(1) | 397 | expect(torrent.files.length).to.equal(1) |
403 | expect(torrent.files[0].path).to.exist.and.to.not.equal('') | 398 | expect(torrent.files[0].path).to.exist.and.to.not.equal('') |
404 | }) | 399 | }) |
400 | |||
401 | it('Should add the file 2 in 360p by asking pod 1', async function () { | ||
402 | // Yes, this could be long | ||
403 | this.timeout(200000) | ||
404 | |||
405 | const res = await getVideosList(servers[0].url) | ||
406 | |||
407 | const video = res.body.data.find(v => v.name === 'my super name for pod 2') | ||
408 | const file = video.files.find(f => f.resolution === 360) | ||
409 | expect(file).not.to.be.undefined | ||
410 | |||
411 | const torrent = await webtorrentAdd(file.magnetUri) | ||
412 | expect(torrent.files).to.be.an('array') | ||
413 | expect(torrent.files.length).to.equal(1) | ||
414 | expect(torrent.files[0].path).to.exist.and.to.not.equal('') | ||
415 | }) | ||
405 | }) | 416 | }) |
406 | 417 | ||
407 | describe('Should update video views, likes and dislikes', function () { | 418 | describe('Should update video views, likes and dislikes', function () { |
@@ -562,8 +573,8 @@ describe('Test multiple pods', function () { | |||
562 | 573 | ||
563 | const file = videoUpdated.files[0] | 574 | const file = videoUpdated.files[0] |
564 | expect(file.magnetUri).to.have.lengthOf.above(2) | 575 | expect(file.magnetUri).to.have.lengthOf.above(2) |
565 | expect(file.resolution).to.equal(0) | 576 | expect(file.resolution).to.equal(720) |
566 | expect(file.resolutionLabel).to.equal('original') | 577 | expect(file.resolutionLabel).to.equal('720p') |
567 | expect(file.size).to.equal(292677) | 578 | expect(file.size).to.equal(292677) |
568 | 579 | ||
569 | const test = await testVideoImage(server.url, 'video_short3.webm', videoUpdated.thumbnailPath) | 580 | 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 () { | |||
127 | const file = video.files[0] | 127 | const file = video.files[0] |
128 | const magnetUri = file.magnetUri | 128 | const magnetUri = file.magnetUri |
129 | expect(file.magnetUri).to.have.lengthOf.above(2) | 129 | expect(file.magnetUri).to.have.lengthOf.above(2) |
130 | expect(file.resolution).to.equal(0) | 130 | expect(file.resolution).to.equal(720) |
131 | expect(file.resolutionLabel).to.equal('original') | 131 | expect(file.resolutionLabel).to.equal('720p') |
132 | expect(file.size).to.equal(218910) | 132 | expect(file.size).to.equal(218910) |
133 | 133 | ||
134 | const test = await testVideoImage(server.url, 'video_short.webm', video.thumbnailPath) | 134 | const test = await testVideoImage(server.url, 'video_short.webm', video.thumbnailPath) |
@@ -170,8 +170,8 @@ describe('Test a single pod', function () { | |||
170 | 170 | ||
171 | const file = video.files[0] | 171 | const file = video.files[0] |
172 | expect(file.magnetUri).to.have.lengthOf.above(2) | 172 | expect(file.magnetUri).to.have.lengthOf.above(2) |
173 | expect(file.resolution).to.equal(0) | 173 | expect(file.resolution).to.equal(720) |
174 | expect(file.resolutionLabel).to.equal('original') | 174 | expect(file.resolutionLabel).to.equal('720p') |
175 | expect(file.size).to.equal(218910) | 175 | expect(file.size).to.equal(218910) |
176 | 176 | ||
177 | const test = await testVideoImage(server.url, 'video_short.webm', video.thumbnailPath) | 177 | const test = await testVideoImage(server.url, 'video_short.webm', video.thumbnailPath) |
@@ -229,8 +229,8 @@ describe('Test a single pod', function () { | |||
229 | 229 | ||
230 | const file = video.files[0] | 230 | const file = video.files[0] |
231 | expect(file.magnetUri).to.have.lengthOf.above(2) | 231 | expect(file.magnetUri).to.have.lengthOf.above(2) |
232 | expect(file.resolution).to.equal(0) | 232 | expect(file.resolution).to.equal(720) |
233 | expect(file.resolutionLabel).to.equal('original') | 233 | expect(file.resolutionLabel).to.equal('720p') |
234 | expect(file.size).to.equal(218910) | 234 | expect(file.size).to.equal(218910) |
235 | 235 | ||
236 | const test = await testVideoImage(server.url, 'video_short.webm', video.thumbnailPath) | 236 | const test = await testVideoImage(server.url, 'video_short.webm', video.thumbnailPath) |
@@ -291,8 +291,8 @@ describe('Test a single pod', function () { | |||
291 | 291 | ||
292 | const file = video.files[0] | 292 | const file = video.files[0] |
293 | expect(file.magnetUri).to.have.lengthOf.above(2) | 293 | expect(file.magnetUri).to.have.lengthOf.above(2) |
294 | expect(file.resolution).to.equal(0) | 294 | expect(file.resolution).to.equal(720) |
295 | expect(file.resolutionLabel).to.equal('original') | 295 | expect(file.resolutionLabel).to.equal('720p') |
296 | expect(file.size).to.equal(218910) | 296 | expect(file.size).to.equal(218910) |
297 | 297 | ||
298 | const test = await testVideoImage(server.url, 'video_short.webm', video.thumbnailPath) | 298 | const test = await testVideoImage(server.url, 'video_short.webm', video.thumbnailPath) |
@@ -569,8 +569,8 @@ describe('Test a single pod', function () { | |||
569 | const file = video.files[0] | 569 | const file = video.files[0] |
570 | const magnetUri = file.magnetUri | 570 | const magnetUri = file.magnetUri |
571 | expect(file.magnetUri).to.have.lengthOf.above(2) | 571 | expect(file.magnetUri).to.have.lengthOf.above(2) |
572 | expect(file.resolution).to.equal(0) | 572 | expect(file.resolution).to.equal(720) |
573 | expect(file.resolutionLabel).to.equal('original') | 573 | expect(file.resolutionLabel).to.equal('720p') |
574 | expect(file.size).to.equal(292677) | 574 | expect(file.size).to.equal(292677) |
575 | 575 | ||
576 | const test = await testVideoImage(server.url, 'video_short3.webm', video.thumbnailPath) | 576 | const test = await testVideoImage(server.url, 'video_short3.webm', video.thumbnailPath) |
@@ -612,8 +612,8 @@ describe('Test a single pod', function () { | |||
612 | 612 | ||
613 | const file = video.files[0] | 613 | const file = video.files[0] |
614 | expect(file.magnetUri).to.have.lengthOf.above(2) | 614 | expect(file.magnetUri).to.have.lengthOf.above(2) |
615 | expect(file.resolution).to.equal(0) | 615 | expect(file.resolution).to.equal(720) |
616 | expect(file.resolutionLabel).to.equal('original') | 616 | expect(file.resolutionLabel).to.equal('720p') |
617 | expect(file.size).to.equal(292677) | 617 | expect(file.size).to.equal(292677) |
618 | }) | 618 | }) |
619 | 619 | ||
@@ -647,8 +647,8 @@ describe('Test a single pod', function () { | |||
647 | 647 | ||
648 | const file = video.files[0] | 648 | const file = video.files[0] |
649 | expect(file.magnetUri).to.have.lengthOf.above(2) | 649 | expect(file.magnetUri).to.have.lengthOf.above(2) |
650 | expect(file.resolution).to.equal(0) | 650 | expect(file.resolution).to.equal(720) |
651 | expect(file.resolutionLabel).to.equal('original') | 651 | expect(file.resolutionLabel).to.equal('720p') |
652 | expect(file.size).to.equal(292677) | 652 | expect(file.size).to.equal(292677) |
653 | }) | 653 | }) |
654 | 654 | ||
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 () { | |||
68 | const res = await getVideosList(servers[1].url) | 68 | const res = await getVideosList(servers[1].url) |
69 | 69 | ||
70 | const video = res.body.data[0] | 70 | const video = res.body.data[0] |
71 | expect(video.files).to.have.lengthOf(5) | 71 | expect(video.files).to.have.lengthOf(4) |
72 | 72 | ||
73 | const magnetUri = video.files[0].magnetUri | 73 | const magnetUri = video.files[0].magnetUri |
74 | expect(magnetUri).to.match(/\.mp4/) | 74 | 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 () { | |||
55 | expect(videos).to.have.lengthOf(2) | 55 | expect(videos).to.have.lengthOf(2) |
56 | 56 | ||
57 | for (const video of videos) { | 57 | for (const video of videos) { |
58 | expect(video.files).to.have.lengthOf(5) | 58 | expect(video.files).to.have.lengthOf(4) |
59 | 59 | ||
60 | for (const file of video.files) { | 60 | for (const file of video.files) { |
61 | expect(file.magnetUri).to.contain('localhost%3A9002%2Ftracker%2Fsocket') | 61 | expect(file.magnetUri).to.contain('localhost%3A9002%2Ftracker%2Fsocket') |
62 | expect(file.magnetUri).to.contain('localhost%3A9002%2Fstatic%2Fwebseed%2F') | 62 | expect(file.magnetUri).to.contain('localhost%3A9002%2Fstatic%2Fwebseed%2F') |
63 | 63 | ||
64 | const torrent = await parseTorrentVideo(server, video.uuid, file.resolutionLabel) | 64 | const torrent = await parseTorrentVideo(server, video.uuid, file.resolution) |
65 | expect(torrent.announce[0]).to.equal('ws://localhost:9002/tracker/socket') | 65 | expect(torrent.announce[0]).to.equal('ws://localhost:9002/tracker/socket') |
66 | expect(torrent.urlList[0]).to.contain('http://localhost:9002/static/webseed') | 66 | expect(torrent.urlList[0]).to.contain('http://localhost:9002/static/webseed') |
67 | } | 67 | } |
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 | |||
196 | req.field('tags[' + i + ']', attributes.tags[i]) | 196 | req.field('tags[' + i + ']', attributes.tags[i]) |
197 | } | 197 | } |
198 | 198 | ||
199 | let filepath = '' | 199 | let filePath = '' |
200 | if (isAbsolute(attributes.fixture)) { | 200 | if (isAbsolute(attributes.fixture)) { |
201 | filepath = attributes.fixture | 201 | filePath = attributes.fixture |
202 | } else { | 202 | } else { |
203 | filepath = join(__dirname, '..', 'api', 'fixtures', attributes.fixture) | 203 | filePath = join(__dirname, '..', 'api', 'fixtures', attributes.fixture) |
204 | } | 204 | } |
205 | 205 | ||
206 | return req.attach('videofile', filepath) | 206 | return req.attach('videofile', filePath) |
207 | .expect(specialStatus) | 207 | .expect(specialStatus) |
208 | } | 208 | } |
209 | 209 | ||
@@ -238,9 +238,9 @@ function rateVideo (url: string, accessToken: string, id: number, rating: string | |||
238 | .expect(specialStatus) | 238 | .expect(specialStatus) |
239 | } | 239 | } |
240 | 240 | ||
241 | function parseTorrentVideo (server: ServerInfo, videoUUID: string, resolutionLabel: string) { | 241 | function parseTorrentVideo (server: ServerInfo, videoUUID: string, resolution: number) { |
242 | return new Promise<any>((res, rej) => { | 242 | return new Promise<any>((res, rej) => { |
243 | const torrentName = videoUUID + '-' + resolutionLabel + '.torrent' | 243 | const torrentName = videoUUID + '-' + resolution + '.torrent' |
244 | const torrentPath = join(__dirname, '..', '..', '..', 'test' + server.serverNumber, 'torrents', torrentName) | 244 | const torrentPath = join(__dirname, '..', '..', '..', 'test' + server.serverNumber, 'torrents', torrentName) |
245 | readFile(torrentPath, (err, data) => { | 245 | readFile(torrentPath, (err, data) => { |
246 | if (err) return rej(err) | 246 | 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 @@ | |||
1 | export enum VideoResolution { | 1 | export enum VideoResolution { |
2 | ORIGINAL = 0, | ||
3 | H_240P = 240, | 2 | H_240P = 240, |
4 | H_360P = 360, | 3 | H_360P = 360, |
5 | H_480P = 480, | 4 | H_480P = 480, |