diff options
author | Chocobozzz <me@florianbigard.com> | 2021-05-27 16:12:41 +0200 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2021-05-27 16:12:41 +0200 |
commit | 8f608a4cb22ab232cfab20665050764b38bac9c7 (patch) | |
tree | 6a6785aae79bf5939ad7b7a50a1bd8031268d2b4 /shared/extra-utils | |
parent | 030ccfce59a8cb8f2fee6ea8dd363ba635c5c5c2 (diff) | |
parent | c215e627b575d2c4085ccb222f4ca8d0237b7552 (diff) | |
download | PeerTube-8f608a4cb22ab232cfab20665050764b38bac9c7.tar.gz PeerTube-8f608a4cb22ab232cfab20665050764b38bac9c7.tar.zst PeerTube-8f608a4cb22ab232cfab20665050764b38bac9c7.zip |
Merge branch 'develop' into shorter-URLs-channels-accounts
Diffstat (limited to 'shared/extra-utils')
-rw-r--r-- | shared/extra-utils/custom-pages/custom-pages.ts | 31 | ||||
-rw-r--r-- | shared/extra-utils/index.ts | 18 | ||||
-rw-r--r-- | shared/extra-utils/server/config.ts | 15 | ||||
-rw-r--r-- | shared/extra-utils/server/debug.ts | 18 | ||||
-rw-r--r-- | shared/extra-utils/server/jobs.ts | 4 | ||||
-rw-r--r-- | shared/extra-utils/server/plugins.ts | 4 | ||||
-rw-r--r-- | shared/extra-utils/server/servers.ts | 5 | ||||
-rw-r--r-- | shared/extra-utils/users/users.ts | 23 | ||||
-rw-r--r-- | shared/extra-utils/videos/video-channels.ts | 11 | ||||
-rw-r--r-- | shared/extra-utils/videos/videos.ts | 258 |
10 files changed, 318 insertions, 69 deletions
diff --git a/shared/extra-utils/custom-pages/custom-pages.ts b/shared/extra-utils/custom-pages/custom-pages.ts new file mode 100644 index 000000000..bf2d16c70 --- /dev/null +++ b/shared/extra-utils/custom-pages/custom-pages.ts | |||
@@ -0,0 +1,31 @@ | |||
1 | import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' | ||
2 | import { makeGetRequest, makePutBodyRequest } from '../requests/requests' | ||
3 | |||
4 | function getInstanceHomepage (url: string, statusCodeExpected = HttpStatusCode.OK_200) { | ||
5 | const path = '/api/v1/custom-pages/homepage/instance' | ||
6 | |||
7 | return makeGetRequest({ | ||
8 | url, | ||
9 | path, | ||
10 | statusCodeExpected | ||
11 | }) | ||
12 | } | ||
13 | |||
14 | function updateInstanceHomepage (url: string, token: string, content: string) { | ||
15 | const path = '/api/v1/custom-pages/homepage/instance' | ||
16 | |||
17 | return makePutBodyRequest({ | ||
18 | url, | ||
19 | path, | ||
20 | token, | ||
21 | fields: { content }, | ||
22 | statusCodeExpected: HttpStatusCode.NO_CONTENT_204 | ||
23 | }) | ||
24 | } | ||
25 | |||
26 | // --------------------------------------------------------------------------- | ||
27 | |||
28 | export { | ||
29 | getInstanceHomepage, | ||
30 | updateInstanceHomepage | ||
31 | } | ||
diff --git a/shared/extra-utils/index.ts b/shared/extra-utils/index.ts index 5c520d0c2..9f5b5bb28 100644 --- a/shared/extra-utils/index.ts +++ b/shared/extra-utils/index.ts | |||
@@ -1,16 +1,27 @@ | |||
1 | export * from './actors/actors' | 1 | export * from './actors/actors' |
2 | export * from './bulk/bulk' | 2 | export * from './bulk/bulk' |
3 | |||
3 | export * from './cli/cli' | 4 | export * from './cli/cli' |
5 | |||
6 | export * from './custom-pages/custom-pages' | ||
7 | |||
4 | export * from './feeds/feeds' | 8 | export * from './feeds/feeds' |
9 | |||
5 | export * from './mock-servers/mock-instances-index' | 10 | export * from './mock-servers/mock-instances-index' |
6 | export * from './miscs/miscs' | 11 | |
12 | export * from './miscs/email' | ||
7 | export * from './miscs/sql' | 13 | export * from './miscs/sql' |
14 | export * from './miscs/miscs' | ||
8 | export * from './miscs/stubs' | 15 | export * from './miscs/stubs' |
16 | |||
9 | export * from './moderation/abuses' | 17 | export * from './moderation/abuses' |
10 | export * from './plugins/mock-blocklist' | 18 | export * from './plugins/mock-blocklist' |
19 | |||
11 | export * from './requests/check-api-params' | 20 | export * from './requests/check-api-params' |
12 | export * from './requests/requests' | 21 | export * from './requests/requests' |
22 | |||
13 | export * from './search/videos' | 23 | export * from './search/videos' |
24 | |||
14 | export * from './server/activitypub' | 25 | export * from './server/activitypub' |
15 | export * from './server/clients' | 26 | export * from './server/clients' |
16 | export * from './server/config' | 27 | export * from './server/config' |
@@ -19,9 +30,14 @@ export * from './server/follows' | |||
19 | export * from './server/jobs' | 30 | export * from './server/jobs' |
20 | export * from './server/plugins' | 31 | export * from './server/plugins' |
21 | export * from './server/servers' | 32 | export * from './server/servers' |
33 | |||
22 | export * from './users/accounts' | 34 | export * from './users/accounts' |
35 | export * from './users/blocklist' | ||
23 | export * from './users/login' | 36 | export * from './users/login' |
37 | export * from './users/user-notifications' | ||
38 | export * from './users/user-subscriptions' | ||
24 | export * from './users/users' | 39 | export * from './users/users' |
40 | |||
25 | export * from './videos/live' | 41 | export * from './videos/live' |
26 | export * from './videos/services' | 42 | export * from './videos/services' |
27 | export * from './videos/video-blacklist' | 43 | export * from './videos/video-blacklist' |
diff --git a/shared/extra-utils/server/config.ts b/shared/extra-utils/server/config.ts index 026a5e61c..b70110852 100644 --- a/shared/extra-utils/server/config.ts +++ b/shared/extra-utils/server/config.ts | |||
@@ -223,6 +223,18 @@ function updateCustomSubConfig (url: string, token: string, newConfig: DeepParti | |||
223 | return updateCustomConfig(url, token, updateParams) | 223 | return updateCustomConfig(url, token, updateParams) |
224 | } | 224 | } |
225 | 225 | ||
226 | function getCustomConfigResolutions (enabled: boolean) { | ||
227 | return { | ||
228 | '240p': enabled, | ||
229 | '360p': enabled, | ||
230 | '480p': enabled, | ||
231 | '720p': enabled, | ||
232 | '1080p': enabled, | ||
233 | '1440p': enabled, | ||
234 | '2160p': enabled | ||
235 | } | ||
236 | } | ||
237 | |||
226 | function deleteCustomConfig (url: string, token: string, statusCodeExpected = HttpStatusCode.OK_200) { | 238 | function deleteCustomConfig (url: string, token: string, statusCodeExpected = HttpStatusCode.OK_200) { |
227 | const path = '/api/v1/config/custom' | 239 | const path = '/api/v1/config/custom' |
228 | 240 | ||
@@ -242,5 +254,6 @@ export { | |||
242 | updateCustomConfig, | 254 | updateCustomConfig, |
243 | getAbout, | 255 | getAbout, |
244 | deleteCustomConfig, | 256 | deleteCustomConfig, |
245 | updateCustomSubConfig | 257 | updateCustomSubConfig, |
258 | getCustomConfigResolutions | ||
246 | } | 259 | } |
diff --git a/shared/extra-utils/server/debug.ts b/shared/extra-utils/server/debug.ts index 5cf80a5fb..f196812b7 100644 --- a/shared/extra-utils/server/debug.ts +++ b/shared/extra-utils/server/debug.ts | |||
@@ -1,5 +1,6 @@ | |||
1 | import { makeGetRequest } from '../requests/requests' | 1 | import { makeGetRequest, makePostBodyRequest } from '../requests/requests' |
2 | import { HttpStatusCode } from '../../core-utils/miscs/http-error-codes' | 2 | import { HttpStatusCode } from '../../core-utils/miscs/http-error-codes' |
3 | import { SendDebugCommand } from '@shared/models' | ||
3 | 4 | ||
4 | function getDebug (url: string, token: string) { | 5 | function getDebug (url: string, token: string) { |
5 | const path = '/api/v1/server/debug' | 6 | const path = '/api/v1/server/debug' |
@@ -12,8 +13,21 @@ function getDebug (url: string, token: string) { | |||
12 | }) | 13 | }) |
13 | } | 14 | } |
14 | 15 | ||
16 | function sendDebugCommand (url: string, token: string, body: SendDebugCommand) { | ||
17 | const path = '/api/v1/server/debug/run-command' | ||
18 | |||
19 | return makePostBodyRequest({ | ||
20 | url, | ||
21 | path, | ||
22 | token, | ||
23 | fields: body, | ||
24 | statusCodeExpected: HttpStatusCode.NO_CONTENT_204 | ||
25 | }) | ||
26 | } | ||
27 | |||
15 | // --------------------------------------------------------------------------- | 28 | // --------------------------------------------------------------------------- |
16 | 29 | ||
17 | export { | 30 | export { |
18 | getDebug | 31 | getDebug, |
32 | sendDebugCommand | ||
19 | } | 33 | } |
diff --git a/shared/extra-utils/server/jobs.ts b/shared/extra-utils/server/jobs.ts index 704929bd4..763374e03 100644 --- a/shared/extra-utils/server/jobs.ts +++ b/shared/extra-utils/server/jobs.ts | |||
@@ -55,7 +55,7 @@ function getJobsListPaginationAndSort (options: { | |||
55 | async function waitJobs (serversArg: ServerInfo[] | ServerInfo) { | 55 | async function waitJobs (serversArg: ServerInfo[] | ServerInfo) { |
56 | const pendingJobWait = process.env.NODE_PENDING_JOB_WAIT | 56 | const pendingJobWait = process.env.NODE_PENDING_JOB_WAIT |
57 | ? parseInt(process.env.NODE_PENDING_JOB_WAIT, 10) | 57 | ? parseInt(process.env.NODE_PENDING_JOB_WAIT, 10) |
58 | : 500 | 58 | : 250 |
59 | 59 | ||
60 | let servers: ServerInfo[] | 60 | let servers: ServerInfo[] |
61 | 61 | ||
@@ -115,7 +115,7 @@ async function waitJobs (serversArg: ServerInfo[] | ServerInfo) { | |||
115 | } | 115 | } |
116 | 116 | ||
117 | if (pendingRequests) { | 117 | if (pendingRequests) { |
118 | await wait(1000) | 118 | await wait(pendingJobWait) |
119 | } | 119 | } |
120 | } while (pendingRequests) | 120 | } while (pendingRequests) |
121 | } | 121 | } |
diff --git a/shared/extra-utils/server/plugins.ts b/shared/extra-utils/server/plugins.ts index 864954ee7..d53e5b382 100644 --- a/shared/extra-utils/server/plugins.ts +++ b/shared/extra-utils/server/plugins.ts | |||
@@ -4,12 +4,12 @@ import { expect } from 'chai' | |||
4 | import { readJSON, writeJSON } from 'fs-extra' | 4 | import { readJSON, writeJSON } from 'fs-extra' |
5 | import { join } from 'path' | 5 | import { join } from 'path' |
6 | import { RegisteredServerSettings } from '@shared/models' | 6 | import { RegisteredServerSettings } from '@shared/models' |
7 | import { PeertubePluginIndexList } from '../../models/plugins/peertube-plugin-index-list.model' | 7 | import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' |
8 | import { PeertubePluginIndexList } from '../../models/plugins/plugin-index/peertube-plugin-index-list.model' | ||
8 | import { PluginType } from '../../models/plugins/plugin.type' | 9 | import { PluginType } from '../../models/plugins/plugin.type' |
9 | import { buildServerDirectory, root } from '../miscs/miscs' | 10 | import { buildServerDirectory, root } from '../miscs/miscs' |
10 | import { makeGetRequest, makePostBodyRequest, makePutBodyRequest } from '../requests/requests' | 11 | import { makeGetRequest, makePostBodyRequest, makePutBodyRequest } from '../requests/requests' |
11 | import { ServerInfo } from './servers' | 12 | import { ServerInfo } from './servers' |
12 | import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' | ||
13 | 13 | ||
14 | function listPlugins (parameters: { | 14 | function listPlugins (parameters: { |
15 | url: string | 15 | url: string |
diff --git a/shared/extra-utils/server/servers.ts b/shared/extra-utils/server/servers.ts index 779a3cc36..d04757470 100644 --- a/shared/extra-utils/server/servers.ts +++ b/shared/extra-utils/server/servers.ts | |||
@@ -45,9 +45,12 @@ interface ServerInfo { | |||
45 | uuid: string | 45 | uuid: string |
46 | name?: string | 46 | name?: string |
47 | url?: string | 47 | url?: string |
48 | |||
48 | account?: { | 49 | account?: { |
49 | name: string | 50 | name: string |
50 | } | 51 | } |
52 | |||
53 | embedPath?: string | ||
51 | } | 54 | } |
52 | 55 | ||
53 | remoteVideo?: { | 56 | remoteVideo?: { |
@@ -274,7 +277,7 @@ async function reRunServer (server: ServerInfo, configOverride?: any) { | |||
274 | } | 277 | } |
275 | 278 | ||
276 | async function checkTmpIsEmpty (server: ServerInfo) { | 279 | async function checkTmpIsEmpty (server: ServerInfo) { |
277 | await checkDirectoryIsEmpty(server, 'tmp', [ 'plugins-global.css', 'hls' ]) | 280 | await checkDirectoryIsEmpty(server, 'tmp', [ 'plugins-global.css', 'hls', 'resumable-uploads' ]) |
278 | 281 | ||
279 | if (await pathExists(join('test' + server.internalServerNumber, 'tmp', 'hls'))) { | 282 | if (await pathExists(join('test' + server.internalServerNumber, 'tmp', 'hls'))) { |
280 | await checkDirectoryIsEmpty(server, 'tmp/hls') | 283 | await checkDirectoryIsEmpty(server, 'tmp/hls') |
diff --git a/shared/extra-utils/users/users.ts b/shared/extra-utils/users/users.ts index 6040dd9c0..0f15962ad 100644 --- a/shared/extra-utils/users/users.ts +++ b/shared/extra-utils/users/users.ts | |||
@@ -1,5 +1,6 @@ | |||
1 | import { omit } from 'lodash' | 1 | import { omit } from 'lodash' |
2 | import * as request from 'supertest' | 2 | import * as request from 'supertest' |
3 | import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' | ||
3 | import { UserUpdateMe } from '../../models/users' | 4 | import { UserUpdateMe } from '../../models/users' |
4 | import { UserAdminFlag } from '../../models/users/user-flag.model' | 5 | import { UserAdminFlag } from '../../models/users/user-flag.model' |
5 | import { UserRegister } from '../../models/users/user-register.model' | 6 | import { UserRegister } from '../../models/users/user-register.model' |
@@ -7,9 +8,8 @@ import { UserRole } from '../../models/users/user-role' | |||
7 | import { makeGetRequest, makePostBodyRequest, makePutBodyRequest, updateImageRequest } from '../requests/requests' | 8 | import { makeGetRequest, makePostBodyRequest, makePutBodyRequest, updateImageRequest } from '../requests/requests' |
8 | import { ServerInfo } from '../server/servers' | 9 | import { ServerInfo } from '../server/servers' |
9 | import { userLogin } from './login' | 10 | import { userLogin } from './login' |
10 | import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' | ||
11 | 11 | ||
12 | type CreateUserArgs = { | 12 | function createUser (parameters: { |
13 | url: string | 13 | url: string |
14 | accessToken: string | 14 | accessToken: string |
15 | username: string | 15 | username: string |
@@ -19,8 +19,7 @@ type CreateUserArgs = { | |||
19 | role?: UserRole | 19 | role?: UserRole |
20 | adminFlags?: UserAdminFlag | 20 | adminFlags?: UserAdminFlag |
21 | specialStatus?: number | 21 | specialStatus?: number |
22 | } | 22 | }) { |
23 | function createUser (parameters: CreateUserArgs) { | ||
24 | const { | 23 | const { |
25 | url, | 24 | url, |
26 | accessToken, | 25 | accessToken, |
@@ -52,6 +51,21 @@ function createUser (parameters: CreateUserArgs) { | |||
52 | .expect(specialStatus) | 51 | .expect(specialStatus) |
53 | } | 52 | } |
54 | 53 | ||
54 | async function generateUser (server: ServerInfo, username: string) { | ||
55 | const password = 'my super password' | ||
56 | const resCreate = await createUser({ url: server.url, accessToken: server.accessToken, username: username, password: password }) | ||
57 | |||
58 | const token = await userLogin(server, { username, password }) | ||
59 | |||
60 | const resMe = await getMyUserInformation(server.url, token) | ||
61 | |||
62 | return { | ||
63 | token, | ||
64 | userId: resCreate.body.user.id, | ||
65 | userChannelId: resMe.body.videoChannels[0].id | ||
66 | } | ||
67 | } | ||
68 | |||
55 | async function generateUserAccessToken (server: ServerInfo, username: string) { | 69 | async function generateUserAccessToken (server: ServerInfo, username: string) { |
56 | const password = 'my super password' | 70 | const password = 'my super password' |
57 | await createUser({ url: server.url, accessToken: server.accessToken, username: username, password: password }) | 71 | await createUser({ url: server.url, accessToken: server.accessToken, username: username, password: password }) |
@@ -393,6 +407,7 @@ export { | |||
393 | resetPassword, | 407 | resetPassword, |
394 | renewUserScopedTokens, | 408 | renewUserScopedTokens, |
395 | updateMyAvatar, | 409 | updateMyAvatar, |
410 | generateUser, | ||
396 | askSendVerifyEmail, | 411 | askSendVerifyEmail, |
397 | generateUserAccessToken, | 412 | generateUserAccessToken, |
398 | verifyEmail, | 413 | verifyEmail, |
diff --git a/shared/extra-utils/videos/video-channels.ts b/shared/extra-utils/videos/video-channels.ts index d0dfb5856..0aab93e52 100644 --- a/shared/extra-utils/videos/video-channels.ts +++ b/shared/extra-utils/videos/video-channels.ts | |||
@@ -5,7 +5,7 @@ import { VideoChannelUpdate } from '../../models/videos/channel/video-channel-up | |||
5 | import { VideoChannelCreate } from '../../models/videos/channel/video-channel-create.model' | 5 | import { VideoChannelCreate } from '../../models/videos/channel/video-channel-create.model' |
6 | import { makeDeleteRequest, makeGetRequest, updateImageRequest } from '../requests/requests' | 6 | import { makeDeleteRequest, makeGetRequest, updateImageRequest } from '../requests/requests' |
7 | import { ServerInfo } from '../server/servers' | 7 | import { ServerInfo } from '../server/servers' |
8 | import { User } from '../../models/users/user.model' | 8 | import { MyUser, User } from '../../models/users/user.model' |
9 | import { getMyUserInformation } from '../users/users' | 9 | import { getMyUserInformation } from '../users/users' |
10 | import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' | 10 | import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' |
11 | 11 | ||
@@ -170,6 +170,12 @@ function setDefaultVideoChannel (servers: ServerInfo[]) { | |||
170 | return Promise.all(tasks) | 170 | return Promise.all(tasks) |
171 | } | 171 | } |
172 | 172 | ||
173 | async function getDefaultVideoChannel (url: string, token: string) { | ||
174 | const res = await getMyUserInformation(url, token) | ||
175 | |||
176 | return (res.body as MyUser).videoChannels[0].id | ||
177 | } | ||
178 | |||
173 | // --------------------------------------------------------------------------- | 179 | // --------------------------------------------------------------------------- |
174 | 180 | ||
175 | export { | 181 | export { |
@@ -181,5 +187,6 @@ export { | |||
181 | deleteVideoChannel, | 187 | deleteVideoChannel, |
182 | getVideoChannel, | 188 | getVideoChannel, |
183 | setDefaultVideoChannel, | 189 | setDefaultVideoChannel, |
184 | deleteVideoChannelImage | 190 | deleteVideoChannelImage, |
191 | getDefaultVideoChannel | ||
185 | } | 192 | } |
diff --git a/shared/extra-utils/videos/videos.ts b/shared/extra-utils/videos/videos.ts index a0143b0ef..e88256ac0 100644 --- a/shared/extra-utils/videos/videos.ts +++ b/shared/extra-utils/videos/videos.ts | |||
@@ -1,7 +1,8 @@ | |||
1 | /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/no-floating-promises */ | 1 | /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/no-floating-promises */ |
2 | 2 | ||
3 | import { expect } from 'chai' | 3 | import { expect } from 'chai' |
4 | import { pathExists, readdir, readFile } from 'fs-extra' | 4 | import { createReadStream, pathExists, readdir, readFile, stat } from 'fs-extra' |
5 | import got, { Response as GotResponse } from 'got/dist/source' | ||
5 | import * as parseTorrent from 'parse-torrent' | 6 | import * as parseTorrent from 'parse-torrent' |
6 | import { extname, join } from 'path' | 7 | import { extname, join } from 'path' |
7 | import * as request from 'supertest' | 8 | import * as request from 'supertest' |
@@ -42,6 +43,7 @@ type VideoAttributes = { | |||
42 | channelId?: number | 43 | channelId?: number |
43 | privacy?: VideoPrivacy | 44 | privacy?: VideoPrivacy |
44 | fixture?: string | 45 | fixture?: string |
46 | support?: string | ||
45 | thumbnailfile?: string | 47 | thumbnailfile?: string |
46 | previewfile?: string | 48 | previewfile?: string |
47 | scheduleUpdate?: { | 49 | scheduleUpdate?: { |
@@ -364,8 +366,13 @@ async function checkVideoFilesWereRemoved ( | |||
364 | } | 366 | } |
365 | } | 367 | } |
366 | 368 | ||
367 | async function uploadVideo (url: string, accessToken: string, videoAttributesArg: VideoAttributes, specialStatus = HttpStatusCode.OK_200) { | 369 | async function uploadVideo ( |
368 | const path = '/api/v1/videos/upload' | 370 | url: string, |
371 | accessToken: string, | ||
372 | videoAttributesArg: VideoAttributes, | ||
373 | specialStatus = HttpStatusCode.OK_200, | ||
374 | mode: 'legacy' | 'resumable' = 'legacy' | ||
375 | ) { | ||
369 | let defaultChannelId = '1' | 376 | let defaultChannelId = '1' |
370 | 377 | ||
371 | try { | 378 | try { |
@@ -391,74 +398,170 @@ async function uploadVideo (url: string, accessToken: string, videoAttributesArg | |||
391 | fixture: 'video_short.webm' | 398 | fixture: 'video_short.webm' |
392 | }, videoAttributesArg) | 399 | }, videoAttributesArg) |
393 | 400 | ||
401 | const res = mode === 'legacy' | ||
402 | ? await buildLegacyUpload(url, accessToken, attributes, specialStatus) | ||
403 | : await buildResumeUpload(url, accessToken, attributes, specialStatus) | ||
404 | |||
405 | // Wait torrent generation | ||
406 | if (specialStatus === HttpStatusCode.OK_200) { | ||
407 | let video: VideoDetails | ||
408 | do { | ||
409 | const resVideo = await getVideoWithToken(url, accessToken, res.body.video.uuid) | ||
410 | video = resVideo.body | ||
411 | |||
412 | await wait(50) | ||
413 | } while (!video.files[0].torrentUrl) | ||
414 | } | ||
415 | |||
416 | return res | ||
417 | } | ||
418 | |||
419 | function checkUploadVideoParam ( | ||
420 | url: string, | ||
421 | token: string, | ||
422 | attributes: Partial<VideoAttributes>, | ||
423 | specialStatus = HttpStatusCode.OK_200, | ||
424 | mode: 'legacy' | 'resumable' = 'legacy' | ||
425 | ) { | ||
426 | return mode === 'legacy' | ||
427 | ? buildLegacyUpload(url, token, attributes, specialStatus) | ||
428 | : buildResumeUpload(url, token, attributes, specialStatus) | ||
429 | } | ||
430 | |||
431 | async function buildLegacyUpload (url: string, token: string, attributes: VideoAttributes, specialStatus = HttpStatusCode.OK_200) { | ||
432 | const path = '/api/v1/videos/upload' | ||
394 | const req = request(url) | 433 | const req = request(url) |
395 | .post(path) | 434 | .post(path) |
396 | .set('Accept', 'application/json') | 435 | .set('Accept', 'application/json') |
397 | .set('Authorization', 'Bearer ' + accessToken) | 436 | .set('Authorization', 'Bearer ' + token) |
398 | .field('name', attributes.name) | ||
399 | .field('nsfw', JSON.stringify(attributes.nsfw)) | ||
400 | .field('commentsEnabled', JSON.stringify(attributes.commentsEnabled)) | ||
401 | .field('downloadEnabled', JSON.stringify(attributes.downloadEnabled)) | ||
402 | .field('waitTranscoding', JSON.stringify(attributes.waitTranscoding)) | ||
403 | .field('privacy', attributes.privacy.toString()) | ||
404 | .field('channelId', attributes.channelId) | ||
405 | |||
406 | if (attributes.support !== undefined) { | ||
407 | req.field('support', attributes.support) | ||
408 | } | ||
409 | 437 | ||
410 | if (attributes.description !== undefined) { | 438 | buildUploadReq(req, attributes) |
411 | req.field('description', attributes.description) | ||
412 | } | ||
413 | if (attributes.language !== undefined) { | ||
414 | req.field('language', attributes.language.toString()) | ||
415 | } | ||
416 | if (attributes.category !== undefined) { | ||
417 | req.field('category', attributes.category.toString()) | ||
418 | } | ||
419 | if (attributes.licence !== undefined) { | ||
420 | req.field('licence', attributes.licence.toString()) | ||
421 | } | ||
422 | 439 | ||
423 | const tags = attributes.tags || [] | 440 | if (attributes.fixture !== undefined) { |
424 | for (let i = 0; i < tags.length; i++) { | 441 | req.attach('videofile', buildAbsoluteFixturePath(attributes.fixture)) |
425 | req.field('tags[' + i + ']', attributes.tags[i]) | ||
426 | } | 442 | } |
427 | 443 | ||
428 | if (attributes.thumbnailfile !== undefined) { | 444 | return req.expect(specialStatus) |
429 | req.attach('thumbnailfile', buildAbsoluteFixturePath(attributes.thumbnailfile)) | 445 | } |
430 | } | ||
431 | if (attributes.previewfile !== undefined) { | ||
432 | req.attach('previewfile', buildAbsoluteFixturePath(attributes.previewfile)) | ||
433 | } | ||
434 | 446 | ||
435 | if (attributes.scheduleUpdate) { | 447 | async function buildResumeUpload (url: string, token: string, attributes: VideoAttributes, specialStatus = HttpStatusCode.OK_200) { |
436 | req.field('scheduleUpdate[updateAt]', attributes.scheduleUpdate.updateAt) | 448 | let size = 0 |
449 | let videoFilePath: string | ||
450 | let mimetype = 'video/mp4' | ||
437 | 451 | ||
438 | if (attributes.scheduleUpdate.privacy) { | 452 | if (attributes.fixture) { |
439 | req.field('scheduleUpdate[privacy]', attributes.scheduleUpdate.privacy) | 453 | videoFilePath = buildAbsoluteFixturePath(attributes.fixture) |
454 | size = (await stat(videoFilePath)).size | ||
455 | |||
456 | if (videoFilePath.endsWith('.mkv')) { | ||
457 | mimetype = 'video/x-matroska' | ||
458 | } else if (videoFilePath.endsWith('.webm')) { | ||
459 | mimetype = 'video/webm' | ||
440 | } | 460 | } |
441 | } | 461 | } |
442 | 462 | ||
443 | if (attributes.originallyPublishedAt !== undefined) { | 463 | const initializeSessionRes = await prepareResumableUpload({ url, token, attributes, size, mimetype }) |
444 | req.field('originallyPublishedAt', attributes.originallyPublishedAt) | 464 | const initStatus = initializeSessionRes.status |
465 | |||
466 | if (videoFilePath && initStatus === HttpStatusCode.CREATED_201) { | ||
467 | const locationHeader = initializeSessionRes.header['location'] | ||
468 | expect(locationHeader).to.not.be.undefined | ||
469 | |||
470 | const pathUploadId = locationHeader.split('?')[1] | ||
471 | |||
472 | return sendResumableChunks({ url, token, pathUploadId, videoFilePath, size, specialStatus }) | ||
445 | } | 473 | } |
446 | 474 | ||
447 | const res = await req.attach('videofile', buildAbsoluteFixturePath(attributes.fixture)) | 475 | const expectedInitStatus = specialStatus === HttpStatusCode.OK_200 |
448 | .expect(specialStatus) | 476 | ? HttpStatusCode.CREATED_201 |
477 | : specialStatus | ||
449 | 478 | ||
450 | // Wait torrent generation | 479 | expect(initStatus).to.equal(expectedInitStatus) |
451 | if (specialStatus === HttpStatusCode.OK_200) { | ||
452 | let video: VideoDetails | ||
453 | do { | ||
454 | const resVideo = await getVideoWithToken(url, accessToken, res.body.video.uuid) | ||
455 | video = resVideo.body | ||
456 | 480 | ||
457 | await wait(50) | 481 | return initializeSessionRes |
458 | } while (!video.files[0].torrentUrl) | 482 | } |
483 | |||
484 | async function prepareResumableUpload (options: { | ||
485 | url: string | ||
486 | token: string | ||
487 | attributes: VideoAttributes | ||
488 | size: number | ||
489 | mimetype: string | ||
490 | }) { | ||
491 | const { url, token, attributes, size, mimetype } = options | ||
492 | |||
493 | const path = '/api/v1/videos/upload-resumable' | ||
494 | |||
495 | const req = request(url) | ||
496 | .post(path) | ||
497 | .set('Authorization', 'Bearer ' + token) | ||
498 | .set('X-Upload-Content-Type', mimetype) | ||
499 | .set('X-Upload-Content-Length', size.toString()) | ||
500 | |||
501 | buildUploadReq(req, attributes) | ||
502 | |||
503 | if (attributes.fixture) { | ||
504 | req.field('filename', attributes.fixture) | ||
459 | } | 505 | } |
460 | 506 | ||
461 | return res | 507 | return req |
508 | } | ||
509 | |||
510 | function sendResumableChunks (options: { | ||
511 | url: string | ||
512 | token: string | ||
513 | pathUploadId: string | ||
514 | videoFilePath: string | ||
515 | size: number | ||
516 | specialStatus?: HttpStatusCode | ||
517 | contentLength?: number | ||
518 | contentRangeBuilder?: (start: number, chunk: any) => string | ||
519 | }) { | ||
520 | const { url, token, pathUploadId, videoFilePath, size, specialStatus, contentLength, contentRangeBuilder } = options | ||
521 | |||
522 | const expectedStatus = specialStatus || HttpStatusCode.OK_200 | ||
523 | |||
524 | const path = '/api/v1/videos/upload-resumable' | ||
525 | let start = 0 | ||
526 | |||
527 | const readable = createReadStream(videoFilePath, { highWaterMark: 8 * 1024 }) | ||
528 | return new Promise<GotResponse>((resolve, reject) => { | ||
529 | readable.on('data', async function onData (chunk) { | ||
530 | readable.pause() | ||
531 | |||
532 | const headers = { | ||
533 | 'Authorization': 'Bearer ' + token, | ||
534 | 'Content-Type': 'application/octet-stream', | ||
535 | 'Content-Range': contentRangeBuilder | ||
536 | ? contentRangeBuilder(start, chunk) | ||
537 | : `bytes ${start}-${start + chunk.length - 1}/${size}`, | ||
538 | 'Content-Length': contentLength ? contentLength + '' : chunk.length + '' | ||
539 | } | ||
540 | |||
541 | const res = await got({ | ||
542 | url, | ||
543 | method: 'put', | ||
544 | headers, | ||
545 | path: path + '?' + pathUploadId, | ||
546 | body: chunk, | ||
547 | responseType: 'json', | ||
548 | throwHttpErrors: false | ||
549 | }) | ||
550 | |||
551 | start += chunk.length | ||
552 | |||
553 | if (res.statusCode === expectedStatus) { | ||
554 | return resolve(res) | ||
555 | } | ||
556 | |||
557 | if (res.statusCode !== HttpStatusCode.PERMANENT_REDIRECT_308) { | ||
558 | readable.off('data', onData) | ||
559 | return reject(new Error('Incorrect transient behaviour sending intermediary chunks')) | ||
560 | } | ||
561 | |||
562 | readable.resume() | ||
563 | }) | ||
564 | }) | ||
462 | } | 565 | } |
463 | 566 | ||
464 | function updateVideo ( | 567 | function updateVideo ( |
@@ -749,11 +852,13 @@ export { | |||
749 | getVideoWithToken, | 852 | getVideoWithToken, |
750 | getVideosList, | 853 | getVideosList, |
751 | removeAllVideos, | 854 | removeAllVideos, |
855 | checkUploadVideoParam, | ||
752 | getVideosListPagination, | 856 | getVideosListPagination, |
753 | getVideosListSort, | 857 | getVideosListSort, |
754 | removeVideo, | 858 | removeVideo, |
755 | getVideosListWithToken, | 859 | getVideosListWithToken, |
756 | uploadVideo, | 860 | uploadVideo, |
861 | sendResumableChunks, | ||
757 | getVideosWithFilters, | 862 | getVideosWithFilters, |
758 | uploadRandomVideoOnServers, | 863 | uploadRandomVideoOnServers, |
759 | updateVideo, | 864 | updateVideo, |
@@ -767,5 +872,50 @@ export { | |||
767 | getMyVideosWithFilter, | 872 | getMyVideosWithFilter, |
768 | uploadVideoAndGetId, | 873 | uploadVideoAndGetId, |
769 | getLocalIdByUUID, | 874 | getLocalIdByUUID, |
770 | getVideoIdFromUUID | 875 | getVideoIdFromUUID, |
876 | prepareResumableUpload | ||
877 | } | ||
878 | |||
879 | // --------------------------------------------------------------------------- | ||
880 | |||
881 | function buildUploadReq (req: request.Test, attributes: VideoAttributes) { | ||
882 | |||
883 | for (const key of [ 'name', 'support', 'channelId', 'description', 'originallyPublishedAt' ]) { | ||
884 | if (attributes[key] !== undefined) { | ||
885 | req.field(key, attributes[key]) | ||
886 | } | ||
887 | } | ||
888 | |||
889 | for (const key of [ 'nsfw', 'commentsEnabled', 'downloadEnabled', 'waitTranscoding' ]) { | ||
890 | if (attributes[key] !== undefined) { | ||
891 | req.field(key, JSON.stringify(attributes[key])) | ||
892 | } | ||
893 | } | ||
894 | |||
895 | for (const key of [ 'language', 'privacy', 'category', 'licence' ]) { | ||
896 | if (attributes[key] !== undefined) { | ||
897 | req.field(key, attributes[key].toString()) | ||
898 | } | ||
899 | } | ||
900 | |||
901 | const tags = attributes.tags || [] | ||
902 | for (let i = 0; i < tags.length; i++) { | ||
903 | req.field('tags[' + i + ']', attributes.tags[i]) | ||
904 | } | ||
905 | |||
906 | for (const key of [ 'thumbnailfile', 'previewfile' ]) { | ||
907 | if (attributes[key] !== undefined) { | ||
908 | req.attach(key, buildAbsoluteFixturePath(attributes[key])) | ||
909 | } | ||
910 | } | ||
911 | |||
912 | if (attributes.scheduleUpdate) { | ||
913 | if (attributes.scheduleUpdate.updateAt) { | ||
914 | req.field('scheduleUpdate[updateAt]', attributes.scheduleUpdate.updateAt) | ||
915 | } | ||
916 | |||
917 | if (attributes.scheduleUpdate.privacy) { | ||
918 | req.field('scheduleUpdate[privacy]', attributes.scheduleUpdate.privacy) | ||
919 | } | ||
920 | } | ||
771 | } | 921 | } |