diff options
-rw-r--r-- | server/helpers/custom-validators/activitypub/videos.ts | 14 | ||||
-rw-r--r-- | server/helpers/ffmpeg-utils.ts | 2 | ||||
-rw-r--r-- | server/lib/activitypub/videos.ts | 26 | ||||
-rw-r--r-- | server/lib/video-transcoding.ts | 3 | ||||
-rw-r--r-- | server/models/video/video-file.ts | 79 | ||||
-rw-r--r-- | server/models/video/video-format-utils.ts | 4 | ||||
-rw-r--r-- | server/models/video/video.ts | 3 | ||||
-rw-r--r-- | server/tests/api/videos/video-transcoder.ts | 70 |
8 files changed, 102 insertions, 99 deletions
diff --git a/server/helpers/custom-validators/activitypub/videos.ts b/server/helpers/custom-validators/activitypub/videos.ts index af8c8a0c8..876cc7f50 100644 --- a/server/helpers/custom-validators/activitypub/videos.ts +++ b/server/helpers/custom-validators/activitypub/videos.ts | |||
@@ -13,6 +13,7 @@ import { | |||
13 | import { isActivityPubUrlValid, isBaseActivityValid, setValidAttributedTo } from './misc' | 13 | import { isActivityPubUrlValid, isBaseActivityValid, setValidAttributedTo } from './misc' |
14 | import { VideoState } from '../../../../shared/models/videos' | 14 | import { VideoState } from '../../../../shared/models/videos' |
15 | import { logger } from '@server/helpers/logger' | 15 | import { logger } from '@server/helpers/logger' |
16 | import { ActivityVideoFileMetadataObject } from '@shared/models' | ||
16 | 17 | ||
17 | function sanitizeAndCheckVideoTorrentUpdateActivity (activity: any) { | 18 | function sanitizeAndCheckVideoTorrentUpdateActivity (activity: any) { |
18 | return isBaseActivityValid(activity, 'Update') && | 19 | return isBaseActivityValid(activity, 'Update') && |
@@ -104,7 +105,15 @@ function isRemoteVideoUrlValid (url: any) { | |||
104 | (url.mediaType || url.mimeType) === 'application/x-mpegURL' && | 105 | (url.mediaType || url.mimeType) === 'application/x-mpegURL' && |
105 | isActivityPubUrlValid(url.href) && | 106 | isActivityPubUrlValid(url.href) && |
106 | isArray(url.tag) | 107 | isArray(url.tag) |
107 | ) | 108 | ) || |
109 | isAPVideoFileMetadataObject(url) | ||
110 | } | ||
111 | |||
112 | function isAPVideoFileMetadataObject (url: any): url is ActivityVideoFileMetadataObject { | ||
113 | return url && | ||
114 | url.type === 'Link' && | ||
115 | url.mediaType === 'application/json' && | ||
116 | isArray(url.rel) && url.rel.includes('metadata') | ||
108 | } | 117 | } |
109 | 118 | ||
110 | // --------------------------------------------------------------------------- | 119 | // --------------------------------------------------------------------------- |
@@ -113,7 +122,8 @@ export { | |||
113 | sanitizeAndCheckVideoTorrentUpdateActivity, | 122 | sanitizeAndCheckVideoTorrentUpdateActivity, |
114 | isRemoteStringIdentifierValid, | 123 | isRemoteStringIdentifierValid, |
115 | sanitizeAndCheckVideoTorrentObject, | 124 | sanitizeAndCheckVideoTorrentObject, |
116 | isRemoteVideoUrlValid | 125 | isRemoteVideoUrlValid, |
126 | isAPVideoFileMetadataObject | ||
117 | } | 127 | } |
118 | 128 | ||
119 | // --------------------------------------------------------------------------- | 129 | // --------------------------------------------------------------------------- |
diff --git a/server/helpers/ffmpeg-utils.ts b/server/helpers/ffmpeg-utils.ts index 5ee295635..e0e408ea0 100644 --- a/server/helpers/ffmpeg-utils.ts +++ b/server/helpers/ffmpeg-utils.ts | |||
@@ -170,7 +170,7 @@ async function getVideoFileFPS (path: string) { | |||
170 | return 0 | 170 | return 0 |
171 | } | 171 | } |
172 | 172 | ||
173 | async function getMetadataFromFile<T> (path: string, cb = metadata => metadata) { | 173 | async function getMetadataFromFile <T> (path: string, cb = metadata => metadata) { |
174 | return new Promise<T>((res, rej) => { | 174 | return new Promise<T>((res, rej) => { |
175 | ffmpeg.ffprobe(path, (err, metadata) => { | 175 | ffmpeg.ffprobe(path, (err, metadata) => { |
176 | if (err) return rej(err) | 176 | if (err) return rej(err) |
diff --git a/server/lib/activitypub/videos.ts b/server/lib/activitypub/videos.ts index 30de4714c..452e43c8c 100644 --- a/server/lib/activitypub/videos.ts +++ b/server/lib/activitypub/videos.ts | |||
@@ -9,13 +9,13 @@ import { | |||
9 | ActivityPlaylistUrlObject, | 9 | ActivityPlaylistUrlObject, |
10 | ActivityTagObject, | 10 | ActivityTagObject, |
11 | ActivityUrlObject, | 11 | ActivityUrlObject, |
12 | ActivityVideoFileMetadataObject, | ||
12 | ActivityVideoUrlObject, | 13 | ActivityVideoUrlObject, |
13 | VideoState, | 14 | VideoState |
14 | ActivityVideoFileMetadataObject | ||
15 | } from '../../../shared/index' | 15 | } from '../../../shared/index' |
16 | import { VideoTorrentObject } from '../../../shared/models/activitypub/objects' | 16 | import { VideoTorrentObject } from '../../../shared/models/activitypub/objects' |
17 | import { VideoPrivacy } from '../../../shared/models/videos' | 17 | import { VideoPrivacy } from '../../../shared/models/videos' |
18 | import { sanitizeAndCheckVideoTorrentObject } from '../../helpers/custom-validators/activitypub/videos' | 18 | import { sanitizeAndCheckVideoTorrentObject, isAPVideoFileMetadataObject } from '../../helpers/custom-validators/activitypub/videos' |
19 | import { isVideoFileInfoHashValid } from '../../helpers/custom-validators/videos' | 19 | import { isVideoFileInfoHashValid } from '../../helpers/custom-validators/videos' |
20 | import { deleteNonExistingModels, resetSequelizeInstance, retryTransactionWrapper } from '../../helpers/database-utils' | 20 | import { deleteNonExistingModels, resetSequelizeInstance, retryTransactionWrapper } from '../../helpers/database-utils' |
21 | import { logger } from '../../helpers/logger' | 21 | import { logger } from '../../helpers/logger' |
@@ -26,7 +26,8 @@ import { | |||
26 | P2P_MEDIA_LOADER_PEER_VERSION, | 26 | P2P_MEDIA_LOADER_PEER_VERSION, |
27 | PREVIEWS_SIZE, | 27 | PREVIEWS_SIZE, |
28 | REMOTE_SCHEME, | 28 | REMOTE_SCHEME, |
29 | STATIC_PATHS, THUMBNAILS_SIZE | 29 | STATIC_PATHS, |
30 | THUMBNAILS_SIZE | ||
30 | } from '../../initializers/constants' | 31 | } from '../../initializers/constants' |
31 | import { TagModel } from '../../models/video/tag' | 32 | import { TagModel } from '../../models/video/tag' |
32 | import { VideoModel } from '../../models/video/video' | 33 | import { VideoModel } from '../../models/video/video' |
@@ -69,7 +70,8 @@ import { | |||
69 | MVideoAPWithoutCaption, | 70 | MVideoAPWithoutCaption, |
70 | MVideoFile, | 71 | MVideoFile, |
71 | MVideoFullLight, | 72 | MVideoFullLight, |
72 | MVideoId, MVideoImmutable, | 73 | MVideoId, |
74 | MVideoImmutable, | ||
73 | MVideoThumbnail | 75 | MVideoThumbnail |
74 | } from '../../typings/models' | 76 | } from '../../typings/models' |
75 | import { MThumbnail } from '../../typings/models/video/thumbnail' | 77 | import { MThumbnail } from '../../typings/models/video/thumbnail' |
@@ -527,10 +529,6 @@ function isAPHashTagObject (url: any): url is ActivityHashTagObject { | |||
527 | return url && url.type === 'Hashtag' | 529 | return url && url.type === 'Hashtag' |
528 | } | 530 | } |
529 | 531 | ||
530 | function isAPVideoFileMetadataObject (url: any): url is ActivityVideoFileMetadataObject { | ||
531 | return url && url.type === 'Link' && url.mediaType === 'application/json' && url.hasAttribute('rel') && url.rel.includes('metadata') | ||
532 | } | ||
533 | |||
534 | async function createVideo (videoObject: VideoTorrentObject, channel: MChannelAccountLight, waitThumbnail = false) { | 532 | async function createVideo (videoObject: VideoTorrentObject, channel: MChannelAccountLight, waitThumbnail = false) { |
535 | logger.debug('Adding remote video %s.', videoObject.id) | 533 | logger.debug('Adding remote video %s.', videoObject.id) |
536 | 534 | ||
@@ -701,11 +699,11 @@ function videoFileActivityUrlToDBAttributes ( | |||
701 | 699 | ||
702 | // Fetch associated metadata url, if any | 700 | // Fetch associated metadata url, if any |
703 | const metadata = urls.filter(isAPVideoFileMetadataObject) | 701 | const metadata = urls.filter(isAPVideoFileMetadataObject) |
704 | .find(u => | 702 | .find(u => { |
705 | u.height === fileUrl.height && | 703 | return u.height === fileUrl.height && |
706 | u.fps === fileUrl.fps && | 704 | u.fps === fileUrl.fps && |
707 | u.rel.includes(fileUrl.mediaType) | 705 | u.rel.includes(fileUrl.mediaType) |
708 | ) | 706 | }) |
709 | 707 | ||
710 | const mediaType = fileUrl.mediaType | 708 | const mediaType = fileUrl.mediaType |
711 | const attribute = { | 709 | const attribute = { |
diff --git a/server/lib/video-transcoding.ts b/server/lib/video-transcoding.ts index 444b0d954..bde4c2633 100644 --- a/server/lib/video-transcoding.ts +++ b/server/lib/video-transcoding.ts | |||
@@ -237,12 +237,9 @@ async function onVideoFileTranscoding (video: MVideoWithFile, videoFile: MVideoF | |||
237 | 237 | ||
238 | await move(transcodingPath, outputPath) | 238 | await move(transcodingPath, outputPath) |
239 | 239 | ||
240 | const extractedVideo = extractVideo(video) | ||
241 | |||
242 | videoFile.size = stats.size | 240 | videoFile.size = stats.size |
243 | videoFile.fps = fps | 241 | videoFile.fps = fps |
244 | videoFile.metadata = metadata | 242 | videoFile.metadata = metadata |
245 | videoFile.metadataUrl = extractedVideo.getVideoFileMetadataUrl(videoFile, extractedVideo.getBaseUrls().baseUrlHttp) | ||
246 | 243 | ||
247 | await createTorrentAndSetInfoHash(video, videoFile) | 244 | await createTorrentAndSetInfoHash(video, videoFile) |
248 | 245 | ||
diff --git a/server/models/video/video-file.ts b/server/models/video/video-file.ts index 029468004..201f0c0f1 100644 --- a/server/models/video/video-file.ts +++ b/server/models/video/video-file.ts | |||
@@ -30,18 +30,16 @@ import { MIMETYPES, MEMOIZE_LENGTH, MEMOIZE_TTL } from '../../initializers/const | |||
30 | import { MVideoFile, MVideoFileStreamingPlaylistVideo, MVideoFileVideo } from '../../typings/models/video/video-file' | 30 | import { MVideoFile, MVideoFileStreamingPlaylistVideo, MVideoFileVideo } from '../../typings/models/video/video-file' |
31 | import { MStreamingPlaylistVideo, MVideo } from '@server/typings/models' | 31 | import { MStreamingPlaylistVideo, MVideo } from '@server/typings/models' |
32 | import * as memoizee from 'memoizee' | 32 | import * as memoizee from 'memoizee' |
33 | import validator from 'validator' | ||
33 | 34 | ||
34 | export enum ScopeNames { | 35 | export enum ScopeNames { |
35 | WITH_VIDEO = 'WITH_VIDEO', | 36 | WITH_VIDEO = 'WITH_VIDEO', |
36 | WITH_VIDEO_OR_PLAYLIST = 'WITH_VIDEO_OR_PLAYLIST', | ||
37 | WITH_METADATA = 'WITH_METADATA' | 37 | WITH_METADATA = 'WITH_METADATA' |
38 | } | 38 | } |
39 | 39 | ||
40 | const METADATA_FIELDS = [ 'metadata', 'metadataUrl' ] | ||
41 | |||
42 | @DefaultScope(() => ({ | 40 | @DefaultScope(() => ({ |
43 | attributes: { | 41 | attributes: { |
44 | exclude: [ METADATA_FIELDS[0] ] | 42 | exclude: [ 'metadata' ] |
45 | } | 43 | } |
46 | })) | 44 | })) |
47 | @Scopes(() => ({ | 45 | @Scopes(() => ({ |
@@ -53,35 +51,9 @@ const METADATA_FIELDS = [ 'metadata', 'metadataUrl' ] | |||
53 | } | 51 | } |
54 | ] | 52 | ] |
55 | }, | 53 | }, |
56 | [ScopeNames.WITH_VIDEO_OR_PLAYLIST]: (videoIdOrUUID: string | number) => { | ||
57 | const where = (typeof videoIdOrUUID === 'number') | ||
58 | ? { id: videoIdOrUUID } | ||
59 | : { uuid: videoIdOrUUID } | ||
60 | |||
61 | return { | ||
62 | include: [ | ||
63 | { | ||
64 | model: VideoModel.unscoped(), | ||
65 | required: false, | ||
66 | where | ||
67 | }, | ||
68 | { | ||
69 | model: VideoStreamingPlaylistModel.unscoped(), | ||
70 | required: false, | ||
71 | include: [ | ||
72 | { | ||
73 | model: VideoModel.unscoped(), | ||
74 | required: true, | ||
75 | where | ||
76 | } | ||
77 | ] | ||
78 | } | ||
79 | ] | ||
80 | } | ||
81 | }, | ||
82 | [ScopeNames.WITH_METADATA]: { | 54 | [ScopeNames.WITH_METADATA]: { |
83 | attributes: { | 55 | attributes: { |
84 | include: METADATA_FIELDS | 56 | include: [ 'metadata' ] |
85 | } | 57 | } |
86 | } | 58 | } |
87 | })) | 59 | })) |
@@ -223,10 +195,8 @@ export class VideoFileModel extends Model<VideoFileModel> { | |||
223 | 195 | ||
224 | static async doesVideoExistForVideoFile (id: number, videoIdOrUUID: number | string) { | 196 | static async doesVideoExistForVideoFile (id: number, videoIdOrUUID: number | string) { |
225 | const videoFile = await VideoFileModel.loadWithVideoOrPlaylist(id, videoIdOrUUID) | 197 | const videoFile = await VideoFileModel.loadWithVideoOrPlaylist(id, videoIdOrUUID) |
226 | return (videoFile?.Video.id === videoIdOrUUID) || | 198 | |
227 | (videoFile?.Video.uuid === videoIdOrUUID) || | 199 | return !!videoFile |
228 | (videoFile?.VideoStreamingPlaylist?.Video?.id === videoIdOrUUID) || | ||
229 | (videoFile?.VideoStreamingPlaylist?.Video?.uuid === videoIdOrUUID) | ||
230 | } | 200 | } |
231 | 201 | ||
232 | static loadWithMetadata (id: number) { | 202 | static loadWithMetadata (id: number) { |
@@ -238,12 +208,41 @@ export class VideoFileModel extends Model<VideoFileModel> { | |||
238 | } | 208 | } |
239 | 209 | ||
240 | static loadWithVideoOrPlaylist (id: number, videoIdOrUUID: number | string) { | 210 | static loadWithVideoOrPlaylist (id: number, videoIdOrUUID: number | string) { |
241 | return VideoFileModel.scope({ | 211 | const whereVideo = validator.isUUID(videoIdOrUUID + '') |
242 | method: [ | 212 | ? { uuid: videoIdOrUUID } |
243 | ScopeNames.WITH_VIDEO_OR_PLAYLIST, | 213 | : { id: videoIdOrUUID } |
244 | videoIdOrUUID | 214 | |
215 | const options = { | ||
216 | where: { | ||
217 | id | ||
218 | }, | ||
219 | include: [ | ||
220 | { | ||
221 | model: VideoModel.unscoped(), | ||
222 | required: false, | ||
223 | where: whereVideo | ||
224 | }, | ||
225 | { | ||
226 | model: VideoStreamingPlaylistModel.unscoped(), | ||
227 | required: false, | ||
228 | include: [ | ||
229 | { | ||
230 | model: VideoModel.unscoped(), | ||
231 | required: true, | ||
232 | where: whereVideo | ||
233 | } | ||
234 | ] | ||
235 | } | ||
245 | ] | 236 | ] |
246 | }).findByPk(id) | 237 | } |
238 | |||
239 | return VideoFileModel.findOne(options) | ||
240 | .then(file => { | ||
241 | // We used `required: false` so check we have at least a video or a streaming playlist | ||
242 | if (!file.Video && !file.VideoStreamingPlaylist) return null | ||
243 | |||
244 | return file | ||
245 | }) | ||
247 | } | 246 | } |
248 | 247 | ||
249 | static listByStreamingPlaylist (streamingPlaylistId: number, transaction: Transaction) { | 248 | static listByStreamingPlaylist (streamingPlaylistId: number, transaction: Transaction) { |
diff --git a/server/models/video/video-format-utils.ts b/server/models/video/video-format-utils.ts index 21f0e0a68..365c9581e 100644 --- a/server/models/video/video-format-utils.ts +++ b/server/models/video/video-format-utils.ts | |||
@@ -181,6 +181,8 @@ function videoFilesModelToFormattedJSON ( | |||
181 | baseUrlWs: string, | 181 | baseUrlWs: string, |
182 | videoFiles: MVideoFileRedundanciesOpt[] | 182 | videoFiles: MVideoFileRedundanciesOpt[] |
183 | ): VideoFile[] { | 183 | ): VideoFile[] { |
184 | const video = extractVideo(model) | ||
185 | |||
184 | return videoFiles | 186 | return videoFiles |
185 | .map(videoFile => { | 187 | .map(videoFile => { |
186 | return { | 188 | return { |
@@ -195,7 +197,7 @@ function videoFilesModelToFormattedJSON ( | |||
195 | torrentDownloadUrl: model.getTorrentDownloadUrl(videoFile, baseUrlHttp), | 197 | torrentDownloadUrl: model.getTorrentDownloadUrl(videoFile, baseUrlHttp), |
196 | fileUrl: model.getVideoFileUrl(videoFile, baseUrlHttp), | 198 | fileUrl: model.getVideoFileUrl(videoFile, baseUrlHttp), |
197 | fileDownloadUrl: model.getVideoFileDownloadUrl(videoFile, baseUrlHttp), | 199 | fileDownloadUrl: model.getVideoFileDownloadUrl(videoFile, baseUrlHttp), |
198 | metadataUrl: videoFile.metadataUrl // only send the metadataUrl and not the metadata over the wire | 200 | metadataUrl: video.getVideoFileMetadataUrl(videoFile, baseUrlHttp) |
199 | } as VideoFile | 201 | } as VideoFile |
200 | }) | 202 | }) |
201 | .sort((a, b) => { | 203 | .sort((a, b) => { |
diff --git a/server/models/video/video.ts b/server/models/video/video.ts index 5e4b7d44c..958a49e65 100644 --- a/server/models/video/video.ts +++ b/server/models/video/video.ts | |||
@@ -1849,7 +1849,8 @@ export class VideoModel extends Model<VideoModel> { | |||
1849 | 1849 | ||
1850 | getVideoFileMetadataUrl (videoFile: MVideoFile, baseUrlHttp: string) { | 1850 | getVideoFileMetadataUrl (videoFile: MVideoFile, baseUrlHttp: string) { |
1851 | const path = '/api/v1/videos/' | 1851 | const path = '/api/v1/videos/' |
1852 | return videoFile.metadata | 1852 | |
1853 | return this.isOwned() | ||
1853 | ? baseUrlHttp + path + this.uuid + '/metadata/' + videoFile.id | 1854 | ? baseUrlHttp + path + this.uuid + '/metadata/' + videoFile.id |
1854 | : videoFile.metadataUrl | 1855 | : videoFile.metadataUrl |
1855 | } | 1856 | } |
diff --git a/server/tests/api/videos/video-transcoder.ts b/server/tests/api/videos/video-transcoder.ts index ce0dd14d5..13b3530b1 100644 --- a/server/tests/api/videos/video-transcoder.ts +++ b/server/tests/api/videos/video-transcoder.ts | |||
@@ -27,13 +27,14 @@ import { | |||
27 | root, | 27 | root, |
28 | ServerInfo, | 28 | ServerInfo, |
29 | setAccessTokensToServers, | 29 | setAccessTokensToServers, |
30 | uploadVideo, | 30 | uploadVideo, uploadVideoAndGetId, |
31 | waitJobs, | 31 | waitJobs, |
32 | webtorrentAdd | 32 | webtorrentAdd |
33 | } from '../../../../shared/extra-utils' | 33 | } from '../../../../shared/extra-utils' |
34 | import { join } from 'path' | 34 | import { join } from 'path' |
35 | import { VIDEO_TRANSCODING_FPS } from '../../../../server/initializers/constants' | 35 | import { VIDEO_TRANSCODING_FPS } from '../../../../server/initializers/constants' |
36 | import { FfprobeData } from 'fluent-ffmpeg' | 36 | import { FfprobeData } from 'fluent-ffmpeg' |
37 | import { VideoFileMetadata } from '@shared/models/videos/video-file-metadata' | ||
37 | 38 | ||
38 | const expect = chai.expect | 39 | const expect = chai.expect |
39 | 40 | ||
@@ -470,61 +471,56 @@ describe('Test video transcoding', function () { | |||
470 | it('Should provide valid ffprobe data', async function () { | 471 | it('Should provide valid ffprobe data', async function () { |
471 | this.timeout(160000) | 472 | this.timeout(160000) |
472 | 473 | ||
473 | const videoAttributes = { | 474 | const videoUUID = (await uploadVideoAndGetId({ server: servers[1], videoName: 'ffprobe data' })).uuid |
474 | name: 'my super name for server 1', | ||
475 | description: 'my super description for server 1', | ||
476 | fixture: 'video_short.webm' | ||
477 | } | ||
478 | await uploadVideo(servers[1].url, servers[1].accessToken, videoAttributes) | ||
479 | |||
480 | await waitJobs(servers) | 475 | await waitJobs(servers) |
481 | 476 | ||
482 | const res = await getVideosList(servers[1].url) | 477 | { |
478 | const path = join(root(), 'test' + servers[1].internalServerNumber, 'videos', videoUUID + '-240.mp4') | ||
479 | const metadata = await getMetadataFromFile<VideoFileMetadata>(path) | ||
483 | 480 | ||
484 | const videoOnOrigin = res.body.data.find(v => v.name === videoAttributes.name) | 481 | // expected format properties |
485 | const res2 = await getVideo(servers[1].url, videoOnOrigin.id) | 482 | for (const p of [ |
486 | const videoOnOriginDetails: VideoDetails = res2.body | 483 | 'tags.encoder', |
484 | 'format_long_name', | ||
485 | 'size', | ||
486 | 'bit_rate' | ||
487 | ]) { | ||
488 | expect(metadata.format).to.have.nested.property(p) | ||
489 | } | ||
487 | 490 | ||
488 | { | 491 | // expected stream properties |
489 | const path = join(root(), 'test' + servers[1].internalServerNumber, 'videos', videoOnOrigin.uuid + '-240.mp4') | ||
490 | const metadata = await getMetadataFromFile(path) | ||
491 | for (const p of [ | 492 | for (const p of [ |
492 | // expected format properties | 493 | 'codec_long_name', |
493 | 'format.encoder', | 494 | 'profile', |
494 | 'format.format_long_name', | 495 | 'width', |
495 | 'format.size', | 496 | 'height', |
496 | 'format.bit_rate', | 497 | 'display_aspect_ratio', |
497 | // expected stream properties | 498 | 'avg_frame_rate', |
498 | 'stream[0].codec_long_name', | 499 | 'pix_fmt' |
499 | 'stream[0].profile', | ||
500 | 'stream[0].width', | ||
501 | 'stream[0].height', | ||
502 | 'stream[0].display_aspect_ratio', | ||
503 | 'stream[0].avg_frame_rate', | ||
504 | 'stream[0].pix_fmt' | ||
505 | ]) { | 500 | ]) { |
506 | expect(metadata).to.have.nested.property(p) | 501 | expect(metadata.streams[0]).to.have.nested.property(p) |
507 | } | 502 | } |
503 | |||
508 | expect(metadata).to.not.have.nested.property('format.filename') | 504 | expect(metadata).to.not.have.nested.property('format.filename') |
509 | } | 505 | } |
510 | 506 | ||
511 | for (const server of servers) { | 507 | for (const server of servers) { |
512 | const res = await getVideosList(server.url) | 508 | const res2 = await getVideo(server.url, videoUUID) |
513 | 509 | const videoDetails: VideoDetails = res2.body | |
514 | const video = res.body.data.find(v => v.name === videoAttributes.name) | ||
515 | const res2 = await getVideo(server.url, video.id) | ||
516 | const videoDetails = res2.body | ||
517 | 510 | ||
518 | const videoFiles = videoDetails.files | 511 | const videoFiles = videoDetails.files |
519 | for (const [ index, file ] of videoFiles.entries()) { | 512 | .concat(videoDetails.streamingPlaylists[0].files) |
513 | expect(videoFiles).to.have.lengthOf(8) | ||
514 | |||
515 | for (const file of videoFiles) { | ||
520 | expect(file.metadata).to.be.undefined | 516 | expect(file.metadata).to.be.undefined |
517 | expect(file.metadataUrl).to.exist | ||
521 | expect(file.metadataUrl).to.contain(servers[1].url) | 518 | expect(file.metadataUrl).to.contain(servers[1].url) |
522 | expect(file.metadataUrl).to.contain(videoOnOrigin.uuid) | 519 | expect(file.metadataUrl).to.contain(videoUUID) |
523 | 520 | ||
524 | const res3 = await getVideoFileMetadataUrl(file.metadataUrl) | 521 | const res3 = await getVideoFileMetadataUrl(file.metadataUrl) |
525 | const metadata: FfprobeData = res3.body | 522 | const metadata: FfprobeData = res3.body |
526 | expect(metadata).to.have.nested.property('format.size') | 523 | expect(metadata).to.have.nested.property('format.size') |
527 | expect(metadata.format.size).to.equal(videoOnOriginDetails.files[index].metadata.format.size) | ||
528 | } | 524 | } |
529 | } | 525 | } |
530 | }) | 526 | }) |