import { expect } from 'chai'
import { createReadStream, stat } from 'fs-extra'
import got, { Response as GotResponse } from 'got'
-import { omit, pick } from 'lodash'
+import { omit } from 'lodash'
import validator from 'validator'
import { buildUUID } from '@server/helpers/uuid'
import { loadLanguages } from '@server/initializers/constants'
-import { HttpStatusCode } from '@shared/core-utils'
+import { pick } from '@shared/core-utils'
import {
+ HttpStatusCode,
ResultList,
UserVideoRateType,
Video,
VideoFileMetadata,
VideoPrivacy,
VideosCommonQuery,
- VideosWithSearchCommonQuery
+ VideoTranscodingCreate
} from '@shared/models'
import { buildAbsoluteFixturePath, wait } from '../miscs'
import { unwrapBody } from '../requests'
return id
}
+ async listFiles (options: OverrideCommandOptions & {
+ id: number | string
+ }) {
+ const video = await this.get(options)
+
+ const files = video.files || []
+ const hlsFiles = video.streamingPlaylists[0]?.files || []
+
+ return files.concat(hlsFiles)
+ }
+
// ---------------------------------------------------------------------------
listMyVideos (options: OverrideCommandOptions & {
sort?: string
search?: string
isLive?: boolean
+ channelId?: number
} = {}) {
const path = '/api/v1/users/me/videos'
...options,
path,
- query: pick(options, [ 'start', 'count', 'sort', 'search', 'isLive' ]),
+ query: pick(options, [ 'start', 'count', 'sort', 'search', 'isLive', 'channelId' ]),
implicitToken: true,
defaultExpectedStatus: HttpStatusCode.OK_200
})
})
}
- listByAccount (options: OverrideCommandOptions & VideosWithSearchCommonQuery & {
- accountName: string
+ listByAccount (options: OverrideCommandOptions & VideosCommonQuery & {
+ handle: string
}) {
- const { accountName, search } = options
- const path = '/api/v1/accounts/' + accountName + '/videos'
+ const { handle, search } = options
+ const path = '/api/v1/accounts/' + handle + '/videos'
return this.getRequestBody<ResultList<Video>>({
...options,
})
}
- listByChannel (options: OverrideCommandOptions & VideosWithSearchCommonQuery & {
- videoChannelName: string
+ listByChannel (options: OverrideCommandOptions & VideosCommonQuery & {
+ handle: string
}) {
- const { videoChannelName } = options
- const path = '/api/v1/video-channels/' + videoChannelName + '/videos'
+ const { handle } = options
+ const path = '/api/v1/video-channels/' + handle + '/videos'
return this.getRequestBody<ResultList<Video>>({
...options,
// ---------------------------------------------------------------------------
+ async find (options: OverrideCommandOptions & {
+ name: string
+ }) {
+ const { data } = await this.list(options)
+
+ return data.find(v => v.name === options.name)
+ }
+
+ // ---------------------------------------------------------------------------
+
update (options: OverrideCommandOptions & {
id: number | string
attributes?: VideoEdit
}) {
const path = '/api/v1/videos/' + options.id
- return this.deleteRequest({
+ return unwrapBody(this.deleteRequest({
...options,
path,
implicitToken: true,
defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
- })
+ }))
}
async removeAll () {
attributes?: VideoEdit
mode?: 'legacy' | 'resumable' // default legacy
} = {}) {
- const { mode = 'legacy', expectedStatus } = options
+ const { mode = 'legacy' } = options
let defaultChannelId = 1
try {
...options.attributes
}
- const res = mode === 'legacy'
+ const created = mode === 'legacy'
? await this.buildLegacyUpload({ ...options, attributes })
: await this.buildResumeUpload({ ...options, attributes })
// Wait torrent generation
+ const expectedStatus = this.buildExpectedStatus({ ...options, defaultExpectedStatus: HttpStatusCode.OK_200 })
if (expectedStatus === HttpStatusCode.OK_200) {
let video: VideoDetails
do {
- video = await this.getWithToken({ ...options, id: video.uuid })
+ video = await this.getWithToken({ ...options, id: created.uuid })
await wait(50)
} while (!video.files[0].torrentUrl)
}
- return res
+ return created
}
async buildLegacyUpload (options: OverrideCommandOptions & {
async buildResumeUpload (options: OverrideCommandOptions & {
attributes: VideoEdit
- }) {
+ }): Promise<VideoCreateResult> {
const { attributes, expectedStatus } = options
let size = 0
}
}
- const initializeSessionRes = await this.prepareResumableUpload({ ...options, attributes, size, mimetype })
+ // Do not check status automatically, we'll check it manually
+ const initializeSessionRes = await this.prepareResumableUpload({ ...options, expectedStatus: null, attributes, size, mimetype })
const initStatus = initializeSessionRes.status
if (videoFilePath && initStatus === HttpStatusCode.CREATED_201) {
const result = await this.sendResumableChunks({ ...options, pathUploadId, videoFilePath, size })
- return result.body.video
+ if (result.statusCode === HttpStatusCode.OK_200) {
+ await this.endResumableUpload({ ...options, expectedStatus: HttpStatusCode.NO_CONTENT_204, pathUploadId })
+ }
+
+ return result.body?.video || result.body as any
}
const expectedInitStatus = expectedStatus === HttpStatusCode.OK_200
expect(initStatus).to.equal(expectedInitStatus)
- return initializeSessionRes.body.video as VideoCreateResult
+ return initializeSessionRes.body.video || initializeSessionRes.body
}
async prepareResumableUpload (options: OverrideCommandOptions & {
attributes: VideoEdit
size: number
mimetype: string
+
+ originalName?: string
+ lastModified?: number
}) {
- const { attributes, size, mimetype } = options
+ const { attributes, originalName, lastModified, size, mimetype } = options
const path = '/api/v1/videos/upload-resumable'
'X-Upload-Content-Type': mimetype,
'X-Upload-Content-Length': size.toString()
},
- fields: { filename: attributes.fixture, ...this.buildUploadFields(options.attributes) },
+ fields: {
+ filename: attributes.fixture,
+ originalName,
+ lastModified,
+
+ ...this.buildUploadFields(options.attributes)
+ },
+
+ // Fixture will be sent later
+ attaches: this.buildUploadAttaches(omit(options.attributes, 'fixture')),
implicitToken: true,
+
defaultExpectedStatus: null
})
}
})
}
+ endResumableUpload (options: OverrideCommandOptions & {
+ pathUploadId: string
+ }) {
+ return this.deleteRequest({
+ ...options,
+
+ path: '/api/v1/videos/upload-resumable',
+ rawQuery: options.pathUploadId,
+ implicitToken: true,
+ defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
+ })
+ }
+
quickUpload (options: OverrideCommandOptions & {
name: string
nsfw?: boolean
async randomUpload (options: OverrideCommandOptions & {
wait?: boolean // default true
- additionalParams?: VideoEdit & { prefixName: string }
+ additionalParams?: VideoEdit & { prefixName?: string }
} = {}) {
const { wait = true, additionalParams } = options
const prefixName = additionalParams?.prefixName || ''
const name = prefixName + buildUUID()
- const attributes = { name, additionalParams }
-
- if (wait) await waitJobs([ this.server ])
+ const attributes = { name, ...additionalParams }
const result = await this.upload({ ...options, attributes })
+ if (wait) await waitJobs([ this.server ])
+
return { ...result, name }
}
// ---------------------------------------------------------------------------
+ removeHLSFiles (options: OverrideCommandOptions & {
+ videoId: number | string
+ }) {
+ const path = '/api/v1/videos/' + options.videoId + '/hls'
+
+ return this.deleteRequest({
+ ...options,
+
+ path,
+ implicitToken: true,
+ defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
+ })
+ }
+
+ removeWebTorrentFiles (options: OverrideCommandOptions & {
+ videoId: number | string
+ }) {
+ const path = '/api/v1/videos/' + options.videoId + '/webtorrent'
+
+ return this.deleteRequest({
+ ...options,
+
+ path,
+ implicitToken: true,
+ defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
+ })
+ }
+
+ runTranscoding (options: OverrideCommandOptions & {
+ videoId: number | string
+ transcodingType: 'hls' | 'webtorrent'
+ }) {
+ const path = '/api/v1/videos/' + options.videoId + '/transcoding'
+
+ const fields: VideoTranscodingCreate = pick(options, [ 'transcodingType' ])
+
+ return this.postBodyRequest({
+ ...options,
+
+ path,
+ fields,
+ implicitToken: true,
+ defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
+ })
+ }
+
+ // ---------------------------------------------------------------------------
+
private buildListQuery (options: VideosCommonQuery) {
return pick(options, [
'start',
'languageOneOf',
'tagsOneOf',
'tagsAllOf',
- 'filter',
+ 'isLocal',
+ 'include',
'skipCount'
])
}
private buildUploadFields (attributes: VideoEdit) {
- return omit(attributes, [ 'thumbnailfile', 'previewfile' ])
+ return omit(attributes, [ 'fixture', 'thumbnailfile', 'previewfile' ])
}
private buildUploadAttaches (attributes: VideoEdit) {