aboutsummaryrefslogtreecommitdiffhomepage
path: root/shared/extra-utils/videos/videos.ts
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2021-07-15 10:02:54 +0200
committerChocobozzz <me@florianbigard.com>2021-07-20 15:27:18 +0200
commitd23dd9fbfc4d26026352c10f81d2795ceaf2908a (patch)
treeda82286d423c5e834a1ee2dcd5970076b8263cf1 /shared/extra-utils/videos/videos.ts
parent7926c5f9b3ffcabb1ffb0dcfa5e48b8e0b88fbc0 (diff)
downloadPeerTube-d23dd9fbfc4d26026352c10f81d2795ceaf2908a.tar.gz
PeerTube-d23dd9fbfc4d26026352c10f81d2795ceaf2908a.tar.zst
PeerTube-d23dd9fbfc4d26026352c10f81d2795ceaf2908a.zip
Introduce videos command
Diffstat (limited to 'shared/extra-utils/videos/videos.ts')
-rw-r--r--shared/extra-utils/videos/videos.ts714
1 files changed, 24 insertions, 690 deletions
diff --git a/shared/extra-utils/videos/videos.ts b/shared/extra-utils/videos/videos.ts
index 5e20f8010..19f0df8b8 100644
--- a/shared/extra-utils/videos/videos.ts
+++ b/shared/extra-utils/videos/videos.ts
@@ -1,306 +1,16 @@
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
3import { expect } from 'chai' 3import { expect } from 'chai'
4import { createReadStream, pathExists, readdir, readFile, stat } from 'fs-extra' 4import { pathExists, readdir } from 'fs-extra'
5import got, { Response as GotResponse } from 'got/dist/source'
6import * as parseTorrent from 'parse-torrent'
7import { join } from 'path' 5import { join } from 'path'
8import * as request from 'supertest'
9import validator from 'validator'
10import { getLowercaseExtension } from '@server/helpers/core-utils' 6import { getLowercaseExtension } from '@server/helpers/core-utils'
11import { buildUUID } from '@server/helpers/uuid'
12import { HttpStatusCode } from '@shared/core-utils' 7import { HttpStatusCode } from '@shared/core-utils'
13import { BooleanBothQuery, VideosCommonQuery } from '@shared/models' 8import { VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_PRIVACIES } from '../../../server/initializers/constants'
14import { loadLanguages, VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_PRIVACIES } from '../../../server/initializers/constants' 9import { dateIsValid, testImage, webtorrentAdd } from '../miscs'
15import { VideoDetails, VideoPrivacy } from '../../models/videos' 10import { makeRawRequest } from '../requests/requests'
16import { buildAbsoluteFixturePath, dateIsValid, testImage, wait, webtorrentAdd } from '../miscs' 11import { waitJobs } from '../server'
17import { makeGetRequest, makePutBodyRequest, makeRawRequest, makeUploadRequest } from '../requests/requests'
18import { waitJobs } from '../server/jobs'
19import { ServerInfo } from '../server/servers' 12import { ServerInfo } from '../server/servers'
20import { xxxgetMyUserInformation } from '../users' 13import { VideoEdit } from './videos-command'
21
22loadLanguages()
23
24type VideoAttributes = {
25 name?: string
26 category?: number
27 licence?: number
28 language?: string
29 nsfw?: boolean
30 commentsEnabled?: boolean
31 downloadEnabled?: boolean
32 waitTranscoding?: boolean
33 description?: string
34 originallyPublishedAt?: string
35 tags?: string[]
36 channelId?: number
37 privacy?: VideoPrivacy
38 fixture?: string
39 support?: string
40 thumbnailfile?: string
41 previewfile?: string
42 scheduleUpdate?: {
43 updateAt: string
44 privacy?: VideoPrivacy
45 }
46}
47
48function getVideoCategories (url: string) {
49 const path = '/api/v1/videos/categories'
50
51 return makeGetRequest({
52 url,
53 path,
54 statusCodeExpected: HttpStatusCode.OK_200
55 })
56}
57
58function getVideoLicences (url: string) {
59 const path = '/api/v1/videos/licences'
60
61 return makeGetRequest({
62 url,
63 path,
64 statusCodeExpected: HttpStatusCode.OK_200
65 })
66}
67
68function getVideoLanguages (url: string) {
69 const path = '/api/v1/videos/languages'
70
71 return makeGetRequest({
72 url,
73 path,
74 statusCodeExpected: HttpStatusCode.OK_200
75 })
76}
77
78function getVideoPrivacies (url: string) {
79 const path = '/api/v1/videos/privacies'
80
81 return makeGetRequest({
82 url,
83 path,
84 statusCodeExpected: HttpStatusCode.OK_200
85 })
86}
87
88function getVideo (url: string, id: number | string, expectedStatus = HttpStatusCode.OK_200) {
89 const path = '/api/v1/videos/' + id
90
91 return request(url)
92 .get(path)
93 .set('Accept', 'application/json')
94 .expect(expectedStatus)
95}
96
97async function getVideoIdFromUUID (url: string, uuid: string) {
98 const res = await getVideo(url, uuid)
99
100 return res.body.id
101}
102
103function getVideoFileMetadataUrl (url: string) {
104 return request(url)
105 .get('/')
106 .set('Accept', 'application/json')
107 .expect(HttpStatusCode.OK_200)
108 .expect('Content-Type', /json/)
109}
110
111function viewVideo (url: string, id: number | string, expectedStatus = HttpStatusCode.NO_CONTENT_204, xForwardedFor?: string) {
112 const path = '/api/v1/videos/' + id + '/views'
113
114 const req = request(url)
115 .post(path)
116 .set('Accept', 'application/json')
117
118 if (xForwardedFor) {
119 req.set('X-Forwarded-For', xForwardedFor)
120 }
121
122 return req.expect(expectedStatus)
123}
124
125function getVideoWithToken (url: string, token: string, id: number | string, expectedStatus = HttpStatusCode.OK_200) {
126 const path = '/api/v1/videos/' + id
127
128 return request(url)
129 .get(path)
130 .set('Authorization', 'Bearer ' + token)
131 .set('Accept', 'application/json')
132 .expect(expectedStatus)
133}
134
135function getVideoDescription (url: string, descriptionPath: string) {
136 return request(url)
137 .get(descriptionPath)
138 .set('Accept', 'application/json')
139 .expect(HttpStatusCode.OK_200)
140 .expect('Content-Type', /json/)
141}
142
143function getVideosList (url: string) {
144 const path = '/api/v1/videos'
145
146 return request(url)
147 .get(path)
148 .query({ sort: 'name' })
149 .set('Accept', 'application/json')
150 .expect(HttpStatusCode.OK_200)
151 .expect('Content-Type', /json/)
152}
153
154function getVideosListWithToken (url: string, token: string, query: { nsfw?: BooleanBothQuery } = {}) {
155 const path = '/api/v1/videos'
156
157 return request(url)
158 .get(path)
159 .set('Authorization', 'Bearer ' + token)
160 .query({ sort: 'name', ...query })
161 .set('Accept', 'application/json')
162 .expect(HttpStatusCode.OK_200)
163 .expect('Content-Type', /json/)
164}
165
166function getLocalVideos (url: string) {
167 const path = '/api/v1/videos'
168
169 return request(url)
170 .get(path)
171 .query({ sort: 'name', filter: 'local' })
172 .set('Accept', 'application/json')
173 .expect(HttpStatusCode.OK_200)
174 .expect('Content-Type', /json/)
175}
176
177function getMyVideos (url: string, accessToken: string, start: number, count: number, sort?: string, search?: string) {
178 const path = '/api/v1/users/me/videos'
179
180 const req = request(url)
181 .get(path)
182 .query({ start: start })
183 .query({ count: count })
184 .query({ search: search })
185
186 if (sort) req.query({ sort })
187
188 return req.set('Accept', 'application/json')
189 .set('Authorization', 'Bearer ' + accessToken)
190 .expect(HttpStatusCode.OK_200)
191 .expect('Content-Type', /json/)
192}
193
194function getMyVideosWithFilter (url: string, accessToken: string, query: { isLive?: boolean }) {
195 const path = '/api/v1/users/me/videos'
196
197 return makeGetRequest({
198 url,
199 path,
200 token: accessToken,
201 query,
202 statusCodeExpected: HttpStatusCode.OK_200
203 })
204}
205
206function getAccountVideos (
207 url: string,
208 accessToken: string,
209 accountName: string,
210 start: number,
211 count: number,
212 sort?: string,
213 query: {
214 nsfw?: BooleanBothQuery
215 search?: string
216 } = {}
217) {
218 const path = '/api/v1/accounts/' + accountName + '/videos'
219
220 return makeGetRequest({
221 url,
222 path,
223 query: { ...query, start, count, sort },
224 token: accessToken,
225 statusCodeExpected: HttpStatusCode.OK_200
226 })
227}
228
229function getVideoChannelVideos (
230 url: string,
231 accessToken: string,
232 videoChannelName: string,
233 start: number,
234 count: number,
235 sort?: string,
236 query: { nsfw?: BooleanBothQuery } = {}
237) {
238 const path = '/api/v1/video-channels/' + videoChannelName + '/videos'
239
240 return makeGetRequest({
241 url,
242 path,
243 query: { ...query, start, count, sort },
244 token: accessToken,
245 statusCodeExpected: HttpStatusCode.OK_200
246 })
247}
248
249function getVideosListPagination (url: string, start: number, count: number, sort?: string, skipCount?: boolean) {
250 const path = '/api/v1/videos'
251
252 const req = request(url)
253 .get(path)
254 .query({ start: start })
255 .query({ count: count })
256
257 if (sort) req.query({ sort })
258 if (skipCount) req.query({ skipCount })
259
260 return req.set('Accept', 'application/json')
261 .expect(HttpStatusCode.OK_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(HttpStatusCode.OK_200)
273 .expect('Content-Type', /json/)
274}
275
276function getVideosWithFilters (url: string, query: VideosCommonQuery) {
277 const path = '/api/v1/videos'
278
279 return request(url)
280 .get(path)
281 .query(query)
282 .set('Accept', 'application/json')
283 .expect(HttpStatusCode.OK_200)
284 .expect('Content-Type', /json/)
285}
286
287function removeVideo (url: string, token: string, id: number | string, expectedStatus = HttpStatusCode.NO_CONTENT_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 removeAllVideos (server: ServerInfo) {
298 const resVideos = await getVideosList(server.url)
299
300 for (const v of resVideos.body.data) {
301 await removeVideo(server.url, server.accessToken, v.id)
302 }
303}
304 14
305async function checkVideoFilesWereRemoved ( 15async function checkVideoFilesWereRemoved (
306 videoUUID: string, 16 videoUUID: string,
@@ -329,280 +39,20 @@ async function checkVideoFilesWereRemoved (
329 } 39 }
330} 40}
331 41
332async function uploadVideo (
333 url: string,
334 accessToken: string,
335 videoAttributesArg: VideoAttributes,
336 specialStatus = HttpStatusCode.OK_200,
337 mode: 'legacy' | 'resumable' = 'legacy'
338) {
339 let defaultChannelId = '1'
340
341 try {
342 const res = await xxxgetMyUserInformation(url, accessToken)
343 defaultChannelId = res.body.videoChannels[0].id
344 } catch (e) { /* empty */ }
345
346 // Override default attributes
347 const attributes = Object.assign({
348 name: 'my super video',
349 category: 5,
350 licence: 4,
351 language: 'zh',
352 channelId: defaultChannelId,
353 nsfw: true,
354 waitTranscoding: false,
355 description: 'my super description',
356 support: 'my super support text',
357 tags: [ 'tag' ],
358 privacy: VideoPrivacy.PUBLIC,
359 commentsEnabled: true,
360 downloadEnabled: true,
361 fixture: 'video_short.webm'
362 }, videoAttributesArg)
363
364 const res = mode === 'legacy'
365 ? await buildLegacyUpload(url, accessToken, attributes, specialStatus)
366 : await buildResumeUpload(url, accessToken, attributes, specialStatus)
367
368 // Wait torrent generation
369 if (specialStatus === HttpStatusCode.OK_200) {
370 let video: VideoDetails
371 do {
372 const resVideo = await getVideoWithToken(url, accessToken, res.body.video.uuid)
373 video = resVideo.body
374
375 await wait(50)
376 } while (!video.files[0].torrentUrl)
377 }
378
379 return res
380}
381
382function checkUploadVideoParam ( 42function checkUploadVideoParam (
383 url: string, 43 server: ServerInfo,
384 token: string, 44 token: string,
385 attributes: Partial<VideoAttributes>, 45 attributes: Partial<VideoEdit>,
386 specialStatus = HttpStatusCode.OK_200, 46 expectedStatus = HttpStatusCode.OK_200,
387 mode: 'legacy' | 'resumable' = 'legacy' 47 mode: 'legacy' | 'resumable' = 'legacy'
388) { 48) {
389 return mode === 'legacy' 49 return mode === 'legacy'
390 ? buildLegacyUpload(url, token, attributes, specialStatus) 50 ? server.videosCommand.buildLegacyUpload({ token, attributes, expectedStatus })
391 : buildResumeUpload(url, token, attributes, specialStatus) 51 : server.videosCommand.buildResumeUpload({ token, attributes, expectedStatus })
392}
393
394async function buildLegacyUpload (url: string, token: string, attributes: VideoAttributes, specialStatus = HttpStatusCode.OK_200) {
395 const path = '/api/v1/videos/upload'
396 const req = request(url)
397 .post(path)
398 .set('Accept', 'application/json')
399 .set('Authorization', 'Bearer ' + token)
400
401 buildUploadReq(req, attributes)
402
403 if (attributes.fixture !== undefined) {
404 req.attach('videofile', buildAbsoluteFixturePath(attributes.fixture))
405 }
406
407 return req.expect(specialStatus)
408}
409
410async function buildResumeUpload (url: string, token: string, attributes: VideoAttributes, specialStatus = HttpStatusCode.OK_200) {
411 let size = 0
412 let videoFilePath: string
413 let mimetype = 'video/mp4'
414
415 if (attributes.fixture) {
416 videoFilePath = buildAbsoluteFixturePath(attributes.fixture)
417 size = (await stat(videoFilePath)).size
418
419 if (videoFilePath.endsWith('.mkv')) {
420 mimetype = 'video/x-matroska'
421 } else if (videoFilePath.endsWith('.webm')) {
422 mimetype = 'video/webm'
423 }
424 }
425
426 const initializeSessionRes = await prepareResumableUpload({ url, token, attributes, size, mimetype })
427 const initStatus = initializeSessionRes.status
428
429 if (videoFilePath && initStatus === HttpStatusCode.CREATED_201) {
430 const locationHeader = initializeSessionRes.header['location']
431 expect(locationHeader).to.not.be.undefined
432
433 const pathUploadId = locationHeader.split('?')[1]
434
435 return sendResumableChunks({ url, token, pathUploadId, videoFilePath, size, specialStatus })
436 }
437
438 const expectedInitStatus = specialStatus === HttpStatusCode.OK_200
439 ? HttpStatusCode.CREATED_201
440 : specialStatus
441
442 expect(initStatus).to.equal(expectedInitStatus)
443
444 return initializeSessionRes
445}
446
447async function prepareResumableUpload (options: {
448 url: string
449 token: string
450 attributes: VideoAttributes
451 size: number
452 mimetype: string
453}) {
454 const { url, token, attributes, size, mimetype } = options
455
456 const path = '/api/v1/videos/upload-resumable'
457
458 const req = request(url)
459 .post(path)
460 .set('Authorization', 'Bearer ' + token)
461 .set('X-Upload-Content-Type', mimetype)
462 .set('X-Upload-Content-Length', size.toString())
463
464 buildUploadReq(req, attributes)
465
466 if (attributes.fixture) {
467 req.field('filename', attributes.fixture)
468 }
469
470 return req
471}
472
473function sendResumableChunks (options: {
474 url: string
475 token: string
476 pathUploadId: string
477 videoFilePath: string
478 size: number
479 specialStatus?: HttpStatusCode
480 contentLength?: number
481 contentRangeBuilder?: (start: number, chunk: any) => string
482}) {
483 const { url, token, pathUploadId, videoFilePath, size, specialStatus, contentLength, contentRangeBuilder } = options
484
485 const expectedStatus = specialStatus || HttpStatusCode.OK_200
486
487 const path = '/api/v1/videos/upload-resumable'
488 let start = 0
489
490 const readable = createReadStream(videoFilePath, { highWaterMark: 8 * 1024 })
491 return new Promise<GotResponse>((resolve, reject) => {
492 readable.on('data', async function onData (chunk) {
493 readable.pause()
494
495 const headers = {
496 'Authorization': 'Bearer ' + token,
497 'Content-Type': 'application/octet-stream',
498 'Content-Range': contentRangeBuilder
499 ? contentRangeBuilder(start, chunk)
500 : `bytes ${start}-${start + chunk.length - 1}/${size}`,
501 'Content-Length': contentLength ? contentLength + '' : chunk.length + ''
502 }
503
504 const res = await got({
505 url,
506 method: 'put',
507 headers,
508 path: path + '?' + pathUploadId,
509 body: chunk,
510 responseType: 'json',
511 throwHttpErrors: false
512 })
513
514 start += chunk.length
515
516 if (res.statusCode === expectedStatus) {
517 return resolve(res)
518 }
519
520 if (res.statusCode !== HttpStatusCode.PERMANENT_REDIRECT_308) {
521 readable.off('data', onData)
522 return reject(new Error('Incorrect transient behaviour sending intermediary chunks'))
523 }
524
525 readable.resume()
526 })
527 })
528}
529
530function updateVideo (
531 url: string,
532 accessToken: string,
533 id: number | string,
534 attributes: VideoAttributes,
535 statusCodeExpected = HttpStatusCode.NO_CONTENT_204
536) {
537 const path = '/api/v1/videos/' + id
538 const body = {}
539
540 if (attributes.name) body['name'] = attributes.name
541 if (attributes.category) body['category'] = attributes.category
542 if (attributes.licence) body['licence'] = attributes.licence
543 if (attributes.language) body['language'] = attributes.language
544 if (attributes.nsfw !== undefined) body['nsfw'] = JSON.stringify(attributes.nsfw)
545 if (attributes.commentsEnabled !== undefined) body['commentsEnabled'] = JSON.stringify(attributes.commentsEnabled)
546 if (attributes.downloadEnabled !== undefined) body['downloadEnabled'] = JSON.stringify(attributes.downloadEnabled)
547 if (attributes.originallyPublishedAt !== undefined) body['originallyPublishedAt'] = attributes.originallyPublishedAt
548 if (attributes.description) body['description'] = attributes.description
549 if (attributes.tags) body['tags'] = attributes.tags
550 if (attributes.privacy) body['privacy'] = attributes.privacy
551 if (attributes.channelId) body['channelId'] = attributes.channelId
552 if (attributes.scheduleUpdate) body['scheduleUpdate'] = attributes.scheduleUpdate
553
554 // Upload request
555 if (attributes.thumbnailfile || attributes.previewfile) {
556 const attaches: any = {}
557 if (attributes.thumbnailfile) attaches.thumbnailfile = attributes.thumbnailfile
558 if (attributes.previewfile) attaches.previewfile = attributes.previewfile
559
560 return makeUploadRequest({
561 url,
562 method: 'PUT',
563 path,
564 token: accessToken,
565 fields: body,
566 attaches,
567 statusCodeExpected
568 })
569 }
570
571 return makePutBodyRequest({
572 url,
573 path,
574 fields: body,
575 token: accessToken,
576 statusCodeExpected
577 })
578}
579
580function rateVideo (url: string, accessToken: string, id: number | string, rating: string, specialStatus = HttpStatusCode.NO_CONTENT_204) {
581 const path = '/api/v1/videos/' + id + '/rate'
582
583 return request(url)
584 .put(path)
585 .set('Accept', 'application/json')
586 .set('Authorization', 'Bearer ' + accessToken)
587 .send({ rating })
588 .expect(specialStatus)
589}
590
591function parseTorrentVideo (server: ServerInfo, videoUUID: string, resolution: number) {
592 return new Promise<any>((res, rej) => {
593 const torrentName = videoUUID + '-' + resolution + '.torrent'
594 const torrentPath = server.serversCommand.buildDirectory(join('torrents', torrentName))
595
596 readFile(torrentPath, (err, data) => {
597 if (err) return rej(err)
598
599 return res(parseTorrent(data))
600 })
601 })
602} 52}
603 53
604async function completeVideoCheck ( 54async function completeVideoCheck (
605 url: string, 55 server: ServerInfo,
606 video: any, 56 video: any,
607 attributes: { 57 attributes: {
608 name: string 58 name: string
@@ -644,7 +94,7 @@ async function completeVideoCheck (
644 if (!attributes.likes) attributes.likes = 0 94 if (!attributes.likes) attributes.likes = 0
645 if (!attributes.dislikes) attributes.dislikes = 0 95 if (!attributes.dislikes) attributes.dislikes = 0
646 96
647 const host = new URL(url).host 97 const host = new URL(server.url).host
648 const originHost = attributes.account.host 98 const originHost = attributes.account.host
649 99
650 expect(video.name).to.equal(attributes.name) 100 expect(video.name).to.equal(attributes.name)
@@ -681,8 +131,7 @@ async function completeVideoCheck (
681 expect(video.originallyPublishedAt).to.be.null 131 expect(video.originallyPublishedAt).to.be.null
682 } 132 }
683 133
684 const res = await getVideo(url, video.uuid) 134 const videoDetails = await server.videosCommand.get({ id: video.uuid })
685 const videoDetails: VideoDetails = res.body
686 135
687 expect(videoDetails.files).to.have.lengthOf(attributes.files.length) 136 expect(videoDetails.files).to.have.lengthOf(attributes.files.length)
688 expect(videoDetails.tags).to.deep.equal(attributes.tags) 137 expect(videoDetails.tags).to.deep.equal(attributes.tags)
@@ -738,148 +187,33 @@ async function completeVideoCheck (
738 } 187 }
739 188
740 expect(videoDetails.thumbnailPath).to.exist 189 expect(videoDetails.thumbnailPath).to.exist
741 await testImage(url, attributes.thumbnailfile || attributes.fixture, videoDetails.thumbnailPath) 190 await testImage(server.url, attributes.thumbnailfile || attributes.fixture, videoDetails.thumbnailPath)
742 191
743 if (attributes.previewfile) { 192 if (attributes.previewfile) {
744 expect(videoDetails.previewPath).to.exist 193 expect(videoDetails.previewPath).to.exist
745 await testImage(url, attributes.previewfile, videoDetails.previewPath) 194 await testImage(server.url, attributes.previewfile, videoDetails.previewPath)
746 } 195 }
747} 196}
748 197
749async function videoUUIDToId (url: string, id: number | string) {
750 if (validator.isUUID('' + id) === false) return id
751
752 const res = await getVideo(url, id)
753 return res.body.id
754}
755
756async function uploadVideoAndGetId (options: {
757 server: ServerInfo
758 videoName: string
759 nsfw?: boolean
760 privacy?: VideoPrivacy
761 token?: string
762 fixture?: string
763}) {
764 const videoAttrs: any = { name: options.videoName }
765 if (options.nsfw) videoAttrs.nsfw = options.nsfw
766 if (options.privacy) videoAttrs.privacy = options.privacy
767 if (options.fixture) videoAttrs.fixture = options.fixture
768
769 const res = await uploadVideo(options.server.url, options.token || options.server.accessToken, videoAttrs)
770
771 return res.body.video as { id: number, uuid: string, shortUUID: string }
772}
773
774async function getLocalIdByUUID (url: string, uuid: string) {
775 const res = await getVideo(url, uuid)
776
777 return res.body.id
778}
779
780// serverNumber starts from 1 198// serverNumber starts from 1
781async function uploadRandomVideoOnServers (servers: ServerInfo[], serverNumber: number, additionalParams: any = {}) { 199async function uploadRandomVideoOnServers (
200 servers: ServerInfo[],
201 serverNumber: number,
202 additionalParams?: VideoEdit & { prefixName?: string }
203) {
782 const server = servers.find(s => s.serverNumber === serverNumber) 204 const server = servers.find(s => s.serverNumber === serverNumber)
783 const res = await uploadRandomVideo(server, false, additionalParams) 205 const res = await server.videosCommand.randomUpload({ wait: false, ...additionalParams })
784 206
785 await waitJobs(servers) 207 await waitJobs(servers)
786 208
787 return res 209 return res
788} 210}
789 211
790async function uploadRandomVideo (server: ServerInfo, wait = true, additionalParams: any = {}) {
791 const prefixName = additionalParams.prefixName || ''
792 const name = prefixName + buildUUID()
793
794 const data = Object.assign({ name }, additionalParams)
795 const res = await uploadVideo(server.url, server.accessToken, data)
796
797 if (wait) await waitJobs([ server ])
798
799 return { uuid: res.body.video.uuid, name }
800}
801
802// --------------------------------------------------------------------------- 212// ---------------------------------------------------------------------------
803 213
804export { 214export {
805 getVideoDescription,
806 getVideoCategories,
807 uploadRandomVideo,
808 getVideoLicences,
809 videoUUIDToId,
810 getVideoPrivacies,
811 getVideoLanguages,
812 getMyVideos,
813 getAccountVideos,
814 getVideoChannelVideos,
815 getVideo,
816 getVideoFileMetadataUrl,
817 getVideoWithToken,
818 getVideosList,
819 removeAllVideos,
820 checkUploadVideoParam, 215 checkUploadVideoParam,
821 getVideosListPagination,
822 getVideosListSort,
823 removeVideo,
824 getVideosListWithToken,
825 uploadVideo,
826 sendResumableChunks,
827 getVideosWithFilters,
828 uploadRandomVideoOnServers,
829 updateVideo,
830 rateVideo,
831 viewVideo,
832 parseTorrentVideo,
833 getLocalVideos,
834 completeVideoCheck, 216 completeVideoCheck,
835 checkVideoFilesWereRemoved, 217 uploadRandomVideoOnServers,
836 getMyVideosWithFilter, 218 checkVideoFilesWereRemoved
837 uploadVideoAndGetId,
838 getLocalIdByUUID,
839 getVideoIdFromUUID,
840 prepareResumableUpload
841}
842
843// ---------------------------------------------------------------------------
844
845function buildUploadReq (req: request.Test, attributes: VideoAttributes) {
846
847 for (const key of [ 'name', 'support', 'channelId', 'description', 'originallyPublishedAt' ]) {
848 if (attributes[key] !== undefined) {
849 req.field(key, attributes[key])
850 }
851 }
852
853 for (const key of [ 'nsfw', 'commentsEnabled', 'downloadEnabled', 'waitTranscoding' ]) {
854 if (attributes[key] !== undefined) {
855 req.field(key, JSON.stringify(attributes[key]))
856 }
857 }
858
859 for (const key of [ 'language', 'privacy', 'category', 'licence' ]) {
860 if (attributes[key] !== undefined) {
861 req.field(key, attributes[key].toString())
862 }
863 }
864
865 const tags = attributes.tags || []
866 for (let i = 0; i < tags.length; i++) {
867 req.field('tags[' + i + ']', attributes.tags[i])
868 }
869
870 for (const key of [ 'thumbnailfile', 'previewfile' ]) {
871 if (attributes[key] !== undefined) {
872 req.attach(key, buildAbsoluteFixturePath(attributes[key]))
873 }
874 }
875
876 if (attributes.scheduleUpdate) {
877 if (attributes.scheduleUpdate.updateAt) {
878 req.field('scheduleUpdate[updateAt]', attributes.scheduleUpdate.updateAt)
879 }
880
881 if (attributes.scheduleUpdate.privacy) {
882 req.field('scheduleUpdate[privacy]', attributes.scheduleUpdate.privacy)
883 }
884 }
885} 219}