Add ability to search a video with an URL

pull/974/head
Chocobozzz 2018-08-22 11:51:39 +02:00
parent 22a16e36f6
commit f6eebcb336
19 changed files with 244 additions and 135 deletions

View File

@ -4,7 +4,7 @@
#search-video {
@include peertube-input-text($search-input-width);
margin-right: 15px;
padding-right: 25px; // For the search icon
padding-right: 40px; // For the search icon
&::placeholder {
color: #000;

View File

@ -1,2 +1,2 @@
export * from './user-subscription.service'
export * from './subscribe-button.component'
export * from './subscribe-button.component'

View File

@ -17,11 +17,6 @@ export class VideoChannelService {
videoChannelLoaded = new ReplaySubject<VideoChannel>(1)
constructor (
private authHttp: HttpClient,
private restExtractor: RestExtractor
) {}
static extractVideoChannels (result: ResultList<VideoChannelServer>) {
const videoChannels: VideoChannel[] = []
@ -32,6 +27,11 @@ export class VideoChannelService {
return { data: videoChannels, total: result.total }
}
constructor (
private authHttp: HttpClient,
private restExtractor: RestExtractor
) { }
getVideoChannel (videoChannelName: string) {
return this.authHttp.get<VideoChannel>(VideoChannelService.BASE_VIDEO_CHANNEL_URL + videoChannelName)
.pipe(

View File

@ -13,6 +13,8 @@ import {
videosSearchSortValidator
} from '../../middlewares'
import { VideosSearchQuery } from '../../../shared/models/search'
import { getOrCreateAccountAndVideoAndChannel } from '../../lib/activitypub'
import { logger } from '../../helpers/logger'
const searchRouter = express.Router()
@ -33,9 +35,16 @@ export { searchRouter }
// ---------------------------------------------------------------------------
async function searchVideos (req: express.Request, res: express.Response) {
function searchVideos (req: express.Request, res: express.Response) {
const query: VideosSearchQuery = req.query
if (query.search.startsWith('http://') || query.search.startsWith('https://')) {
return searchVideoUrl(query.search, res)
}
return searchVideosDB(query, res)
}
async function searchVideosDB (query: VideosSearchQuery, res: express.Response) {
const options = Object.assign(query, {
includeLocalVideos: true,
nsfw: buildNSFWFilter(res, query.nsfw)
@ -44,3 +53,27 @@ async function searchVideos (req: express.Request, res: express.Response) {
return res.json(getFormattedObjects(resultList.data, resultList.total))
}
async function searchVideoUrl (url: string, res: express.Response) {
let video: VideoModel
try {
const syncParam = {
likes: false,
dislikes: false,
shares: false,
comments: false,
thumbnail: true
}
const res = await getOrCreateAccountAndVideoAndChannel(url, syncParam)
video = res ? res.video : undefined
} catch (err) {
logger.info('Cannot search remote video %s.', url)
}
return res.json({
total: video ? 1 : 0,
data: video ? [ video.toFormattedJSON() ] : []
})
}

View File

@ -112,6 +112,7 @@ const JOB_TTL: { [ id in JobType ]: number } = {
'email': 60000 * 10 // 10 minutes
}
const BROADCAST_CONCURRENCY = 10 // How many requests in parallel we do in activitypub-http-broadcast job
const CRAWL_REQUEST_CONCURRENCY = 5 // How many requests in parallel to fetch remote data (likes, shares...)
const JOB_REQUEST_TIMEOUT = 3000 // 3 seconds
const JOB_COMPLETED_LIFETIME = 60000 * 60 * 24 * 2 // 2 days
@ -643,6 +644,7 @@ export {
STATIC_DOWNLOAD_PATHS,
RATES_LIMIT,
VIDEO_EXT_MIMETYPE,
CRAWL_REQUEST_CONCURRENCY,
JOB_COMPLETED_LIFETIME,
VIDEO_IMPORT_STATES,
VIDEO_VIEW_LIFETIME,

View File

@ -177,7 +177,8 @@ async function addFetchOutboxJob (actor: ActorModel) {
}
const payload = {
uris: [ actor.outboxUrl ]
uri: actor.outboxUrl,
type: 'activity' as 'activity'
}
return JobQueue.Instance.createJob({ type: 'activitypub-http-fetcher', payload })
@ -248,6 +249,7 @@ function saveActorAndServerAndModelIfNotExist (
} else if (actorCreated.type === 'Group') { // Video channel
actorCreated.VideoChannel = await saveVideoChannel(actorCreated, result, ownerActor, t)
actorCreated.VideoChannel.Actor = actorCreated
actorCreated.VideoChannel.Account = ownerActor.Account
}
return actorCreated

View File

@ -1,8 +1,9 @@
import { ACTIVITY_PUB, JOB_REQUEST_TIMEOUT } from '../../initializers'
import { doRequest } from '../../helpers/requests'
import { logger } from '../../helpers/logger'
import Bluebird = require('bluebird')
async function crawlCollectionPage <T> (uri: string, handler: (items: T[]) => Promise<any>) {
async function crawlCollectionPage <T> (uri: string, handler: (items: T[]) => Promise<any> | Bluebird<any>) {
logger.info('Crawling ActivityPub data on %s.', uri)
const options = {

View File

@ -24,10 +24,8 @@ export {
async function processVideoShare (actorAnnouncer: ActorModel, activity: ActivityAnnounce) {
const objectUri = typeof activity.object === 'string' ? activity.object : activity.object.id
let video: VideoModel
const res = await getOrCreateAccountAndVideoAndChannel(objectUri)
video = res.video
const { video } = await getOrCreateAccountAndVideoAndChannel(objectUri)
return sequelizeTypescript.transaction(async t => {
// Add share entry

View File

@ -23,7 +23,7 @@ async function processCreateActivity (activity: ActivityCreate) {
} else if (activityType === 'Dislike') {
return retryTransactionWrapper(processCreateDislike, actor, activity)
} else if (activityType === 'Video') {
return processCreateVideo(actor, activity)
return processCreateVideo(activity)
} else if (activityType === 'Flag') {
return retryTransactionWrapper(processCreateVideoAbuse, actor, activityObject as VideoAbuseObject)
} else if (activityType === 'Note') {
@ -42,13 +42,10 @@ export {
// ---------------------------------------------------------------------------
async function processCreateVideo (
actor: ActorModel,
activity: ActivityCreate
) {
async function processCreateVideo (activity: ActivityCreate) {
const videoToCreateData = activity.object as VideoTorrentObject
const { video } = await getOrCreateAccountAndVideoAndChannel(videoToCreateData, actor)
const { video } = await getOrCreateAccountAndVideoAndChannel(videoToCreateData)
return video
}

View File

@ -2,12 +2,13 @@ import { VideoCommentObject } from '../../../shared/models/activitypub/objects/v
import { sanitizeAndCheckVideoCommentObject } from '../../helpers/custom-validators/activitypub/video-comments'
import { logger } from '../../helpers/logger'
import { doRequest } from '../../helpers/requests'
import { ACTIVITY_PUB } from '../../initializers'
import { ACTIVITY_PUB, CRAWL_REQUEST_CONCURRENCY } from '../../initializers'
import { ActorModel } from '../../models/activitypub/actor'
import { VideoModel } from '../../models/video/video'
import { VideoCommentModel } from '../../models/video/video-comment'
import { getOrCreateActorAndServerAndModel } from './actor'
import { getOrCreateAccountAndVideoAndChannel } from './videos'
import * as Bluebird from 'bluebird'
async function videoCommentActivityObjectToDBAttributes (video: VideoModel, actor: ActorModel, comment: VideoCommentObject) {
let originCommentId: number = null
@ -38,9 +39,9 @@ async function videoCommentActivityObjectToDBAttributes (video: VideoModel, acto
}
async function addVideoComments (commentUrls: string[], instance: VideoModel) {
for (const commentUrl of commentUrls) {
await addVideoComment(instance, commentUrl)
}
return Bluebird.map(commentUrls, commentUrl => {
return addVideoComment(instance, commentUrl)
}, { concurrency: CRAWL_REQUEST_CONCURRENCY })
}
async function addVideoComment (videoInstance: VideoModel, commentUrl: string) {

View File

@ -11,7 +11,7 @@ import { isVideoFileInfoHashValid } from '../../helpers/custom-validators/videos
import { retryTransactionWrapper } from '../../helpers/database-utils'
import { logger } from '../../helpers/logger'
import { doRequest, doRequestAndSaveToFile } from '../../helpers/requests'
import { ACTIVITY_PUB, CONFIG, REMOTE_SCHEME, sequelizeTypescript, VIDEO_MIMETYPE_EXT } from '../../initializers'
import { ACTIVITY_PUB, CONFIG, CRAWL_REQUEST_CONCURRENCY, REMOTE_SCHEME, sequelizeTypescript, VIDEO_MIMETYPE_EXT } from '../../initializers'
import { AccountVideoRateModel } from '../../models/account/account-video-rate'
import { ActorModel } from '../../models/activitypub/actor'
import { TagModel } from '../../models/video/tag'
@ -26,6 +26,8 @@ import { sendCreateVideo, sendUpdateVideo } from './send'
import { shareVideoByServerAndChannel } from './index'
import { isArray } from '../../helpers/custom-validators/misc'
import { VideoCaptionModel } from '../../models/video/video-caption'
import { JobQueue } from '../job-queue'
import { ActivitypubHttpFetcherPayload } from '../job-queue/handlers/activitypub-http-fetcher'
async function federateVideoIfNeeded (video: VideoModel, isNewVideo: boolean, transaction?: sequelize.Transaction) {
// If the video is not private and published, we federate it
@ -178,10 +180,10 @@ function getOrCreateVideoChannel (videoObject: VideoTorrentObject) {
return getOrCreateActorAndServerAndModel(channel.id)
}
async function getOrCreateVideo (videoObject: VideoTorrentObject, channelActor: ActorModel) {
async function getOrCreateVideo (videoObject: VideoTorrentObject, channelActor: ActorModel, waitThumbnail = false) {
logger.debug('Adding remote video %s.', videoObject.id)
return sequelizeTypescript.transaction(async t => {
const videoCreated: VideoModel = await sequelizeTypescript.transaction(async t => {
const sequelizeOptions = {
transaction: t
}
@ -191,10 +193,6 @@ async function getOrCreateVideo (videoObject: VideoTorrentObject, channelActor:
const videoData = await videoActivityObjectToDBAttributes(channelActor.VideoChannel, videoObject, videoObject.to)
const video = VideoModel.build(videoData)
// Don't block on remote HTTP request (we are in a transaction!)
generateThumbnailFromUrl(video, videoObject.icon)
.catch(err => logger.warn('Cannot generate thumbnail of %s.', videoObject.id, { err }))
const videoCreated = await video.save(sequelizeOptions)
// Process files
@ -222,68 +220,100 @@ async function getOrCreateVideo (videoObject: VideoTorrentObject, channelActor:
videoCreated.VideoChannel = channelActor.VideoChannel
return videoCreated
})
const p = generateThumbnailFromUrl(videoCreated, videoObject.icon)
.catch(err => logger.warn('Cannot generate thumbnail of %s.', videoObject.id, { err }))
if (waitThumbnail === true) await p
return videoCreated
}
async function getOrCreateAccountAndVideoAndChannel (videoObject: VideoTorrentObject | string, actor?: ActorModel) {
type SyncParam = {
likes: boolean,
dislikes: boolean,
shares: boolean,
comments: boolean,
thumbnail: boolean
}
async function getOrCreateAccountAndVideoAndChannel (
videoObject: VideoTorrentObject | string,
syncParam: SyncParam = { likes: true, dislikes: true, shares: true, comments: true, thumbnail: true }
) {
const videoUrl = typeof videoObject === 'string' ? videoObject : videoObject.id
const videoFromDatabase = await VideoModel.loadByUrlAndPopulateAccount(videoUrl)
if (videoFromDatabase) {
return {
video: videoFromDatabase,
actor: videoFromDatabase.VideoChannel.Account.Actor,
channelActor: videoFromDatabase.VideoChannel.Actor
}
}
if (videoFromDatabase) return { video: videoFromDatabase }
videoObject = await fetchRemoteVideo(videoUrl)
if (!videoObject) throw new Error('Cannot fetch remote video with url: ' + videoUrl)
const fetchedVideo = await fetchRemoteVideo(videoUrl)
if (!fetchedVideo) throw new Error('Cannot fetch remote video with url: ' + videoUrl)
if (!actor) {
const actorObj = videoObject.attributedTo.find(a => a.type === 'Person')
if (!actorObj) throw new Error('Cannot find associated actor to video ' + videoObject.url)
actor = await getOrCreateActorAndServerAndModel(actorObj.id)
}
const channelActor = await getOrCreateVideoChannel(videoObject)
const video = await retryTransactionWrapper(getOrCreateVideo, videoObject, channelActor)
const channelActor = await getOrCreateVideoChannel(fetchedVideo)
const video = await retryTransactionWrapper(getOrCreateVideo, fetchedVideo, channelActor, syncParam.thumbnail)
// Process outside the transaction because we could fetch remote data
logger.info('Adding likes of video %s.', video.uuid)
await crawlCollectionPage<string>(videoObject.likes, (items) => createRates(items, video, 'like'))
logger.info('Adding dislikes of video %s.', video.uuid)
await crawlCollectionPage<string>(videoObject.dislikes, (items) => createRates(items, video, 'dislike'))
logger.info('Adding likes/dislikes/shares/comments of video %s.', video.uuid)
logger.info('Adding shares of video %s.', video.uuid)
await crawlCollectionPage<string>(videoObject.shares, (items) => addVideoShares(items, video))
const jobPayloads: ActivitypubHttpFetcherPayload[] = []
logger.info('Adding comments of video %s.', video.uuid)
await crawlCollectionPage<string>(videoObject.comments, (items) => addVideoComments(items, video))
if (syncParam.likes === true) {
await crawlCollectionPage<string>(fetchedVideo.likes, items => createRates(items, video, 'like'))
.catch(err => logger.error('Cannot add likes of video %s.', video.uuid, { err }))
} else {
jobPayloads.push({ uri: fetchedVideo.likes, videoId: video.id, type: 'video-likes' as 'video-likes' })
}
return { actor, channelActor, video }
if (syncParam.dislikes === true) {
await crawlCollectionPage<string>(fetchedVideo.dislikes, items => createRates(items, video, 'dislike'))
.catch(err => logger.error('Cannot add dislikes of video %s.', video.uuid, { err }))
} else {
jobPayloads.push({ uri: fetchedVideo.dislikes, videoId: video.id, type: 'video-dislikes' as 'video-dislikes' })
}
if (syncParam.shares === true) {
await crawlCollectionPage<string>(fetchedVideo.shares, items => addVideoShares(items, video))
.catch(err => logger.error('Cannot add shares of video %s.', video.uuid, { err }))
} else {
jobPayloads.push({ uri: fetchedVideo.shares, videoId: video.id, type: 'video-shares' as 'video-shares' })
}
if (syncParam.comments === true) {
await crawlCollectionPage<string>(fetchedVideo.comments, items => addVideoComments(items, video))
.catch(err => logger.error('Cannot add comments of video %s.', video.uuid, { err }))
} else {
jobPayloads.push({ uri: fetchedVideo.shares, videoId: video.id, type: 'video-shares' as 'video-shares' })
}
await Bluebird.map(jobPayloads, payload => JobQueue.Instance.createJob({ type: 'activitypub-http-fetcher', payload }))
return { video }
}
async function createRates (actorUrls: string[], video: VideoModel, rate: VideoRateType) {
let rateCounts = 0
const tasks: Bluebird<number>[] = []
for (const actorUrl of actorUrls) {
const actor = await getOrCreateActorAndServerAndModel(actorUrl)
const p = AccountVideoRateModel
.create({
videoId: video.id,
accountId: actor.Account.id,
type: rate
})
.then(() => rateCounts += 1)
await Bluebird.map(actorUrls, async actorUrl => {
try {
const actor = await getOrCreateActorAndServerAndModel(actorUrl)
const [ , created ] = await AccountVideoRateModel
.findOrCreate({
where: {
videoId: video.id,
accountId: actor.Account.id
},
defaults: {
videoId: video.id,
accountId: actor.Account.id,
type: rate
}
})
tasks.push(p)
}
await Promise.all(tasks)
if (created) rateCounts += 1
} catch (err) {
logger.warn('Cannot add rate %s for actor %s.', rate, actorUrl, { err })
}
}, { concurrency: CRAWL_REQUEST_CONCURRENCY })
logger.info('Adding %d %s to video %s.', rateCounts, rate, video.uuid)
@ -294,34 +324,35 @@ async function createRates (actorUrls: string[], video: VideoModel, rate: VideoR
}
async function addVideoShares (shareUrls: string[], instance: VideoModel) {
for (const shareUrl of shareUrls) {
// Fetch url
const { body } = await doRequest({
uri: shareUrl,
json: true,
activityPub: true
})
if (!body || !body.actor) {
logger.warn('Cannot add remote share with url: %s, skipping...', shareUrl)
continue
}
await Bluebird.map(shareUrls, async shareUrl => {
try {
// Fetch url
const { body } = await doRequest({
uri: shareUrl,
json: true,
activityPub: true
})
if (!body || !body.actor) throw new Error('Body of body actor is invalid')
const actorUrl = body.actor
const actor = await getOrCreateActorAndServerAndModel(actorUrl)
const actorUrl = body.actor
const actor = await getOrCreateActorAndServerAndModel(actorUrl)
const entry = {
actorId: actor.id,
videoId: instance.id,
url: shareUrl
}
await VideoShareModel.findOrCreate({
where: {
const entry = {
actorId: actor.id,
videoId: instance.id,
url: shareUrl
},
defaults: entry
})
}
}
await VideoShareModel.findOrCreate({
where: {
url: shareUrl
},
defaults: entry
})
} catch (err) {
logger.warn('Cannot add share %s.', shareUrl, { err })
}
}, { concurrency: CRAWL_REQUEST_CONCURRENCY })
}
async function fetchRemoteVideo (videoUrl: string): Promise<VideoTorrentObject> {
@ -355,5 +386,6 @@ export {
videoFileActivityUrlToDBAttributes,
getOrCreateVideo,
getOrCreateVideoChannel,
addVideoShares
addVideoShares,
createRates
}

View File

@ -1,22 +1,36 @@
import * as Bull from 'bull'
import { logger } from '../../../helpers/logger'
import { processActivities } from '../../activitypub/process'
import { ActivitypubHttpBroadcastPayload } from './activitypub-http-broadcast'
import { VideoModel } from '../../../models/video/video'
import { addVideoShares, createRates } from '../../activitypub/videos'
import { addVideoComments } from '../../activitypub/video-comments'
import { crawlCollectionPage } from '../../activitypub/crawl'
import { Activity } from '../../../../shared/models/activitypub'
type FetchType = 'activity' | 'video-likes' | 'video-dislikes' | 'video-shares' | 'video-comments'
export type ActivitypubHttpFetcherPayload = {
uris: string[]
uri: string
type: FetchType
videoId?: number
}
async function processActivityPubHttpFetcher (job: Bull.Job) {
logger.info('Processing ActivityPub fetcher in job %d.', job.id)
const payload = job.data as ActivitypubHttpBroadcastPayload
const payload = job.data as ActivitypubHttpFetcherPayload
for (const uri of payload.uris) {
await crawlCollectionPage<Activity>(uri, (items) => processActivities(items))
let video: VideoModel
if (payload.videoId) video = await VideoModel.loadAndPopulateAccountAndServerAndTags(payload.videoId)
const fetcherType: { [ id in FetchType ]: (items: any[]) => Promise<any> } = {
'activity': items => processActivities(items),
'video-likes': items => createRates(items, video, 'like'),
'video-dislikes': items => createRates(items, video, 'dislike'),
'video-shares': items => addVideoShares(items, video),
'video-comments': items => addVideoComments(items, video)
}
return crawlCollectionPage(payload.uri, fetcherType[payload.type])
}
// ---------------------------------------------------------------------------

View File

@ -20,7 +20,7 @@ describe('Test videos API validator', function () {
let userAccessToken = ''
let accountName: string
let channelId: number
let channelUUID: string
let channelName: string
let videoId
// ---------------------------------------------------------------
@ -42,7 +42,7 @@ describe('Test videos API validator', function () {
{
const res = await getMyUserInformation(server.url, server.accessToken)
channelId = res.body.videoChannels[ 0 ].id
channelUUID = res.body.videoChannels[ 0 ].uuid
channelName = res.body.videoChannels[ 0 ].name
accountName = res.body.account.name + '@' + res.body.account.host
}
})
@ -140,7 +140,7 @@ describe('Test videos API validator', function () {
let path: string
before(async function () {
path = '/api/v1/video-channels/' + channelUUID + '/videos'
path = '/api/v1/video-channels/' + channelName + '/videos'
})
it('Should fail with a bad start pagination', async function () {

View File

@ -311,7 +311,8 @@ describe('Test follows', function () {
likes: 1,
dislikes: 1,
channel: {
name: 'Main root channel',
displayName: 'Main root channel',
name: 'root_channel',
description: '',
isLocal
},

View File

@ -71,7 +71,8 @@ describe('Test handle downs', function () {
privacy: VideoPrivacy.PUBLIC,
commentsEnabled: true,
channel: {
name: 'Main root channel',
name: 'root_channel',
displayName: 'Main root channel',
description: '',
isLocal: false
},

View File

@ -2,7 +2,7 @@
import * as chai from 'chai'
import 'mocha'
import { createUser, doubleFollow, flushAndRunMultipleServers, follow, getVideosList, unfollow, userLogin } from '../../utils'
import { createUser, doubleFollow, flushAndRunMultipleServers, follow, getVideosList, unfollow, updateVideo, userLogin } from '../../utils'
import { killallServers, ServerInfo, uploadVideo } from '../../utils/index'
import { setAccessTokensToServers } from '../../utils/users/login'
import { Video, VideoChannel } from '../../../../shared/models/videos'
@ -20,6 +20,7 @@ const expect = chai.expect
describe('Test users subscriptions', function () {
let servers: ServerInfo[] = []
const users: { accessToken: string }[] = []
let video3UUID: string
before(async function () {
this.timeout(120000)
@ -65,7 +66,8 @@ describe('Test users subscriptions', function () {
await waitJobs(servers)
await uploadVideo(servers[2].url, users[2].accessToken, { name: 'video server 3 added after follow' })
const res = await uploadVideo(servers[2].url, users[2].accessToken, { name: 'video server 3 added after follow' })
video3UUID = res.body.video.uuid
await waitJobs(servers)
})
@ -247,7 +249,21 @@ describe('Test users subscriptions', function () {
}
})
it('Should update a video of server 3 and see the updated video on server 1', async function () {
this.timeout(30000)
await updateVideo(servers[2].url, users[2].accessToken, video3UUID, { name: 'video server 3 added after follow updated' })
await waitJobs(servers)
const res = await listUserSubscriptionVideos(servers[0].url, users[0].accessToken, 'createdAt')
const videos: Video[] = res.body.data
expect(videos[2].name).to.equal('video server 3 added after follow updated')
})
it('Should remove user of server 3 subscription', async function () {
this.timeout(30000)
await removeUserSubscription(servers[0].url, users[0].accessToken, 'user3_channel@localhost:9003')
await waitJobs(servers)
@ -267,6 +283,8 @@ describe('Test users subscriptions', function () {
})
it('Should remove the root subscription and not display the videos anymore', async function () {
this.timeout(30000)
await removeUserSubscription(servers[0].url, users[0].accessToken, 'root_channel@localhost:9001')
await waitJobs(servers)
@ -288,7 +306,7 @@ describe('Test users subscriptions', function () {
for (const video of res.body.data) {
expect(video.name).to.not.contain('1-3')
expect(video.name).to.not.contain('2-3')
expect(video.name).to.not.contain('video server 3 added after follow')
expect(video.name).to.not.contain('video server 3 added after follow updated')
}
})
@ -309,7 +327,7 @@ describe('Test users subscriptions', function () {
expect(videos[0].name).to.equal('video 1-3')
expect(videos[1].name).to.equal('video 2-3')
expect(videos[2].name).to.equal('video server 3 added after follow')
expect(videos[2].name).to.equal('video server 3 added after follow updated')
}
{
@ -319,7 +337,7 @@ describe('Test users subscriptions', function () {
for (const video of res.body.data) {
expect(video.name).to.not.contain('1-3')
expect(video.name).to.not.contain('2-3')
expect(video.name).to.not.contain('video server 3 added after follow')
expect(video.name).to.not.contain('video server 3 added after follow updated')
}
}
})

View File

@ -128,7 +128,8 @@ describe('Test multiple servers', function () {
privacy: VideoPrivacy.PUBLIC,
commentsEnabled: true,
channel: {
name: 'my channel',
displayName: 'my channel',
name: 'super_channel_name',
description: 'super channel',
isLocal
},
@ -201,7 +202,8 @@ describe('Test multiple servers', function () {
tags: [ 'tag1p2', 'tag2p2', 'tag3p2' ],
privacy: VideoPrivacy.PUBLIC,
channel: {
name: 'Main user1 channel',
displayName: 'Main user1 channel',
name: 'user1_channel',
description: 'super channel',
isLocal
},
@ -307,7 +309,8 @@ describe('Test multiple servers', function () {
tags: [ 'tag1p3' ],
privacy: VideoPrivacy.PUBLIC,
channel: {
name: 'Main root channel',
displayName: 'Main root channel',
name: 'root_channel',
description: '',
isLocal
},
@ -339,7 +342,8 @@ describe('Test multiple servers', function () {
tags: [ 'tag2p3', 'tag3p3', 'tag4p3' ],
privacy: VideoPrivacy.PUBLIC,
channel: {
name: 'Main root channel',
displayName: 'Main root channel',
name: 'root_channel',
description: '',
isLocal
},
@ -647,7 +651,8 @@ describe('Test multiple servers', function () {
tags: [ 'tag_up_1', 'tag_up_2' ],
privacy: VideoPrivacy.PUBLIC,
channel: {
name: 'Main root channel',
displayName: 'Main root channel',
name: 'root_channel',
description: '',
isLocal
},
@ -967,7 +972,8 @@ describe('Test multiple servers', function () {
tags: [ ],
privacy: VideoPrivacy.PUBLIC,
channel: {
name: 'Main root channel',
displayName: 'Main root channel',
name: 'root_channel',
description: '',
isLocal
},

View File

@ -56,7 +56,8 @@ describe('Test a single server', function () {
privacy: VideoPrivacy.PUBLIC,
commentsEnabled: true,
channel: {
name: 'Main root channel',
displayName: 'Main root channel',
name: 'root_channel',
description: '',
isLocal: true
},
@ -87,7 +88,8 @@ describe('Test a single server', function () {
duration: 5,
commentsEnabled: false,
channel: {
name: 'Main root channel',
name: 'root_channel',
displayName: 'Main root channel',
description: '',
isLocal: true
},

View File

@ -438,18 +438,19 @@ async function completeVideoCheck (
name: string
host: string
}
isLocal: boolean,
tags: string[],
privacy: number,
likes?: number,
dislikes?: number,
duration: number,
isLocal: boolean
tags: string[]
privacy: number
likes?: number
dislikes?: number
duration: number
channel: {
name: string,
displayName: string
name: string
description
isLocal: boolean
}
fixture: string,
fixture: string
files: {
resolution: number
size: number
@ -476,8 +477,8 @@ async function completeVideoCheck (
expect(video.account.uuid).to.be.a('string')
expect(video.account.host).to.equal(attributes.account.host)
expect(video.account.name).to.equal(attributes.account.name)
expect(video.channel.displayName).to.equal(attributes.channel.name)
expect(video.channel.name).to.have.lengthOf(36)
expect(video.channel.displayName).to.equal(attributes.channel.displayName)
expect(video.channel.name).to.equal(attributes.channel.name)
expect(video.likes).to.equal(attributes.likes)
expect(video.dislikes).to.equal(attributes.dislikes)
expect(video.isLocal).to.equal(attributes.isLocal)
@ -497,8 +498,8 @@ async function completeVideoCheck (
expect(videoDetails.tags).to.deep.equal(attributes.tags)
expect(videoDetails.account.name).to.equal(attributes.account.name)
expect(videoDetails.account.host).to.equal(attributes.account.host)
expect(videoDetails.channel.displayName).to.equal(attributes.channel.name)
expect(videoDetails.channel.name).to.have.lengthOf(36)
expect(video.channel.displayName).to.equal(attributes.channel.displayName)
expect(video.channel.name).to.equal(attributes.channel.name)
expect(videoDetails.channel.host).to.equal(attributes.account.host)
expect(videoDetails.channel.isLocal).to.equal(attributes.channel.isLocal)
expect(dateIsValid(videoDetails.channel.createdAt.toString())).to.be.true