aboutsummaryrefslogtreecommitdiffhomepage
path: root/shared/extra-utils/videos
diff options
context:
space:
mode:
Diffstat (limited to 'shared/extra-utils/videos')
-rw-r--r--shared/extra-utils/videos/services.ts23
-rw-r--r--shared/extra-utils/videos/video-abuses.ts65
-rw-r--r--shared/extra-utils/videos/video-blacklist.ts72
-rw-r--r--shared/extra-utils/videos/video-captions.ts71
-rw-r--r--shared/extra-utils/videos/video-change-ownership.ts54
-rw-r--r--shared/extra-utils/videos/video-channels.ts134
-rw-r--r--shared/extra-utils/videos/video-comments.ts87
-rw-r--r--shared/extra-utils/videos/video-history.ts39
-rw-r--r--shared/extra-utils/videos/video-imports.ts57
-rw-r--r--shared/extra-utils/videos/video-playlists.ts318
-rw-r--r--shared/extra-utils/videos/video-streaming-playlists.ts51
-rw-r--r--shared/extra-utils/videos/videos.ts648
12 files changed, 1619 insertions, 0 deletions
diff --git a/shared/extra-utils/videos/services.ts b/shared/extra-utils/videos/services.ts
new file mode 100644
index 000000000..1a53dd4cf
--- /dev/null
+++ b/shared/extra-utils/videos/services.ts
@@ -0,0 +1,23 @@
1import * as request from 'supertest'
2
3function getOEmbed (url: string, oembedUrl: string, format?: string, maxHeight?: number, maxWidth?: number) {
4 const path = '/services/oembed'
5 const query = {
6 url: oembedUrl,
7 format,
8 maxheight: maxHeight,
9 maxwidth: maxWidth
10 }
11
12 return request(url)
13 .get(path)
14 .query(query)
15 .set('Accept', 'application/json')
16 .expect(200)
17}
18
19// ---------------------------------------------------------------------------
20
21export {
22 getOEmbed
23}
diff --git a/shared/extra-utils/videos/video-abuses.ts b/shared/extra-utils/videos/video-abuses.ts
new file mode 100644
index 000000000..7f011ec0f
--- /dev/null
+++ b/shared/extra-utils/videos/video-abuses.ts
@@ -0,0 +1,65 @@
1import * as request from 'supertest'
2import { VideoAbuseUpdate } from '../../models/videos/abuse/video-abuse-update.model'
3import { makeDeleteRequest, makePutBodyRequest } from '../requests/requests'
4
5function reportVideoAbuse (url: string, token: string, videoId: number | string, reason: string, specialStatus = 200) {
6 const path = '/api/v1/videos/' + videoId + '/abuse'
7
8 return request(url)
9 .post(path)
10 .set('Accept', 'application/json')
11 .set('Authorization', 'Bearer ' + token)
12 .send({ reason })
13 .expect(specialStatus)
14}
15
16function getVideoAbusesList (url: string, token: string) {
17 const path = '/api/v1/videos/abuse'
18
19 return request(url)
20 .get(path)
21 .query({ sort: 'createdAt' })
22 .set('Accept', 'application/json')
23 .set('Authorization', 'Bearer ' + token)
24 .expect(200)
25 .expect('Content-Type', /json/)
26}
27
28function updateVideoAbuse (
29 url: string,
30 token: string,
31 videoId: string | number,
32 videoAbuseId: number,
33 body: VideoAbuseUpdate,
34 statusCodeExpected = 204
35) {
36 const path = '/api/v1/videos/' + videoId + '/abuse/' + videoAbuseId
37
38 return makePutBodyRequest({
39 url,
40 token,
41 path,
42 fields: body,
43 statusCodeExpected
44 })
45}
46
47function deleteVideoAbuse (url: string, token: string, videoId: string | number, videoAbuseId: number, statusCodeExpected = 204) {
48 const path = '/api/v1/videos/' + videoId + '/abuse/' + videoAbuseId
49
50 return makeDeleteRequest({
51 url,
52 token,
53 path,
54 statusCodeExpected
55 })
56}
57
58// ---------------------------------------------------------------------------
59
60export {
61 reportVideoAbuse,
62 getVideoAbusesList,
63 updateVideoAbuse,
64 deleteVideoAbuse
65}
diff --git a/shared/extra-utils/videos/video-blacklist.ts b/shared/extra-utils/videos/video-blacklist.ts
new file mode 100644
index 000000000..e25a292fc
--- /dev/null
+++ b/shared/extra-utils/videos/video-blacklist.ts
@@ -0,0 +1,72 @@
1import * as request from 'supertest'
2import { VideoBlacklistType } from '../../models/videos'
3import { makeGetRequest } from '..'
4
5function addVideoToBlacklist (
6 url: string,
7 token: string,
8 videoId: number | string,
9 reason?: string,
10 unfederate?: boolean,
11 specialStatus = 204
12) {
13 const path = '/api/v1/videos/' + videoId + '/blacklist'
14
15 return request(url)
16 .post(path)
17 .send({ reason, unfederate })
18 .set('Accept', 'application/json')
19 .set('Authorization', 'Bearer ' + token)
20 .expect(specialStatus)
21}
22
23function updateVideoBlacklist (url: string, token: string, videoId: number, reason?: string, specialStatus = 204) {
24 const path = '/api/v1/videos/' + videoId + '/blacklist'
25
26 return request(url)
27 .put(path)
28 .send({ reason })
29 .set('Accept', 'application/json')
30 .set('Authorization', 'Bearer ' + token)
31 .expect(specialStatus)
32}
33
34function removeVideoFromBlacklist (url: string, token: string, videoId: number | string, specialStatus = 204) {
35 const path = '/api/v1/videos/' + videoId + '/blacklist'
36
37 return request(url)
38 .delete(path)
39 .set('Accept', 'application/json')
40 .set('Authorization', 'Bearer ' + token)
41 .expect(specialStatus)
42}
43
44function getBlacklistedVideosList (parameters: {
45 url: string,
46 token: string,
47 sort?: string,
48 type?: VideoBlacklistType,
49 specialStatus?: number
50}) {
51 let { url, token, sort, type, specialStatus = 200 } = parameters
52 const path = '/api/v1/videos/blacklist/'
53
54 const query = { sort, type }
55
56 return makeGetRequest({
57 url,
58 path,
59 query,
60 token,
61 statusCodeExpected: specialStatus
62 })
63}
64
65// ---------------------------------------------------------------------------
66
67export {
68 addVideoToBlacklist,
69 removeVideoFromBlacklist,
70 getBlacklistedVideosList,
71 updateVideoBlacklist
72}
diff --git a/shared/extra-utils/videos/video-captions.ts b/shared/extra-utils/videos/video-captions.ts
new file mode 100644
index 000000000..8d67f617b
--- /dev/null
+++ b/shared/extra-utils/videos/video-captions.ts
@@ -0,0 +1,71 @@
1import { makeDeleteRequest, makeGetRequest, makeUploadRequest } from '../requests/requests'
2import * as request from 'supertest'
3import * as chai from 'chai'
4import { buildAbsoluteFixturePath } from '../miscs/miscs'
5
6const expect = chai.expect
7
8function createVideoCaption (args: {
9 url: string,
10 accessToken: string
11 videoId: string | number
12 language: string
13 fixture: string,
14 mimeType?: string,
15 statusCodeExpected?: number
16}) {
17 const path = '/api/v1/videos/' + args.videoId + '/captions/' + args.language
18
19 const captionfile = buildAbsoluteFixturePath(args.fixture)
20 const captionfileAttach = args.mimeType ? [ captionfile, { contentType: args.mimeType } ] : captionfile
21
22 return makeUploadRequest({
23 method: 'PUT',
24 url: args.url,
25 path,
26 token: args.accessToken,
27 fields: {},
28 attaches: {
29 captionfile: captionfileAttach
30 },
31 statusCodeExpected: args.statusCodeExpected || 204
32 })
33}
34
35function listVideoCaptions (url: string, videoId: string | number) {
36 const path = '/api/v1/videos/' + videoId + '/captions'
37
38 return makeGetRequest({
39 url,
40 path,
41 statusCodeExpected: 200
42 })
43}
44
45function deleteVideoCaption (url: string, token: string, videoId: string | number, language: string) {
46 const path = '/api/v1/videos/' + videoId + '/captions/' + language
47
48 return makeDeleteRequest({
49 url,
50 token,
51 path,
52 statusCodeExpected: 204
53 })
54}
55
56async function testCaptionFile (url: string, captionPath: string, containsString: string) {
57 const res = await request(url)
58 .get(captionPath)
59 .expect(200)
60
61 expect(res.text).to.contain(containsString)
62}
63
64// ---------------------------------------------------------------------------
65
66export {
67 createVideoCaption,
68 listVideoCaptions,
69 testCaptionFile,
70 deleteVideoCaption
71}
diff --git a/shared/extra-utils/videos/video-change-ownership.ts b/shared/extra-utils/videos/video-change-ownership.ts
new file mode 100644
index 000000000..371d02000
--- /dev/null
+++ b/shared/extra-utils/videos/video-change-ownership.ts
@@ -0,0 +1,54 @@
1import * as request from 'supertest'
2
3function changeVideoOwnership (url: string, token: string, videoId: number | string, username, expectedStatus = 204) {
4 const path = '/api/v1/videos/' + videoId + '/give-ownership'
5
6 return request(url)
7 .post(path)
8 .set('Accept', 'application/json')
9 .set('Authorization', 'Bearer ' + token)
10 .send({ username })
11 .expect(expectedStatus)
12}
13
14function getVideoChangeOwnershipList (url: string, token: string) {
15 const path = '/api/v1/videos/ownership'
16
17 return request(url)
18 .get(path)
19 .query({ sort: '-createdAt' })
20 .set('Accept', 'application/json')
21 .set('Authorization', 'Bearer ' + token)
22 .expect(200)
23 .expect('Content-Type', /json/)
24}
25
26function acceptChangeOwnership (url: string, token: string, ownershipId: string, channelId: number, expectedStatus = 204) {
27 const path = '/api/v1/videos/ownership/' + ownershipId + '/accept'
28
29 return request(url)
30 .post(path)
31 .set('Accept', 'application/json')
32 .set('Authorization', 'Bearer ' + token)
33 .send({ channelId })
34 .expect(expectedStatus)
35}
36
37function refuseChangeOwnership (url: string, token: string, ownershipId: string, expectedStatus = 204) {
38 const path = '/api/v1/videos/ownership/' + ownershipId + '/refuse'
39
40 return request(url)
41 .post(path)
42 .set('Accept', 'application/json')
43 .set('Authorization', 'Bearer ' + token)
44 .expect(expectedStatus)
45}
46
47// ---------------------------------------------------------------------------
48
49export {
50 changeVideoOwnership,
51 getVideoChangeOwnershipList,
52 acceptChangeOwnership,
53 refuseChangeOwnership
54}
diff --git a/shared/extra-utils/videos/video-channels.ts b/shared/extra-utils/videos/video-channels.ts
new file mode 100644
index 000000000..93a257bf9
--- /dev/null
+++ b/shared/extra-utils/videos/video-channels.ts
@@ -0,0 +1,134 @@
1import * as request from 'supertest'
2import { VideoChannelCreate, VideoChannelUpdate } from '../../models/videos'
3import { updateAvatarRequest } from '../requests/requests'
4import { getMyUserInformation, ServerInfo } from '..'
5import { User } from '../..'
6
7function getVideoChannelsList (url: string, start: number, count: number, sort?: string) {
8 const path = '/api/v1/video-channels'
9
10 const req = request(url)
11 .get(path)
12 .query({ start: start })
13 .query({ count: count })
14
15 if (sort) req.query({ sort })
16
17 return req.set('Accept', 'application/json')
18 .expect(200)
19 .expect('Content-Type', /json/)
20}
21
22function getAccountVideoChannelsList (url: string, accountName: string, specialStatus = 200) {
23 const path = '/api/v1/accounts/' + accountName + '/video-channels'
24
25 return request(url)
26 .get(path)
27 .set('Accept', 'application/json')
28 .expect(specialStatus)
29 .expect('Content-Type', /json/)
30}
31
32function addVideoChannel (
33 url: string,
34 token: string,
35 videoChannelAttributesArg: VideoChannelCreate,
36 expectedStatus = 200
37) {
38 const path = '/api/v1/video-channels/'
39
40 // Default attributes
41 let attributes = {
42 displayName: 'my super video channel',
43 description: 'my super channel description',
44 support: 'my super channel support'
45 }
46 attributes = Object.assign(attributes, videoChannelAttributesArg)
47
48 return request(url)
49 .post(path)
50 .send(attributes)
51 .set('Accept', 'application/json')
52 .set('Authorization', 'Bearer ' + token)
53 .expect(expectedStatus)
54}
55
56function updateVideoChannel (
57 url: string,
58 token: string,
59 channelName: string,
60 attributes: VideoChannelUpdate,
61 expectedStatus = 204
62) {
63 const body = {}
64 const path = '/api/v1/video-channels/' + channelName
65
66 if (attributes.displayName) body['displayName'] = attributes.displayName
67 if (attributes.description) body['description'] = attributes.description
68 if (attributes.support) body['support'] = attributes.support
69
70 return request(url)
71 .put(path)
72 .send(body)
73 .set('Accept', 'application/json')
74 .set('Authorization', 'Bearer ' + token)
75 .expect(expectedStatus)
76}
77
78function deleteVideoChannel (url: string, token: string, channelName: string, expectedStatus = 204) {
79 const path = '/api/v1/video-channels/' + channelName
80
81 return request(url)
82 .delete(path)
83 .set('Accept', 'application/json')
84 .set('Authorization', 'Bearer ' + token)
85 .expect(expectedStatus)
86}
87
88function getVideoChannel (url: string, channelName: string) {
89 const path = '/api/v1/video-channels/' + channelName
90
91 return request(url)
92 .get(path)
93 .set('Accept', 'application/json')
94 .expect(200)
95 .expect('Content-Type', /json/)
96}
97
98function updateVideoChannelAvatar (options: {
99 url: string,
100 accessToken: string,
101 fixture: string,
102 videoChannelName: string | number
103}) {
104
105 const path = '/api/v1/video-channels/' + options.videoChannelName + '/avatar/pick'
106
107 return updateAvatarRequest(Object.assign(options, { path }))
108}
109
110function setDefaultVideoChannel (servers: ServerInfo[]) {
111 const tasks: Promise<any>[] = []
112
113 for (const server of servers) {
114 const p = getMyUserInformation(server.url, server.accessToken)
115 .then(res => server.videoChannel = (res.body as User).videoChannels[0])
116
117 tasks.push(p)
118 }
119
120 return Promise.all(tasks)
121}
122
123// ---------------------------------------------------------------------------
124
125export {
126 updateVideoChannelAvatar,
127 getVideoChannelsList,
128 getAccountVideoChannelsList,
129 addVideoChannel,
130 updateVideoChannel,
131 deleteVideoChannel,
132 getVideoChannel,
133 setDefaultVideoChannel
134}
diff --git a/shared/extra-utils/videos/video-comments.ts b/shared/extra-utils/videos/video-comments.ts
new file mode 100644
index 000000000..0ebf69ced
--- /dev/null
+++ b/shared/extra-utils/videos/video-comments.ts
@@ -0,0 +1,87 @@
1import * as request from 'supertest'
2import { makeDeleteRequest } from '../requests/requests'
3
4function getVideoCommentThreads (url: string, videoId: number | string, start: number, count: number, sort?: string, token?: string) {
5 const path = '/api/v1/videos/' + videoId + '/comment-threads'
6
7 const req = request(url)
8 .get(path)
9 .query({ start: start })
10 .query({ count: count })
11
12 if (sort) req.query({ sort })
13 if (token) req.set('Authorization', 'Bearer ' + token)
14
15 return req.set('Accept', 'application/json')
16 .expect(200)
17 .expect('Content-Type', /json/)
18}
19
20function getVideoThreadComments (url: string, videoId: number | string, threadId: number, token?: string) {
21 const path = '/api/v1/videos/' + videoId + '/comment-threads/' + threadId
22
23 const req = request(url)
24 .get(path)
25 .set('Accept', 'application/json')
26
27 if (token) req.set('Authorization', 'Bearer ' + token)
28
29 return req.expect(200)
30 .expect('Content-Type', /json/)
31}
32
33function addVideoCommentThread (url: string, token: string, videoId: number | string, text: string, expectedStatus = 200) {
34 const path = '/api/v1/videos/' + videoId + '/comment-threads'
35
36 return request(url)
37 .post(path)
38 .send({ text })
39 .set('Accept', 'application/json')
40 .set('Authorization', 'Bearer ' + token)
41 .expect(expectedStatus)
42}
43
44function addVideoCommentReply (
45 url: string,
46 token: string,
47 videoId: number | string,
48 inReplyToCommentId: number,
49 text: string,
50 expectedStatus = 200
51) {
52 const path = '/api/v1/videos/' + videoId + '/comments/' + inReplyToCommentId
53
54 return request(url)
55 .post(path)
56 .send({ text })
57 .set('Accept', 'application/json')
58 .set('Authorization', 'Bearer ' + token)
59 .expect(expectedStatus)
60}
61
62function deleteVideoComment (
63 url: string,
64 token: string,
65 videoId: number | string,
66 commentId: number,
67 statusCodeExpected = 204
68) {
69 const path = '/api/v1/videos/' + videoId + '/comments/' + commentId
70
71 return makeDeleteRequest({
72 url,
73 path,
74 token,
75 statusCodeExpected
76 })
77}
78
79// ---------------------------------------------------------------------------
80
81export {
82 getVideoCommentThreads,
83 getVideoThreadComments,
84 addVideoCommentThread,
85 addVideoCommentReply,
86 deleteVideoComment
87}
diff --git a/shared/extra-utils/videos/video-history.ts b/shared/extra-utils/videos/video-history.ts
new file mode 100644
index 000000000..dc7095b4d
--- /dev/null
+++ b/shared/extra-utils/videos/video-history.ts
@@ -0,0 +1,39 @@
1import { makeGetRequest, makePostBodyRequest, makePutBodyRequest } from '../requests/requests'
2
3function userWatchVideo (url: string, token: string, videoId: number | string, currentTime: number, statusCodeExpected = 204) {
4 const path = '/api/v1/videos/' + videoId + '/watching'
5 const fields = { currentTime }
6
7 return makePutBodyRequest({ url, path, token, fields, statusCodeExpected })
8}
9
10function listMyVideosHistory (url: string, token: string) {
11 const path = '/api/v1/users/me/history/videos'
12
13 return makeGetRequest({
14 url,
15 path,
16 token,
17 statusCodeExpected: 200
18 })
19}
20
21function removeMyVideosHistory (url: string, token: string, beforeDate?: string) {
22 const path = '/api/v1/users/me/history/videos/remove'
23
24 return makePostBodyRequest({
25 url,
26 path,
27 token,
28 fields: beforeDate ? { beforeDate } : {},
29 statusCodeExpected: 204
30 })
31}
32
33// ---------------------------------------------------------------------------
34
35export {
36 userWatchVideo,
37 listMyVideosHistory,
38 removeMyVideosHistory
39}
diff --git a/shared/extra-utils/videos/video-imports.ts b/shared/extra-utils/videos/video-imports.ts
new file mode 100644
index 000000000..ec77cdcda
--- /dev/null
+++ b/shared/extra-utils/videos/video-imports.ts
@@ -0,0 +1,57 @@
1
2import { VideoImportCreate } from '../../models/videos'
3import { makeGetRequest, makeUploadRequest } from '../requests/requests'
4
5function getYoutubeVideoUrl () {
6 return 'https://youtu.be/msX3jv1XdvM'
7}
8
9function getMagnetURI () {
10 // tslint:disable:max-line-length
11 return 'magnet:?xs=https%3A%2F%2Fpeertube2.cpy.re%2Fstatic%2Ftorrents%2Fb209ca00-c8bb-4b2b-b421-1ede169f3dbc-720.torrent&xt=urn:btih:0f498834733e8057ed5c6f2ee2b4efd8d84a76ee&dn=super+peertube2+video&tr=wss%3A%2F%2Fpeertube2.cpy.re%3A443%2Ftracker%2Fsocket&tr=https%3A%2F%2Fpeertube2.cpy.re%2Ftracker%2Fannounce&ws=https%3A%2F%2Fpeertube2.cpy.re%2Fstatic%2Fwebseed%2Fb209ca00-c8bb-4b2b-b421-1ede169f3dbc-720.mp4'
12}
13
14function getBadVideoUrl () {
15 return 'https://download.cpy.re/peertube/bad_video.mp4'
16}
17
18function importVideo (url: string, token: string, attributes: VideoImportCreate) {
19 const path = '/api/v1/videos/imports'
20
21 let attaches: any = {}
22 if (attributes.torrentfile) attaches = { torrentfile: attributes.torrentfile }
23
24 return makeUploadRequest({
25 url,
26 path,
27 token,
28 attaches,
29 fields: attributes,
30 statusCodeExpected: 200
31 })
32}
33
34function getMyVideoImports (url: string, token: string, sort?: string) {
35 const path = '/api/v1/users/me/videos/imports'
36
37 const query = {}
38 if (sort) query['sort'] = sort
39
40 return makeGetRequest({
41 url,
42 query,
43 path,
44 token,
45 statusCodeExpected: 200
46 })
47}
48
49// ---------------------------------------------------------------------------
50
51export {
52 getBadVideoUrl,
53 getYoutubeVideoUrl,
54 importVideo,
55 getMagnetURI,
56 getMyVideoImports
57}
diff --git a/shared/extra-utils/videos/video-playlists.ts b/shared/extra-utils/videos/video-playlists.ts
new file mode 100644
index 000000000..4d110a131
--- /dev/null
+++ b/shared/extra-utils/videos/video-playlists.ts
@@ -0,0 +1,318 @@
1import { makeDeleteRequest, makeGetRequest, makePostBodyRequest, makePutBodyRequest, makeUploadRequest } from '../requests/requests'
2import { VideoPlaylistCreate } from '../../models/videos/playlist/video-playlist-create.model'
3import { omit } from 'lodash'
4import { VideoPlaylistUpdate } from '../../models/videos/playlist/video-playlist-update.model'
5import { VideoPlaylistElementCreate } from '../../models/videos/playlist/video-playlist-element-create.model'
6import { VideoPlaylistElementUpdate } from '../../models/videos/playlist/video-playlist-element-update.model'
7import { videoUUIDToId } from './videos'
8import { join } from 'path'
9import { root } from '..'
10import { readdir } from 'fs-extra'
11import { expect } from 'chai'
12import { VideoPlaylistType } from '../../models/videos/playlist/video-playlist-type.model'
13
14function getVideoPlaylistsList (url: string, start: number, count: number, sort?: string) {
15 const path = '/api/v1/video-playlists'
16
17 const query = {
18 start,
19 count,
20 sort
21 }
22
23 return makeGetRequest({
24 url,
25 path,
26 query,
27 statusCodeExpected: 200
28 })
29}
30
31function getVideoChannelPlaylistsList (url: string, videoChannelName: string, start: number, count: number, sort?: string) {
32 const path = '/api/v1/video-channels/' + videoChannelName + '/video-playlists'
33
34 const query = {
35 start,
36 count,
37 sort
38 }
39
40 return makeGetRequest({
41 url,
42 path,
43 query,
44 statusCodeExpected: 200
45 })
46}
47
48function getAccountPlaylistsList (url: string, accountName: string, start: number, count: number, sort?: string) {
49 const path = '/api/v1/accounts/' + accountName + '/video-playlists'
50
51 const query = {
52 start,
53 count,
54 sort
55 }
56
57 return makeGetRequest({
58 url,
59 path,
60 query,
61 statusCodeExpected: 200
62 })
63}
64
65function getAccountPlaylistsListWithToken (
66 url: string,
67 token: string,
68 accountName: string,
69 start: number,
70 count: number,
71 playlistType?: VideoPlaylistType,
72 sort?: string
73) {
74 const path = '/api/v1/accounts/' + accountName + '/video-playlists'
75
76 const query = {
77 start,
78 count,
79 playlistType,
80 sort
81 }
82
83 return makeGetRequest({
84 url,
85 token,
86 path,
87 query,
88 statusCodeExpected: 200
89 })
90}
91
92function getVideoPlaylist (url: string, playlistId: number | string, statusCodeExpected = 200) {
93 const path = '/api/v1/video-playlists/' + playlistId
94
95 return makeGetRequest({
96 url,
97 path,
98 statusCodeExpected
99 })
100}
101
102function getVideoPlaylistWithToken (url: string, token: string, playlistId: number | string, statusCodeExpected = 200) {
103 const path = '/api/v1/video-playlists/' + playlistId
104
105 return makeGetRequest({
106 url,
107 token,
108 path,
109 statusCodeExpected
110 })
111}
112
113function deleteVideoPlaylist (url: string, token: string, playlistId: number | string, statusCodeExpected = 204) {
114 const path = '/api/v1/video-playlists/' + playlistId
115
116 return makeDeleteRequest({
117 url,
118 path,
119 token,
120 statusCodeExpected
121 })
122}
123
124function createVideoPlaylist (options: {
125 url: string,
126 token: string,
127 playlistAttrs: VideoPlaylistCreate,
128 expectedStatus?: number
129}) {
130 const path = '/api/v1/video-playlists'
131
132 const fields = omit(options.playlistAttrs, 'thumbnailfile')
133
134 const attaches = options.playlistAttrs.thumbnailfile
135 ? { thumbnailfile: options.playlistAttrs.thumbnailfile }
136 : {}
137
138 return makeUploadRequest({
139 method: 'POST',
140 url: options.url,
141 path,
142 token: options.token,
143 fields,
144 attaches,
145 statusCodeExpected: options.expectedStatus || 200
146 })
147}
148
149function updateVideoPlaylist (options: {
150 url: string,
151 token: string,
152 playlistAttrs: VideoPlaylistUpdate,
153 playlistId: number | string,
154 expectedStatus?: number
155}) {
156 const path = '/api/v1/video-playlists/' + options.playlistId
157
158 const fields = omit(options.playlistAttrs, 'thumbnailfile')
159
160 const attaches = options.playlistAttrs.thumbnailfile
161 ? { thumbnailfile: options.playlistAttrs.thumbnailfile }
162 : {}
163
164 return makeUploadRequest({
165 method: 'PUT',
166 url: options.url,
167 path,
168 token: options.token,
169 fields,
170 attaches,
171 statusCodeExpected: options.expectedStatus || 204
172 })
173}
174
175async function addVideoInPlaylist (options: {
176 url: string,
177 token: string,
178 playlistId: number | string,
179 elementAttrs: VideoPlaylistElementCreate | { videoId: string }
180 expectedStatus?: number
181}) {
182 options.elementAttrs.videoId = await videoUUIDToId(options.url, options.elementAttrs.videoId)
183
184 const path = '/api/v1/video-playlists/' + options.playlistId + '/videos'
185
186 return makePostBodyRequest({
187 url: options.url,
188 path,
189 token: options.token,
190 fields: options.elementAttrs,
191 statusCodeExpected: options.expectedStatus || 200
192 })
193}
194
195function updateVideoPlaylistElement (options: {
196 url: string,
197 token: string,
198 playlistId: number | string,
199 videoId: number | string,
200 elementAttrs: VideoPlaylistElementUpdate,
201 expectedStatus?: number
202}) {
203 const path = '/api/v1/video-playlists/' + options.playlistId + '/videos/' + options.videoId
204
205 return makePutBodyRequest({
206 url: options.url,
207 path,
208 token: options.token,
209 fields: options.elementAttrs,
210 statusCodeExpected: options.expectedStatus || 204
211 })
212}
213
214function removeVideoFromPlaylist (options: {
215 url: string,
216 token: string,
217 playlistId: number | string,
218 videoId: number | string,
219 expectedStatus?: number
220}) {
221 const path = '/api/v1/video-playlists/' + options.playlistId + '/videos/' + options.videoId
222
223 return makeDeleteRequest({
224 url: options.url,
225 path,
226 token: options.token,
227 statusCodeExpected: options.expectedStatus || 204
228 })
229}
230
231function reorderVideosPlaylist (options: {
232 url: string,
233 token: string,
234 playlistId: number | string,
235 elementAttrs: {
236 startPosition: number,
237 insertAfterPosition: number,
238 reorderLength?: number
239 },
240 expectedStatus?: number
241}) {
242 const path = '/api/v1/video-playlists/' + options.playlistId + '/videos/reorder'
243
244 return makePostBodyRequest({
245 url: options.url,
246 path,
247 token: options.token,
248 fields: options.elementAttrs,
249 statusCodeExpected: options.expectedStatus || 204
250 })
251}
252
253async function checkPlaylistFilesWereRemoved (
254 playlistUUID: string,
255 serverNumber: number,
256 directories = [ 'thumbnails' ]
257) {
258 const testDirectory = 'test' + serverNumber
259
260 for (const directory of directories) {
261 const directoryPath = join(root(), testDirectory, directory)
262
263 const files = await readdir(directoryPath)
264 for (const file of files) {
265 expect(file).to.not.contain(playlistUUID)
266 }
267 }
268}
269
270function getVideoPlaylistPrivacies (url: string) {
271 const path = '/api/v1/video-playlists/privacies'
272
273 return makeGetRequest({
274 url,
275 path,
276 statusCodeExpected: 200
277 })
278}
279
280function doVideosExistInMyPlaylist (url: string, token: string, videoIds: number[]) {
281 const path = '/api/v1/users/me/video-playlists/videos-exist'
282
283 return makeGetRequest({
284 url,
285 token,
286 path,
287 query: { videoIds },
288 statusCodeExpected: 200
289 })
290}
291
292// ---------------------------------------------------------------------------
293
294export {
295 getVideoPlaylistPrivacies,
296
297 getVideoPlaylistsList,
298 getVideoChannelPlaylistsList,
299 getAccountPlaylistsList,
300 getAccountPlaylistsListWithToken,
301
302 getVideoPlaylist,
303 getVideoPlaylistWithToken,
304
305 createVideoPlaylist,
306 updateVideoPlaylist,
307 deleteVideoPlaylist,
308
309 addVideoInPlaylist,
310 updateVideoPlaylistElement,
311 removeVideoFromPlaylist,
312
313 reorderVideosPlaylist,
314
315 checkPlaylistFilesWereRemoved,
316
317 doVideosExistInMyPlaylist
318}
diff --git a/shared/extra-utils/videos/video-streaming-playlists.ts b/shared/extra-utils/videos/video-streaming-playlists.ts
new file mode 100644
index 000000000..eb25011cb
--- /dev/null
+++ b/shared/extra-utils/videos/video-streaming-playlists.ts
@@ -0,0 +1,51 @@
1import { makeRawRequest } from '../requests/requests'
2import { sha256 } from '../../../server/helpers/core-utils'
3import { VideoStreamingPlaylist } from '../../models/videos/video-streaming-playlist.model'
4import { expect } from 'chai'
5
6function getPlaylist (url: string, statusCodeExpected = 200) {
7 return makeRawRequest(url, statusCodeExpected)
8}
9
10function getSegment (url: string, statusCodeExpected = 200, range?: string) {
11 return makeRawRequest(url, statusCodeExpected, range)
12}
13
14function getSegmentSha256 (url: string, statusCodeExpected = 200) {
15 return makeRawRequest(url, statusCodeExpected)
16}
17
18async function checkSegmentHash (
19 baseUrlPlaylist: string,
20 baseUrlSegment: string,
21 videoUUID: string,
22 resolution: number,
23 hlsPlaylist: VideoStreamingPlaylist
24) {
25 const res = await getPlaylist(`${baseUrlPlaylist}/${videoUUID}/${resolution}.m3u8`)
26 const playlist = res.text
27
28 const videoName = `${videoUUID}-${resolution}-fragmented.mp4`
29
30 const matches = /#EXT-X-BYTERANGE:(\d+)@(\d+)/.exec(playlist)
31
32 const length = parseInt(matches[1], 10)
33 const offset = parseInt(matches[2], 10)
34 const range = `${offset}-${offset + length - 1}`
35
36 const res2 = await getSegment(`${baseUrlSegment}/${videoUUID}/${videoName}`, 206, `bytes=${range}`)
37
38 const resSha = await getSegmentSha256(hlsPlaylist.segmentsSha256Url)
39
40 const sha256Server = resSha.body[ videoName ][range]
41 expect(sha256(res2.body)).to.equal(sha256Server)
42}
43
44// ---------------------------------------------------------------------------
45
46export {
47 getPlaylist,
48 getSegment,
49 getSegmentSha256,
50 checkSegmentHash
51}
diff --git a/shared/extra-utils/videos/videos.ts b/shared/extra-utils/videos/videos.ts
new file mode 100644
index 000000000..b5a07b792
--- /dev/null
+++ b/shared/extra-utils/videos/videos.ts
@@ -0,0 +1,648 @@
1/* tslint:disable:no-unused-expression */
2
3import { expect } from 'chai'
4import { pathExists, readdir, readFile } from 'fs-extra'
5import * as parseTorrent from 'parse-torrent'
6import { extname, join } from 'path'
7import * as request from 'supertest'
8import {
9 buildAbsoluteFixturePath,
10 getMyUserInformation,
11 immutableAssign,
12 makeGetRequest,
13 makePutBodyRequest,
14 makeUploadRequest,
15 root,
16 ServerInfo,
17 testImage
18} from '../'
19import * as validator from 'validator'
20import { VideoDetails, VideoPrivacy } from '../../models/videos'
21import { VIDEO_CATEGORIES, VIDEO_LANGUAGES, loadLanguages, VIDEO_LICENCES, VIDEO_PRIVACIES } from '../../../server/initializers/constants'
22import { dateIsValid, webtorrentAdd } from '../miscs/miscs'
23
24loadLanguages()
25
26type VideoAttributes = {
27 name?: string
28 category?: number
29 licence?: number
30 language?: string
31 nsfw?: boolean
32 commentsEnabled?: boolean
33 downloadEnabled?: boolean
34 waitTranscoding?: boolean
35 description?: string
36 originallyPublishedAt?: string
37 tags?: string[]
38 channelId?: number
39 privacy?: VideoPrivacy
40 fixture?: string
41 thumbnailfile?: string
42 previewfile?: string
43 scheduleUpdate?: {
44 updateAt: string
45 privacy?: VideoPrivacy
46 }
47}
48
49function getVideoCategories (url: string) {
50 const path = '/api/v1/videos/categories'
51
52 return makeGetRequest({
53 url,
54 path,
55 statusCodeExpected: 200
56 })
57}
58
59function getVideoLicences (url: string) {
60 const path = '/api/v1/videos/licences'
61
62 return makeGetRequest({
63 url,
64 path,
65 statusCodeExpected: 200
66 })
67}
68
69function getVideoLanguages (url: string) {
70 const path = '/api/v1/videos/languages'
71
72 return makeGetRequest({
73 url,
74 path,
75 statusCodeExpected: 200
76 })
77}
78
79function getVideoPrivacies (url: string) {
80 const path = '/api/v1/videos/privacies'
81
82 return makeGetRequest({
83 url,
84 path,
85 statusCodeExpected: 200
86 })
87}
88
89function getVideo (url: string, id: number | string, expectedStatus = 200) {
90 const path = '/api/v1/videos/' + id
91
92 return request(url)
93 .get(path)
94 .set('Accept', 'application/json')
95 .expect(expectedStatus)
96}
97
98function viewVideo (url: string, id: number | string, expectedStatus = 204, xForwardedFor?: string) {
99 const path = '/api/v1/videos/' + id + '/views'
100
101 const req = request(url)
102 .post(path)
103 .set('Accept', 'application/json')
104
105 if (xForwardedFor) {
106 req.set('X-Forwarded-For', xForwardedFor)
107 }
108
109 return req.expect(expectedStatus)
110}
111
112function getVideoWithToken (url: string, token: string, id: number | string, expectedStatus = 200) {
113 const path = '/api/v1/videos/' + id
114
115 return request(url)
116 .get(path)
117 .set('Authorization', 'Bearer ' + token)
118 .set('Accept', 'application/json')
119 .expect(expectedStatus)
120}
121
122function getVideoDescription (url: string, descriptionPath: string) {
123 return request(url)
124 .get(descriptionPath)
125 .set('Accept', 'application/json')
126 .expect(200)
127 .expect('Content-Type', /json/)
128}
129
130function getVideosList (url: string) {
131 const path = '/api/v1/videos'
132
133 return request(url)
134 .get(path)
135 .query({ sort: 'name' })
136 .set('Accept', 'application/json')
137 .expect(200)
138 .expect('Content-Type', /json/)
139}
140
141function getVideosListWithToken (url: string, token: string, query: { nsfw?: boolean } = {}) {
142 const path = '/api/v1/videos'
143
144 return request(url)
145 .get(path)
146 .set('Authorization', 'Bearer ' + token)
147 .query(immutableAssign(query, { sort: 'name' }))
148 .set('Accept', 'application/json')
149 .expect(200)
150 .expect('Content-Type', /json/)
151}
152
153function getLocalVideos (url: string) {
154 const path = '/api/v1/videos'
155
156 return request(url)
157 .get(path)
158 .query({ sort: 'name', filter: 'local' })
159 .set('Accept', 'application/json')
160 .expect(200)
161 .expect('Content-Type', /json/)
162}
163
164function getMyVideos (url: string, accessToken: string, start: number, count: number, sort?: string) {
165 const path = '/api/v1/users/me/videos'
166
167 const req = request(url)
168 .get(path)
169 .query({ start: start })
170 .query({ count: count })
171
172 if (sort) req.query({ sort })
173
174 return req.set('Accept', 'application/json')
175 .set('Authorization', 'Bearer ' + accessToken)
176 .expect(200)
177 .expect('Content-Type', /json/)
178}
179
180function getAccountVideos (
181 url: string,
182 accessToken: string,
183 accountName: string,
184 start: number,
185 count: number,
186 sort?: string,
187 query: { nsfw?: boolean } = {}
188) {
189 const path = '/api/v1/accounts/' + accountName + '/videos'
190
191 return makeGetRequest({
192 url,
193 path,
194 query: immutableAssign(query, {
195 start,
196 count,
197 sort
198 }),
199 token: accessToken,
200 statusCodeExpected: 200
201 })
202}
203
204function getVideoChannelVideos (
205 url: string,
206 accessToken: string,
207 videoChannelName: string,
208 start: number,
209 count: number,
210 sort?: string,
211 query: { nsfw?: boolean } = {}
212) {
213 const path = '/api/v1/video-channels/' + videoChannelName + '/videos'
214
215 return makeGetRequest({
216 url,
217 path,
218 query: immutableAssign(query, {
219 start,
220 count,
221 sort
222 }),
223 token: accessToken,
224 statusCodeExpected: 200
225 })
226}
227
228function getPlaylistVideos (
229 url: string,
230 accessToken: string,
231 playlistId: number | string,
232 start: number,
233 count: number,
234 query: { nsfw?: boolean } = {}
235) {
236 const path = '/api/v1/video-playlists/' + playlistId + '/videos'
237
238 return makeGetRequest({
239 url,
240 path,
241 query: immutableAssign(query, {
242 start,
243 count
244 }),
245 token: accessToken,
246 statusCodeExpected: 200
247 })
248}
249
250function getVideosListPagination (url: string, start: number, count: number, sort?: string) {
251 const path = '/api/v1/videos'
252
253 const req = request(url)
254 .get(path)
255 .query({ start: start })
256 .query({ count: count })
257
258 if (sort) req.query({ sort })
259
260 return req.set('Accept', 'application/json')
261 .expect(200)
262 .expect('Content-Type', /json/)
263}
264
265function getVideosListSort (url: string, sort: string) {
266 const path = '/api/v1/videos'
267
268 return request(url)
269 .get(path)
270 .query({ sort: sort })
271 .set('Accept', 'application/json')
272 .expect(200)
273 .expect('Content-Type', /json/)
274}
275
276function getVideosWithFilters (url: string, query: { tagsAllOf: string[], categoryOneOf: number[] | number }) {
277 const path = '/api/v1/videos'
278
279 return request(url)
280 .get(path)
281 .query(query)
282 .set('Accept', 'application/json')
283 .expect(200)
284 .expect('Content-Type', /json/)
285}
286
287function removeVideo (url: string, token: string, id: number | string, expectedStatus = 204) {
288 const path = '/api/v1/videos'
289
290 return request(url)
291 .delete(path + '/' + id)
292 .set('Accept', 'application/json')
293 .set('Authorization', 'Bearer ' + token)
294 .expect(expectedStatus)
295}
296
297async function checkVideoFilesWereRemoved (
298 videoUUID: string,
299 serverNumber: number,
300 directories = [
301 'redundancy',
302 'videos',
303 'thumbnails',
304 'torrents',
305 'previews',
306 'captions',
307 join('playlists', 'hls'),
308 join('redundancy', 'hls')
309 ]
310) {
311 const testDirectory = 'test' + serverNumber
312
313 for (const directory of directories) {
314 const directoryPath = join(root(), testDirectory, directory)
315
316 const directoryExists = await pathExists(directoryPath)
317 if (directoryExists === false) continue
318
319 const files = await readdir(directoryPath)
320 for (const file of files) {
321 expect(file).to.not.contain(videoUUID)
322 }
323 }
324}
325
326async function uploadVideo (url: string, accessToken: string, videoAttributesArg: VideoAttributes, specialStatus = 200) {
327 const path = '/api/v1/videos/upload'
328 let defaultChannelId = '1'
329
330 try {
331 const res = await getMyUserInformation(url, accessToken)
332 defaultChannelId = res.body.videoChannels[0].id
333 } catch (e) { /* empty */ }
334
335 // Override default attributes
336 const attributes = Object.assign({
337 name: 'my super video',
338 category: 5,
339 licence: 4,
340 language: 'zh',
341 channelId: defaultChannelId,
342 nsfw: true,
343 waitTranscoding: false,
344 description: 'my super description',
345 support: 'my super support text',
346 tags: [ 'tag' ],
347 privacy: VideoPrivacy.PUBLIC,
348 commentsEnabled: true,
349 downloadEnabled: true,
350 fixture: 'video_short.webm'
351 }, videoAttributesArg)
352
353 const req = request(url)
354 .post(path)
355 .set('Accept', 'application/json')
356 .set('Authorization', 'Bearer ' + accessToken)
357 .field('name', attributes.name)
358 .field('nsfw', JSON.stringify(attributes.nsfw))
359 .field('commentsEnabled', JSON.stringify(attributes.commentsEnabled))
360 .field('downloadEnabled', JSON.stringify(attributes.downloadEnabled))
361 .field('waitTranscoding', JSON.stringify(attributes.waitTranscoding))
362 .field('privacy', attributes.privacy.toString())
363 .field('channelId', attributes.channelId)
364
365 if (attributes.description !== undefined) {
366 req.field('description', attributes.description)
367 }
368 if (attributes.language !== undefined) {
369 req.field('language', attributes.language.toString())
370 }
371 if (attributes.category !== undefined) {
372 req.field('category', attributes.category.toString())
373 }
374 if (attributes.licence !== undefined) {
375 req.field('licence', attributes.licence.toString())
376 }
377
378 for (let i = 0; i < attributes.tags.length; i++) {
379 req.field('tags[' + i + ']', attributes.tags[i])
380 }
381
382 if (attributes.thumbnailfile !== undefined) {
383 req.attach('thumbnailfile', buildAbsoluteFixturePath(attributes.thumbnailfile))
384 }
385 if (attributes.previewfile !== undefined) {
386 req.attach('previewfile', buildAbsoluteFixturePath(attributes.previewfile))
387 }
388
389 if (attributes.scheduleUpdate) {
390 req.field('scheduleUpdate[updateAt]', attributes.scheduleUpdate.updateAt)
391
392 if (attributes.scheduleUpdate.privacy) {
393 req.field('scheduleUpdate[privacy]', attributes.scheduleUpdate.privacy)
394 }
395 }
396
397 if (attributes.originallyPublishedAt !== undefined) {
398 req.field('originallyPublishedAt', attributes.originallyPublishedAt)
399 }
400
401 return req.attach('videofile', buildAbsoluteFixturePath(attributes.fixture))
402 .expect(specialStatus)
403}
404
405function updateVideo (url: string, accessToken: string, id: number | string, attributes: VideoAttributes, statusCodeExpected = 204) {
406 const path = '/api/v1/videos/' + id
407 const body = {}
408
409 if (attributes.name) body['name'] = attributes.name
410 if (attributes.category) body['category'] = attributes.category
411 if (attributes.licence) body['licence'] = attributes.licence
412 if (attributes.language) body['language'] = attributes.language
413 if (attributes.nsfw !== undefined) body['nsfw'] = JSON.stringify(attributes.nsfw)
414 if (attributes.commentsEnabled !== undefined) body['commentsEnabled'] = JSON.stringify(attributes.commentsEnabled)
415 if (attributes.downloadEnabled !== undefined) body['downloadEnabled'] = JSON.stringify(attributes.downloadEnabled)
416 if (attributes.originallyPublishedAt !== undefined) body['originallyPublishedAt'] = attributes.originallyPublishedAt
417 if (attributes.description) body['description'] = attributes.description
418 if (attributes.tags) body['tags'] = attributes.tags
419 if (attributes.privacy) body['privacy'] = attributes.privacy
420 if (attributes.channelId) body['channelId'] = attributes.channelId
421 if (attributes.scheduleUpdate) body['scheduleUpdate'] = attributes.scheduleUpdate
422
423 // Upload request
424 if (attributes.thumbnailfile || attributes.previewfile) {
425 const attaches: any = {}
426 if (attributes.thumbnailfile) attaches.thumbnailfile = attributes.thumbnailfile
427 if (attributes.previewfile) attaches.previewfile = attributes.previewfile
428
429 return makeUploadRequest({
430 url,
431 method: 'PUT',
432 path,
433 token: accessToken,
434 fields: body,
435 attaches,
436 statusCodeExpected
437 })
438 }
439
440 return makePutBodyRequest({
441 url,
442 path,
443 fields: body,
444 token: accessToken,
445 statusCodeExpected
446 })
447}
448
449function rateVideo (url: string, accessToken: string, id: number, rating: string, specialStatus = 204) {
450 const path = '/api/v1/videos/' + id + '/rate'
451
452 return request(url)
453 .put(path)
454 .set('Accept', 'application/json')
455 .set('Authorization', 'Bearer ' + accessToken)
456 .send({ rating })
457 .expect(specialStatus)
458}
459
460function parseTorrentVideo (server: ServerInfo, videoUUID: string, resolution: number) {
461 return new Promise<any>((res, rej) => {
462 const torrentName = videoUUID + '-' + resolution + '.torrent'
463 const torrentPath = join(root(), 'test' + server.serverNumber, 'torrents', torrentName)
464 readFile(torrentPath, (err, data) => {
465 if (err) return rej(err)
466
467 return res(parseTorrent(data))
468 })
469 })
470}
471
472async function completeVideoCheck (
473 url: string,
474 video: any,
475 attributes: {
476 name: string
477 category: number
478 licence: number
479 language: string
480 nsfw: boolean
481 commentsEnabled: boolean
482 downloadEnabled: boolean
483 description: string
484 publishedAt?: string
485 support: string
486 originallyPublishedAt?: string,
487 account: {
488 name: string
489 host: string
490 }
491 isLocal: boolean
492 tags: string[]
493 privacy: number
494 likes?: number
495 dislikes?: number
496 duration: number
497 channel: {
498 displayName: string
499 name: string
500 description
501 isLocal: boolean
502 }
503 fixture: string
504 files: {
505 resolution: number
506 size: number
507 }[],
508 thumbnailfile?: string
509 previewfile?: string
510 }
511) {
512 if (!attributes.likes) attributes.likes = 0
513 if (!attributes.dislikes) attributes.dislikes = 0
514
515 expect(video.name).to.equal(attributes.name)
516 expect(video.category.id).to.equal(attributes.category)
517 expect(video.category.label).to.equal(attributes.category !== null ? VIDEO_CATEGORIES[attributes.category] : 'Misc')
518 expect(video.licence.id).to.equal(attributes.licence)
519 expect(video.licence.label).to.equal(attributes.licence !== null ? VIDEO_LICENCES[attributes.licence] : 'Unknown')
520 expect(video.language.id).to.equal(attributes.language)
521 expect(video.language.label).to.equal(attributes.language !== null ? VIDEO_LANGUAGES[attributes.language] : 'Unknown')
522 expect(video.privacy.id).to.deep.equal(attributes.privacy)
523 expect(video.privacy.label).to.deep.equal(VIDEO_PRIVACIES[attributes.privacy])
524 expect(video.nsfw).to.equal(attributes.nsfw)
525 expect(video.description).to.equal(attributes.description)
526 expect(video.account.id).to.be.a('number')
527 expect(video.account.uuid).to.be.a('string')
528 expect(video.account.host).to.equal(attributes.account.host)
529 expect(video.account.name).to.equal(attributes.account.name)
530 expect(video.channel.displayName).to.equal(attributes.channel.displayName)
531 expect(video.channel.name).to.equal(attributes.channel.name)
532 expect(video.likes).to.equal(attributes.likes)
533 expect(video.dislikes).to.equal(attributes.dislikes)
534 expect(video.isLocal).to.equal(attributes.isLocal)
535 expect(video.duration).to.equal(attributes.duration)
536 expect(dateIsValid(video.createdAt)).to.be.true
537 expect(dateIsValid(video.publishedAt)).to.be.true
538 expect(dateIsValid(video.updatedAt)).to.be.true
539
540 if (attributes.publishedAt) {
541 expect(video.publishedAt).to.equal(attributes.publishedAt)
542 }
543
544 if (attributes.originallyPublishedAt) {
545 expect(video.originallyPublishedAt).to.equal(attributes.originallyPublishedAt)
546 } else {
547 expect(video.originallyPublishedAt).to.be.null
548 }
549
550 const res = await getVideo(url, video.uuid)
551 const videoDetails: VideoDetails = res.body
552
553 expect(videoDetails.files).to.have.lengthOf(attributes.files.length)
554 expect(videoDetails.tags).to.deep.equal(attributes.tags)
555 expect(videoDetails.account.name).to.equal(attributes.account.name)
556 expect(videoDetails.account.host).to.equal(attributes.account.host)
557 expect(video.channel.displayName).to.equal(attributes.channel.displayName)
558 expect(video.channel.name).to.equal(attributes.channel.name)
559 expect(videoDetails.channel.host).to.equal(attributes.account.host)
560 expect(videoDetails.channel.isLocal).to.equal(attributes.channel.isLocal)
561 expect(dateIsValid(videoDetails.channel.createdAt.toString())).to.be.true
562 expect(dateIsValid(videoDetails.channel.updatedAt.toString())).to.be.true
563 expect(videoDetails.commentsEnabled).to.equal(attributes.commentsEnabled)
564 expect(videoDetails.downloadEnabled).to.equal(attributes.downloadEnabled)
565
566 for (const attributeFile of attributes.files) {
567 const file = videoDetails.files.find(f => f.resolution.id === attributeFile.resolution)
568 expect(file).not.to.be.undefined
569
570 let extension = extname(attributes.fixture)
571 // Transcoding enabled on server 2, extension will always be .mp4
572 if (attributes.account.host === 'localhost:9002') extension = '.mp4'
573
574 const magnetUri = file.magnetUri
575 expect(file.magnetUri).to.have.lengthOf.above(2)
576 expect(file.torrentUrl).to.equal(`http://${attributes.account.host}/static/torrents/${videoDetails.uuid}-${file.resolution.id}.torrent`)
577 expect(file.fileUrl).to.equal(`http://${attributes.account.host}/static/webseed/${videoDetails.uuid}-${file.resolution.id}${extension}`)
578 expect(file.resolution.id).to.equal(attributeFile.resolution)
579 expect(file.resolution.label).to.equal(attributeFile.resolution + 'p')
580
581 const minSize = attributeFile.size - ((10 * attributeFile.size) / 100)
582 const maxSize = attributeFile.size + ((10 * attributeFile.size) / 100)
583 expect(file.size,
584 'File size for resolution ' + file.resolution.label + ' outside confidence interval (' + minSize + '> size <' + maxSize + ')')
585 .to.be.above(minSize).and.below(maxSize)
586
587 {
588 await testImage(url, attributes.thumbnailfile || attributes.fixture, videoDetails.thumbnailPath)
589 }
590
591 if (attributes.previewfile) {
592 await testImage(url, attributes.previewfile, videoDetails.previewPath)
593 }
594
595 const torrent = await webtorrentAdd(magnetUri, true)
596 expect(torrent.files).to.be.an('array')
597 expect(torrent.files.length).to.equal(1)
598 expect(torrent.files[0].path).to.exist.and.to.not.equal('')
599 }
600}
601
602async function videoUUIDToId (url: string, id: number | string) {
603 if (validator.isUUID('' + id) === false) return id
604
605 const res = await getVideo(url, id)
606 return res.body.id
607}
608
609async function uploadVideoAndGetId (options: { server: ServerInfo, videoName: string, nsfw?: boolean, token?: string }) {
610 const videoAttrs: any = { name: options.videoName }
611 if (options.nsfw) videoAttrs.nsfw = options.nsfw
612
613 const res = await uploadVideo(options.server.url, options.token || options.server.accessToken, videoAttrs)
614
615 return { id: res.body.video.id, uuid: res.body.video.uuid }
616}
617
618// ---------------------------------------------------------------------------
619
620export {
621 getVideoDescription,
622 getVideoCategories,
623 getVideoLicences,
624 videoUUIDToId,
625 getVideoPrivacies,
626 getVideoLanguages,
627 getMyVideos,
628 getAccountVideos,
629 getVideoChannelVideos,
630 getVideo,
631 getVideoWithToken,
632 getVideosList,
633 getVideosListPagination,
634 getVideosListSort,
635 removeVideo,
636 getVideosListWithToken,
637 uploadVideo,
638 getVideosWithFilters,
639 updateVideo,
640 rateVideo,
641 viewVideo,
642 parseTorrentVideo,
643 getLocalVideos,
644 completeVideoCheck,
645 checkVideoFilesWereRemoved,
646 getPlaylistVideos,
647 uploadVideoAndGetId
648}