1
0
Fork 0

Begin live tests

This commit is contained in:
Chocobozzz 2020-11-02 15:43:44 +01:00 committed by Chocobozzz
parent 77e9f859c6
commit af4ae64f6f
21 changed files with 472 additions and 31 deletions

View File

@ -58,8 +58,9 @@ elif [ "$1" = "api-2" ]; then
serverFiles=$(findTestFiles server/tests/api/server)
usersFiles=$(findTestFiles server/tests/api/users)
liveFiles=$(findTestFiles server/tests/api/live)
MOCHA_PARALLEL=true runTest 2 $serverFiles $usersFiles
MOCHA_PARALLEL=true runTest 2 $serverFiles $usersFiles liveFiles
elif [ "$1" = "api-3" ]; then
npm run build:server

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

After

Width:  |  Height:  |  Size: 40 KiB

View File

@ -223,7 +223,7 @@ function getAccountVideoRateFactory (rateType: VideoRateType) {
async function videoController (req: express.Request, res: express.Response) {
// We need more attributes
const video = await VideoModel.loadForGetAPI({ id: res.locals.onlyVideoWithRights.id }) as MVideoAPWithoutCaption
const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(res.locals.onlyVideoWithRights.id)
if (video.url.startsWith(WEBSERVER.URL) === false) return res.redirect(video.url)

View File

@ -189,7 +189,7 @@ async function addVideo (req: express.Request, res: express.Response) {
videoData.state = CONFIG.TRANSCODING.ENABLED ? VideoState.TO_TRANSCODE : VideoState.PUBLISHED
videoData.duration = videoPhysicalFile['duration'] // duration was added by a previous middleware
const video = new VideoModel(videoData) as MVideoDetails
const video = new VideoModel(videoData) as MVideoFullLight
video.url = getVideoActivityPubUrl(video) // We use the UUID, so set the URL after building the object
const videoFile = new VideoFileModel({

View File

@ -4,6 +4,7 @@ import { createReqFiles } from '@server/helpers/express-utils'
import { CONFIG } from '@server/initializers/config'
import { ASSETS_PATH, MIMETYPES } from '@server/initializers/constants'
import { getVideoActivityPubUrl } from '@server/lib/activitypub/url'
import { federateVideoIfNeeded } from '@server/lib/activitypub/videos'
import { buildLocalVideoFromReq, buildVideoThumbnailsFromReq, setVideoTags } from '@server/lib/video'
import { videoLiveAddValidator, videoLiveGetValidator, videoLiveUpdateValidator } from '@server/middlewares/validators/videos/video-live'
import { VideoLiveModel } from '@server/models/video/video-live'
@ -63,10 +64,13 @@ async function getLiveVideo (req: express.Request, res: express.Response) {
async function updateLiveVideo (req: express.Request, res: express.Response) {
const body: LiveVideoUpdate = req.body
const video = res.locals.videoAll
const videoLive = res.locals.videoLive
videoLive.saveReplay = body.saveReplay || false
await videoLive.save()
video.VideoLive = await videoLive.save()
await federateVideoIfNeeded(video, false)
return res.sendStatus(204)
}
@ -113,10 +117,12 @@ async function addLiveVideo (req: express.Request, res: express.Response) {
videoCreated.VideoChannel = res.locals.videoChannel
videoLive.videoId = videoCreated.id
await videoLive.save(sequelizeOptions)
videoCreated.VideoLive = await videoLive.save(sequelizeOptions)
await setVideoTags({ video, tags: videoInfo.tags, transaction: t })
await federateVideoIfNeeded(videoCreated, true, t)
logger.info('Video live %s with uuid %s created.', videoInfo.name, videoCreated.uuid)
return { videoCreated }

View File

@ -63,6 +63,7 @@ function sanitizeAndCheckVideoTorrentObject (video: any) {
if (!isBooleanValid(video.downloadEnabled)) video.downloadEnabled = true
if (!isBooleanValid(video.commentsEnabled)) video.commentsEnabled = false
if (!isBooleanValid(video.isLiveBroadcast)) video.isLiveBroadcast = false
if (!isBooleanValid(video.liveSaveReplay)) video.liveSaveReplay = false
return isActivityPubUrlValid(video.id) &&
isVideoNameValid(video.name) &&
@ -79,7 +80,6 @@ function sanitizeAndCheckVideoTorrentObject (video: any) {
isDateValid(video.updated) &&
(!video.originallyPublishedAt || isDateValid(video.originallyPublishedAt)) &&
(!video.content || isRemoteVideoContentValid(video.mediaType, video.content)) &&
video.url.length !== 0 &&
video.attributedTo.length !== 0
}

View File

@ -92,9 +92,9 @@ async function doesVideoChannelOfAccountExist (channelId: number, user: MUserAcc
return true
}
function checkUserCanManageVideo (user: MUser, video: MVideoAccountLight, right: UserRight, res: Response) {
function checkUserCanManageVideo (user: MUser, video: MVideoAccountLight, right: UserRight, res: Response, onlyOwned = true) {
// Retrieve the user who did the request
if (video.isOwned() === false) {
if (onlyOwned && video.isOwned() === false) {
res.status(403)
.json({ error: 'Cannot manage a video of another server.' })
.end()

View File

@ -1,3 +1,4 @@
import { VideoLiveModel } from '@server/models/video/video-live'
import * as Bluebird from 'bluebird'
import { maxBy, minBy } from 'lodash'
import * as magnetUtil from 'magnet-uri'
@ -84,7 +85,7 @@ async function federateVideoIfNeeded (videoArg: MVideoAPWithoutCaption, isNewVid
// Check this is not a blacklisted video, or unfederated blacklisted video
(video.isBlacklisted() === false || (isNewVideo === false && video.VideoBlacklist.unfederated === false)) &&
// Check the video is public/unlisted and published
video.hasPrivacyForFederation() && video.state === VideoState.PUBLISHED
video.hasPrivacyForFederation() && (video.state === VideoState.PUBLISHED || video.state === VideoState.WAITING_FOR_LIVE)
) {
// Fetch more attributes that we will need to serialize in AP object
if (isArray(video.VideoCaptions) === false) {
@ -424,6 +425,27 @@ async function updateVideoFromAP (options: {
await Promise.all(videoCaptionsPromises)
}
{
// Create or update existing live
if (video.isLive) {
const [ videoLive ] = await VideoLiveModel.upsert({
saveReplay: videoObject.liveSaveReplay,
videoId: video.id
}, { transaction: t, returning: true })
videoUpdated.VideoLive = videoLive
} else { // Delete existing live if it exists
await VideoLiveModel.destroy({
where: {
videoId: video.id
},
transaction: t
})
videoUpdated.VideoLive = null
}
}
return videoUpdated
})
@ -436,7 +458,7 @@ async function updateVideoFromAP (options: {
})
if (wasPrivateVideo || wasUnlistedVideo) Notifier.Instance.notifyOnNewVideoIfNeeded(videoUpdated) // Notify our users?
if (videoUpdated.isLive) PeerTubeSocket.Instance.sendVideoLiveNewState(video)
if (videoUpdated.isLive) PeerTubeSocket.Instance.sendVideoLiveNewState(videoUpdated)
logger.info('Remote video with uuid %s updated', videoObject.uuid)
@ -606,6 +628,16 @@ async function createVideo (videoObject: VideoObject, channel: MChannelAccountLi
videoCreated.VideoFiles = videoFiles
if (videoCreated.isLive) {
const videoLive = new VideoLiveModel({
streamKey: null,
saveReplay: videoObject.liveSaveReplay,
videoId: videoCreated.id
})
videoCreated.VideoLive = await videoLive.save({ transaction: t })
}
const autoBlacklisted = await autoBlacklistVideoIfNeeded({
video: videoCreated,
user: undefined,

View File

@ -16,14 +16,14 @@ const videoLiveGetValidator = [
param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'),
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
logger.debug('Checking videoLiveGetValidator parameters', { parameters: req.body })
logger.debug('Checking videoLiveGetValidator parameters', { parameters: req.params, user: res.locals.oauth.token.User.username })
if (areValidationErrors(req, res)) return
if (!await doesVideoExist(req.params.videoId, res, 'all')) return
// Check if the user who did the request is able to update the video
// Check if the user who did the request is able to get the live info
const user = res.locals.oauth.token.User
if (!checkUserCanManageVideo(user, res.locals.videoAll, UserRight.UPDATE_ANY_VIDEO, res)) return
if (!checkUserCanManageVideo(user, res.locals.videoAll, UserRight.GET_ANY_LIVE, res, false)) return
const videoLive = await VideoLiveModel.loadByVideoId(res.locals.videoAll.id)
if (!videoLive) return res.sendStatus(404)
@ -122,6 +122,10 @@ const videoLiveUpdateValidator = [
.json({ error: 'Cannot update a live that has already started' })
}
// Check the user can manage the live
const user = res.locals.oauth.token.User
if (!checkUserCanManageVideo(user, res.locals.videoAll, UserRight.GET_ANY_LIVE, res)) return
return next()
}
]

View File

@ -352,11 +352,20 @@ function videoModelToActivityPubObject (video: MVideoAP): VideoObject {
sensitive: video.nsfw,
waitTranscoding: video.waitTranscoding,
isLiveBroadcast: video.isLive,
liveSaveReplay: video.isLive
? video.VideoLive.saveReplay
: null,
state: video.state,
commentsEnabled: video.commentsEnabled,
downloadEnabled: video.downloadEnabled,
published: video.publishedAt.toISOString(),
originallyPublishedAt: video.originallyPublishedAt ? video.originallyPublishedAt.toISOString() : null,
originallyPublishedAt: video.originallyPublishedAt
? video.originallyPublishedAt.toISOString()
: null,
updated: video.updatedAt.toISOString(),
mediaType: 'text/markdown',
content: video.description,

View File

@ -93,7 +93,11 @@ export class VideoLiveModel extends Model<VideoLiveModel> {
toFormattedJSON (): LiveVideo {
return {
rtmpUrl: WEBSERVER.RTMP_URL,
// If we don't have a stream key, it means this is a remote live so we don't specify the rtmp URL
rtmpUrl: this.streamKey
? WEBSERVER.RTMP_URL
: null,
streamKey: this.streamKey,
saveReplay: this.saveReplay
}

View File

@ -26,6 +26,7 @@ import {
} from 'sequelize-typescript'
import { buildNSFWFilter } from '@server/helpers/express-utils'
import { getPrivaciesForFederation, isPrivacyForFederation } from '@server/helpers/video'
import { LiveManager } from '@server/lib/live-manager'
import { getHLSDirectory, getTorrentFileName, getTorrentFilePath, getVideoFilename, getVideoFilePath } from '@server/lib/video-paths'
import { getServerActor } from '@server/models/application/application'
import { ModelCache } from '@server/models/model-cache'
@ -121,14 +122,13 @@ import {
videoModelToFormattedJSON
} from './video-format-utils'
import { VideoImportModel } from './video-import'
import { VideoLiveModel } from './video-live'
import { VideoPlaylistElementModel } from './video-playlist-element'
import { buildListQuery, BuildVideosQueryOptions, wrapForAPIResults } from './video-query-builder'
import { VideoShareModel } from './video-share'
import { VideoStreamingPlaylistModel } from './video-streaming-playlist'
import { VideoTagModel } from './video-tag'
import { VideoViewModel } from './video-view'
import { LiveManager } from '@server/lib/live-manager'
import { VideoLiveModel } from './video-live'
export enum ScopeNames {
AVAILABLE_FOR_LIST_IDS = 'AVAILABLE_FOR_LIST_IDS',
@ -142,7 +142,8 @@ export enum ScopeNames {
WITH_STREAMING_PLAYLISTS = 'WITH_STREAMING_PLAYLISTS',
WITH_USER_ID = 'WITH_USER_ID',
WITH_IMMUTABLE_ATTRIBUTES = 'WITH_IMMUTABLE_ATTRIBUTES',
WITH_THUMBNAILS = 'WITH_THUMBNAILS'
WITH_THUMBNAILS = 'WITH_THUMBNAILS',
WITH_LIVE = 'WITH_LIVE'
}
export type ForAPIOptions = {
@ -245,6 +246,14 @@ export type AvailableForListIDsOptions = {
}
]
},
[ScopeNames.WITH_LIVE]: {
include: [
{
model: VideoLiveModel,
required: false
}
]
},
[ScopeNames.WITH_USER_ID]: {
include: [
{
@ -943,6 +952,17 @@ export class VideoModel extends Model<VideoModel> {
}
]
},
{
model: VideoStreamingPlaylistModel.unscoped(),
required: false,
include: [
{
model: VideoFileModel,
required: false
}
]
},
VideoLiveModel,
VideoFileModel,
TagModel
]
@ -1330,7 +1350,8 @@ export class VideoModel extends Model<VideoModel> {
ScopeNames.WITH_SCHEDULED_UPDATE,
ScopeNames.WITH_WEBTORRENT_FILES,
ScopeNames.WITH_STREAMING_PLAYLISTS,
ScopeNames.WITH_THUMBNAILS
ScopeNames.WITH_THUMBNAILS,
ScopeNames.WITH_LIVE
]
if (userId) {
@ -1362,6 +1383,7 @@ export class VideoModel extends Model<VideoModel> {
ScopeNames.WITH_ACCOUNT_DETAILS,
ScopeNames.WITH_SCHEDULED_UPDATE,
ScopeNames.WITH_THUMBNAILS,
ScopeNames.WITH_LIVE,
{ method: [ ScopeNames.WITH_WEBTORRENT_FILES, true ] },
{ method: [ ScopeNames.WITH_STREAMING_PLAYLISTS, true ] }
]

View File

@ -0,0 +1 @@
export * from './live'

View File

@ -0,0 +1,351 @@
/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
import 'mocha'
import * as chai from 'chai'
import { LiveVideo, LiveVideoCreate, VideoDetails, VideoPrivacy } from '@shared/models'
import {
acceptChangeOwnership,
cleanupTests,
createLive,
doubleFollow,
flushAndRunMultipleServers,
getLive,
getVideo,
getVideosList,
makeRawRequest,
removeVideo,
ServerInfo,
setAccessTokensToServers,
setDefaultVideoChannel,
testImage,
updateCustomSubConfig,
updateLive,
waitJobs
} from '../../../../shared/extra-utils'
const expect = chai.expect
describe('Test live', function () {
let servers: ServerInfo[] = []
let liveVideoUUID: string
before(async function () {
this.timeout(120000)
servers = await flushAndRunMultipleServers(2)
// Get the access tokens
await setAccessTokensToServers(servers)
await setDefaultVideoChannel(servers)
await updateCustomSubConfig(servers[0].url, servers[0].accessToken, {
live: {
enabled: true,
allowReplay: true
}
})
// Server 1 and server 2 follow each other
await doubleFollow(servers[0], servers[1])
})
describe('Live creation, update and delete', function () {
it('Should create a live with the appropriate parameters', async function () {
this.timeout(20000)
const attributes: LiveVideoCreate = {
category: 1,
licence: 2,
language: 'fr',
description: 'super live description',
support: 'support field',
channelId: servers[0].videoChannel.id,
nsfw: false,
waitTranscoding: false,
name: 'my super live',
tags: [ 'tag1', 'tag2' ],
commentsEnabled: false,
downloadEnabled: false,
saveReplay: true,
privacy: VideoPrivacy.PUBLIC,
previewfile: 'video_short1-preview.webm.jpg',
thumbnailfile: 'video_short1.webm.jpg'
}
const res = await createLive(servers[0].url, servers[0].accessToken, attributes)
liveVideoUUID = res.body.video.uuid
await waitJobs(servers)
for (const server of servers) {
const resVideo = await getVideo(server.url, liveVideoUUID)
const video: VideoDetails = resVideo.body
expect(video.category.id).to.equal(1)
expect(video.licence.id).to.equal(2)
expect(video.language.id).to.equal('fr')
expect(video.description).to.equal('super live description')
expect(video.support).to.equal('support field')
expect(video.channel.name).to.equal(servers[0].videoChannel.name)
expect(video.channel.host).to.equal(servers[0].videoChannel.host)
expect(video.nsfw).to.be.false
expect(video.waitTranscoding).to.be.false
expect(video.name).to.equal('my super live')
expect(video.tags).to.deep.equal([ 'tag1', 'tag2' ])
expect(video.commentsEnabled).to.be.false
expect(video.downloadEnabled).to.be.false
expect(video.privacy.id).to.equal(VideoPrivacy.PUBLIC)
await testImage(server.url, 'video_short1-preview.webm', video.previewPath)
await testImage(server.url, 'video_short1.webm', video.thumbnailPath)
const resLive = await getLive(server.url, server.accessToken, liveVideoUUID)
const live: LiveVideo = resLive.body
if (server.url === servers[0].url) {
expect(live.rtmpUrl).to.equal('rtmp://' + server.hostname + ':1936/live')
expect(live.streamKey).to.not.be.empty
} else {
expect(live.rtmpUrl).to.be.null
expect(live.streamKey).to.be.null
}
expect(live.saveReplay).to.be.true
}
})
it('Should have a default preview and thumbnail', async function () {
this.timeout(20000)
const attributes: LiveVideoCreate = {
name: 'default live thumbnail',
channelId: servers[0].videoChannel.id,
privacy: VideoPrivacy.UNLISTED,
nsfw: true
}
const res = await createLive(servers[0].url, servers[0].accessToken, attributes)
const videoId = res.body.video.uuid
await waitJobs(servers)
for (const server of servers) {
const resVideo = await getVideo(server.url, videoId)
const video: VideoDetails = resVideo.body
expect(video.privacy.id).to.equal(VideoPrivacy.UNLISTED)
expect(video.nsfw).to.be.true
await makeRawRequest(server.url + video.thumbnailPath, 200)
await makeRawRequest(server.url + video.previewPath, 200)
}
})
it('Should not have the live listed since nobody streams into', async function () {
for (const server of servers) {
const res = await getVideosList(server.url)
expect(res.body.total).to.equal(0)
expect(res.body.data).to.have.lengthOf(0)
}
})
it('Should not be able to update a live of another server', async function () {
await updateLive(servers[1].url, servers[1].accessToken, liveVideoUUID, { saveReplay: false }, 403)
})
it('Should update the live', async function () {
this.timeout(10000)
await updateLive(servers[0].url, servers[0].accessToken, liveVideoUUID, { saveReplay: false })
await waitJobs(servers)
})
it('Have the live updated', async function () {
for (const server of servers) {
const res = await getLive(server.url, server.accessToken, liveVideoUUID)
const live: LiveVideo = res.body
if (server.url === servers[0].url) {
expect(live.rtmpUrl).to.equal('rtmp://' + server.hostname + ':1936/live')
expect(live.streamKey).to.not.be.empty
} else {
expect(live.rtmpUrl).to.be.null
expect(live.streamKey).to.be.null
}
expect(live.saveReplay).to.be.false
}
})
it('Delete the live', async function () {
this.timeout(10000)
await removeVideo(servers[0].url, servers[0].accessToken, liveVideoUUID)
await waitJobs(servers)
})
it('Should have the live deleted', async function () {
for (const server of servers) {
await getVideo(server.url, liveVideoUUID, 404)
await getLive(server.url, server.accessToken, liveVideoUUID, 404)
}
})
})
describe('Test live constraints', function () {
it('Should not have size limit if save replay is disabled', async function () {
})
it('Should have size limit if save replay is enabled', async function () {
// daily quota + total quota
})
it('Should have max duration limit', async function () {
})
})
describe('With save replay disabled', function () {
it('Should correctly create and federate the "waiting for stream" live', async function () {
})
it('Should correctly have updated the live and federated it when streaming in the live', async function () {
})
it('Should correctly delete the video and the live after the stream ended', async function () {
// Wait 10 seconds
// get video 404
// get video federation 404
// check cleanup
})
it('Should correctly terminate the stream on blacklist and delete the live', async function () {
// Wait 10 seconds
// get video 404
// get video federation 404
// check cleanup
})
it('Should correctly terminate the stream on delete and delete the video', async function () {
// Wait 10 seconds
// get video 404
// get video federation 404
// check cleanup
})
})
describe('With save replay enabled', function () {
it('Should correctly create and federate the "waiting for stream" live', async function () {
})
it('Should correctly have updated the live and federated it when streaming in the live', async function () {
})
it('Should correctly have saved the live and federated it after the streaming', async function () {
})
it('Should update the saved live and correctly federate the updated attributes', async function () {
})
it('Should have cleaned up the live files', async function () {
})
it('Should correctly terminate the stream on blacklist and blacklist the saved replay video', async function () {
// Wait 10 seconds
// get video -> blacklisted
// get video federation -> blacklisted
// check cleanup live files quand meme
})
it('Should correctly terminate the stream on delete and delete the video', async function () {
// Wait 10 seconds
// get video 404
// get video federation 404
// check cleanup
})
})
describe('Stream checks', function () {
it('Should not allow a stream without the appropriate path', async function () {
})
it('Should not allow a stream without the appropriate stream key', async function () {
})
it('Should not allow a stream on a live that was blacklisted', async function () {
})
it('Should not allow a stream on a live that was deleted', async function () {
})
})
describe('Live transcoding', function () {
it('Should enable transcoding without additional resolutions', async function () {
// enable
// stream
// wait federation + test
})
it('Should enable transcoding with some resolutions', async function () {
// enable
// stream
// wait federation + test
})
it('Should enable transcoding with some resolutions and correctly save them', async function () {
// enable
// stream
// end stream
// wait federation + test
})
it('Should correctly have cleaned up the live files', async function () {
// check files
})
})
describe('Live socket messages', function () {
it('Should correctly send a message when the live starts', async function () {
// local
// federation
})
it('Should correctly send a message when the live ends', async function () {
// local
// federation
})
})
after(async function () {
await cleanupTests(servers)
})
})

View File

@ -21,6 +21,7 @@ import { MThumbnail } from './thumbnail'
import { MVideoBlacklist, MVideoBlacklistLight, MVideoBlacklistUnfederated } from './video-blacklist'
import { MScheduleVideoUpdate } from './schedule-video-update'
import { MUserVideoHistoryTime } from '../user/user-video-history'
import { MVideoLive } from './video-live'
type Use<K extends keyof VideoModel, M> = PickWith<VideoModel, K, M>
@ -29,7 +30,7 @@ type Use<K extends keyof VideoModel, M> = PickWith<VideoModel, K, M>
export type MVideo =
Omit<VideoModel, 'VideoChannel' | 'Tags' | 'Thumbnails' | 'VideoPlaylistElements' | 'VideoAbuses' |
'VideoFiles' | 'VideoStreamingPlaylists' | 'VideoShares' | 'AccountVideoRates' | 'VideoComments' | 'VideoViews' | 'UserVideoHistories' |
'ScheduleVideoUpdate' | 'VideoBlacklist' | 'VideoImport' | 'VideoCaptions'>
'ScheduleVideoUpdate' | 'VideoBlacklist' | 'VideoImport' | 'VideoCaptions' | 'VideoLive'>
// ############################################################################
@ -151,7 +152,8 @@ export type MVideoFullLight =
Use<'UserVideoHistories', MUserVideoHistoryTime[]> &
Use<'VideoFiles', MVideoFile[]> &
Use<'ScheduleVideoUpdate', MScheduleVideoUpdate> &
Use<'VideoStreamingPlaylists', MStreamingPlaylistFiles[]>
Use<'VideoStreamingPlaylists', MStreamingPlaylistFiles[]> &
Use<'VideoLive', MVideoLive>
// ############################################################################
@ -165,7 +167,8 @@ export type MVideoAP =
Use<'VideoCaptions', MVideoCaptionLanguageUrl[]> &
Use<'VideoBlacklist', MVideoBlacklistUnfederated> &
Use<'VideoFiles', MVideoFileRedundanciesOpt[]> &
Use<'Thumbnails', MThumbnail[]>
Use<'Thumbnails', MThumbnail[]> &
Use<'VideoLive', MVideoLive>
export type MVideoAPWithoutCaption = Omit<MVideoAP, 'VideoCaptions'>

View File

@ -10,10 +10,12 @@ import { randomInt } from '../../core-utils/miscs/miscs'
interface ServerInfo {
app: ChildProcess
url: string
host: string
hostname: string
port: number
parallel: boolean
internalServerNumber: number
serverNumber: number
@ -109,6 +111,7 @@ async function flushAndRunServer (serverNumber: number, configOverride?: Object,
serverNumber,
url: `http://localhost:${port}`,
host: `localhost:${port}`,
hostname: 'localhost',
client: {
id: null,
secret: null

View File

@ -2,8 +2,8 @@ import * as ffmpeg from 'fluent-ffmpeg'
import { LiveVideoCreate, LiveVideoUpdate, VideoDetails, VideoState } from '@shared/models'
import { buildAbsoluteFixturePath, wait } from '../miscs/miscs'
import { makeGetRequest, makePutBodyRequest, makeUploadRequest } from '../requests/requests'
import { ServerInfo } from '../server/servers'
import { getVideo, getVideoWithToken } from './videos'
import { getVideoWithToken } from './videos'
import { omit } from 'lodash'
function getLive (url: string, token: string, videoId: number | string, statusCodeExpected = 200) {
const path = '/api/v1/videos/live'
@ -31,16 +31,18 @@ function updateLive (url: string, token: string, videoId: number | string, field
function createLive (url: string, token: string, fields: LiveVideoCreate, statusCodeExpected = 200) {
const path = '/api/v1/videos/live'
let attaches: any = {}
if (fields.thumbnailfile) attaches = { thumbnailfile: fields.thumbnailfile }
if (fields.previewfile) attaches = { previewfile: fields.previewfile }
const attaches: any = {}
if (fields.thumbnailfile) attaches.thumbnailfile = fields.thumbnailfile
if (fields.previewfile) attaches.previewfile = fields.previewfile
const updatedFields = omit(fields, 'thumbnailfile', 'previewfile')
return makeUploadRequest({
url,
path,
token,
attaches,
fields,
fields: updatedFields,
statusCodeExpected
})
}

View File

@ -21,7 +21,9 @@ export interface VideoObject {
views: number
sensitive: boolean
isLiveBroadcast: boolean
liveSaveReplay: boolean
commentsEnabled: boolean
downloadEnabled: boolean

View File

@ -30,6 +30,7 @@ export const enum UserRight {
UPDATE_ANY_VIDEO,
UPDATE_ANY_VIDEO_PLAYLIST,
GET_ANY_LIVE,
SEE_ALL_VIDEOS,
CHANGE_VIDEO_OWNERSHIP,

View File

@ -18,6 +18,6 @@ export interface VideoCreate {
scheduleUpdate?: VideoScheduleUpdate
originallyPublishedAt?: Date | string
thumbnailfile?: Blob
previewfile?: Blob
thumbnailfile?: Blob | string
previewfile?: Blob | string
}