- name: Run Test
# external-plugins tests only run on schedule
if: github.event_name == 'schedule' || matrix.test_suite != 'external-plugins'
+ env:
+ AKISMET_KEY: ${{ secrets.AKISMET_KEY }}
run: npm run ci -- ${{ matrix.test_suite }}
- name: Display errors
this.search()
},
- error: err => this.notifier.error(err.text)
+ error: err => this.notifier.error(err.message)
})
this.userService.getAnonymousOrLoggedUser()
error: err => {
this.addingComment = false
- this.notifier.error(err.text)
+ this.notifier.error(err.message)
}
})
}
let errorMessage = err.message
if (err.status === HttpStatusCode.FORBIDDEN_403) {
- errorMessage = $localize`Cannot retrieve OAuth Client credentials: ${err.text}.
+ errorMessage = $localize`Cannot retrieve OAuth Client credentials: ${err.message}.
Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.`
}
robots: |
User-agent: *
Disallow:
- # Security.txt rules. To discourage researchers from testing your instance and disable security.txt integration, set this to an empty string
+ # /.well-known/security.txt rules. This endpoint is cached, so you may have to wait a few hours before viewing your changes
+ # To discourage researchers from testing your instance and disable security.txt integration, set this to an empty string
securitytxt:
'# If you would like to report a security issue\n# you may report it to:\nContact: https://github.com/Chocobozzz/PeerTube/blob/develop/SECURITY.md\nContact: mailto:'
robots: |
User-agent: *
Disallow:
- # Security.txt rules. To discourage researchers from testing your instance and disable security.txt integration, set this to an empty string
+ # /.well-known/security.txt rules. This endpoint is cached, so you may have to wait a few hours before viewing your changes
+ # To discourage researchers from testing your instance and disable security.txt integration, set this to an empty string
securitytxt:
'# If you would like to report a security issue\n# you may report it to:\nContact: https://github.com/Chocobozzz/PeerTube/blob/develop/SECURITY.md\nContact: mailto:'
let video: MVideoAccountLightBlacklistAllFiles
let created: boolean
let comment: MCommentOwnerVideo
+
try {
const resolveThreadResult = await resolveThread({ url: commentObject.id, isVideo: false })
+ if (!resolveThreadResult) return // Comment not accepted
video = resolveThreadResult.video
created = resolveThreadResult.commentCreated
import { doJSONRequest } from '../../helpers/requests'
import { ACTIVITY_PUB, CRAWL_REQUEST_CONCURRENCY } from '../../initializers/constants'
import { VideoCommentModel } from '../../models/video/video-comment'
-import { MCommentOwner, MCommentOwnerVideo, MVideoAccountLightBlacklistAllFiles } from '../../types/models/video'
+import { MComment, MCommentOwner, MCommentOwnerVideo, MVideoAccountLightBlacklistAllFiles } from '../../types/models/video'
+import { isRemoteVideoCommentAccepted } from '../moderation'
+import { Hooks } from '../plugins/hooks'
import { getOrCreateAPActor } from './actors'
import { checkUrlsSameHost } from './url'
import { getOrCreateAPVideo } from './videos'
firstReply.changed('updatedAt', true)
firstReply.Video = video
+ if (await isRemoteCommentAccepted(firstReply) !== true) {
+ return undefined
+ }
+
comments[comments.length - 1] = await firstReply.save()
for (let i = comments.length - 2; i >= 0; i--) {
comment.changed('updatedAt', true)
comment.Video = video
+ if (await isRemoteCommentAccepted(comment) !== true) {
+ return undefined
+ }
+
comments[i] = await comment.save()
}
commentCreated: true
})
}
+
+async function isRemoteCommentAccepted (comment: MComment) {
+ // Already created
+ if (comment.id) return true
+
+ const acceptParameters = {
+ comment
+ }
+
+ const acceptedResult = await Hooks.wrapFun(
+ isRemoteVideoCommentAccepted,
+ acceptParameters,
+ 'filter:activity-pub.remote-video-comment.create.accept.result'
+ )
+
+ if (!acceptedResult || acceptedResult.accepted !== true) {
+ logger.info('Refused to create a remote comment.', { acceptedResult, acceptParameters })
+
+ return false
+ }
+
+ return true
+}
import { VideoChannelModel } from '@server/models/video/video-channel'
import { VideoChannelSyncModel } from '@server/models/video/video-channel-sync'
import { MChannelSync } from '@server/types/models'
-import { VideoChannelImportPayload, VideoChannelSyncState } from '@shared/models'
+import { VideoChannelImportPayload } from '@shared/models'
export async function processVideoChannelImport (job: Job) {
const payload = job.data as VideoChannelImportPayload
const videoChannel = await VideoChannelModel.loadAndPopulateAccount(payload.videoChannelId)
- try {
- logger.info(`Starting importing videos from external channel "${payload.externalChannelUrl}" to "${videoChannel.name}" `)
-
- await synchronizeChannel({
- channel: videoChannel,
- externalChannelUrl: payload.externalChannelUrl,
- channelSync
- })
- } catch (err) {
- logger.error(`Failed to import channel ${videoChannel.name}`, { err })
- channelSync.state = VideoChannelSyncState.FAILED
- await channelSync.save()
- }
+ logger.info(`Starting importing videos from external channel "${payload.externalChannelUrl}" to "${videoChannel.name}" `)
+
+ await synchronizeChannel({
+ channel: videoChannel,
+ externalChannelUrl: payload.externalChannelUrl,
+ channelSync
+ })
}
-import { VideoUploadFile } from 'express'
+import express, { VideoUploadFile } from 'express'
import { PathLike } from 'fs-extra'
import { Transaction } from 'sequelize/types'
import { AbuseAuditView, auditLoggerFactory } from '@server/helpers/audit-logger'
MAbuseFull,
MAccountDefault,
MAccountLight,
+ MComment,
MCommentAbuseAccountVideo,
MCommentOwnerVideo,
MUser,
MVideoAbuseVideoFull,
MVideoAccountLightBlacklistAllFiles
} from '@server/types/models'
-import { ActivityCreate } from '../../shared/models/activitypub'
-import { VideoObject } from '../../shared/models/activitypub/objects'
-import { VideoCommentObject } from '../../shared/models/activitypub/objects/video-comment-object'
import { LiveVideoCreate, VideoCreate, VideoImportCreate } from '../../shared/models/videos'
import { VideoCommentCreate } from '../../shared/models/videos/comment'
-import { ActorModel } from '../models/actor/actor'
import { UserModel } from '../models/user/user'
import { VideoModel } from '../models/video/video'
import { VideoCommentModel } from '../models/video/video-comment'
errorMessage?: string
}
-// Can be filtered by plugins
+// ---------------------------------------------------------------------------
+
+// Stub function that can be filtered by plugins
function isLocalVideoAccepted (object: {
videoBody: VideoCreate
videoFile: VideoUploadFile
return { accepted: true }
}
+// ---------------------------------------------------------------------------
+
+// Stub function that can be filtered by plugins
function isLocalLiveVideoAccepted (object: {
liveVideoBody: LiveVideoCreate
user: UserModel
return { accepted: true }
}
+// ---------------------------------------------------------------------------
+
+// Stub function that can be filtered by plugins
function isLocalVideoThreadAccepted (_object: {
+ req: express.Request
commentBody: VideoCommentCreate
video: VideoModel
user: UserModel
return { accepted: true }
}
+// Stub function that can be filtered by plugins
function isLocalVideoCommentReplyAccepted (_object: {
+ req: express.Request
commentBody: VideoCommentCreate
parentComment: VideoCommentModel
video: VideoModel
return { accepted: true }
}
-function isRemoteVideoAccepted (_object: {
- activity: ActivityCreate
- videoAP: VideoObject
- byActor: ActorModel
-}): AcceptResult {
- return { accepted: true }
-}
+// ---------------------------------------------------------------------------
+// Stub function that can be filtered by plugins
function isRemoteVideoCommentAccepted (_object: {
- activity: ActivityCreate
- commentAP: VideoCommentObject
- byActor: ActorModel
+ comment: MComment
}): AcceptResult {
return { accepted: true }
}
+// ---------------------------------------------------------------------------
+
+// Stub function that can be filtered by plugins
function isPreImportVideoAccepted (object: {
videoImportBody: VideoImportCreate
user: MUser
return { accepted: true }
}
+// Stub function that can be filtered by plugins
function isPostImportVideoAccepted (object: {
videoFilePath: PathLike
videoFile: VideoFileModel
return { accepted: true }
}
+// ---------------------------------------------------------------------------
+
async function createVideoAbuse (options: {
baseAbuse: FilteredModelAttributes<AbuseModel>
videoInstance: MVideoAccountLightBlacklistAllFiles
})
}
+// ---------------------------------------------------------------------------
+
export {
isLocalLiveVideoAccepted,
isLocalVideoAccepted,
isLocalVideoThreadAccepted,
- isRemoteVideoAccepted,
isRemoteVideoCommentAccepted,
isLocalVideoCommentReplyAccepted,
isPreImportVideoAccepted,
import { CONFIG } from '@server/initializers/config'
import { VideoChannelModel } from '@server/models/video/video-channel'
import { VideoChannelSyncModel } from '@server/models/video/video-channel-sync'
-import { VideoChannelSyncState } from '@shared/models'
import { SCHEDULER_INTERVALS_MS } from '../../initializers/constants'
import { synchronizeChannel } from '../sync-channel'
import { AbstractScheduler } from './abstract-scheduler'
for (const sync of channelSyncs) {
const channel = await VideoChannelModel.loadAndPopulateAccount(sync.videoChannelId)
- try {
- logger.info(
- 'Creating video import jobs for "%s" sync with external channel "%s"',
- channel.Actor.preferredUsername, sync.externalChannelUrl
- )
-
- const onlyAfter = sync.lastSyncAt || sync.createdAt
-
- await synchronizeChannel({
- channel,
- externalChannelUrl: sync.externalChannelUrl,
- videosCountLimit: CONFIG.IMPORT.VIDEO_CHANNEL_SYNCHRONIZATION.VIDEOS_LIMIT_PER_SYNCHRONIZATION,
- channelSync: sync,
- onlyAfter
- })
- } catch (err) {
- logger.error(`Failed to synchronize channel ${channel.Actor.preferredUsername}`, { err })
- sync.state = VideoChannelSyncState.FAILED
- await sync.save()
- }
+ logger.info(
+ 'Creating video import jobs for "%s" sync with external channel "%s"',
+ channel.Actor.preferredUsername, sync.externalChannelUrl
+ )
+
+ const onlyAfter = sync.lastSyncAt || sync.createdAt
+
+ await synchronizeChannel({
+ channel,
+ externalChannelUrl: sync.externalChannelUrl,
+ videosCountLimit: CONFIG.IMPORT.VIDEO_CHANNEL_SYNCHRONIZATION.VIDEOS_LIMIT_PER_SYNCHRONIZATION,
+ channelSync: sync,
+ onlyAfter
+ })
}
}
await channelSync.save()
}
- const user = await UserModel.loadByChannelActorId(channel.actorId)
- const youtubeDL = new YoutubeDLWrapper(
- externalChannelUrl,
- ServerConfigManager.Instance.getEnabledResolutions('vod'),
- CONFIG.TRANSCODING.ALWAYS_TRANSCODE_ORIGINAL_RESOLUTION
- )
-
- const targetUrls = await youtubeDL.getInfoForListImport({ latestVideosCount: videosCountLimit })
-
- logger.info(
- 'Fetched %d candidate URLs for sync channel %s.',
- targetUrls.length, channel.Actor.preferredUsername, { targetUrls }
- )
-
- if (targetUrls.length === 0) {
- if (channelSync) {
- channelSync.state = VideoChannelSyncState.SYNCED
- await channelSync.save()
- }
-
- return
- }
+ try {
+ const user = await UserModel.loadByChannelActorId(channel.actorId)
+ const youtubeDL = new YoutubeDLWrapper(
+ externalChannelUrl,
+ ServerConfigManager.Instance.getEnabledResolutions('vod'),
+ CONFIG.TRANSCODING.ALWAYS_TRANSCODE_ORIGINAL_RESOLUTION
+ )
- const children: CreateJobArgument[] = []
+ const targetUrls = await youtubeDL.getInfoForListImport({ latestVideosCount: videosCountLimit })
- for (const targetUrl of targetUrls) {
- if (await skipImport(channel, targetUrl, onlyAfter)) continue
+ logger.info(
+ 'Fetched %d candidate URLs for sync channel %s.',
+ targetUrls.length, channel.Actor.preferredUsername, { targetUrls }
+ )
- const { job } = await buildYoutubeDLImport({
- user,
- channel,
- targetUrl,
- channelSync,
- importDataOverride: {
- privacy: VideoPrivacy.PUBLIC
+ if (targetUrls.length === 0) {
+ if (channelSync) {
+ channelSync.state = VideoChannelSyncState.SYNCED
+ await channelSync.save()
}
- })
- children.push(job)
- }
+ return
+ }
+
+ const children: CreateJobArgument[] = []
+
+ for (const targetUrl of targetUrls) {
+ if (await skipImport(channel, targetUrl, onlyAfter)) continue
- // Will update the channel sync status
- const parent: CreateJobArgument = {
- type: 'after-video-channel-import',
- payload: {
- channelSyncId: channelSync?.id
+ const { job } = await buildYoutubeDLImport({
+ user,
+ channel,
+ targetUrl,
+ channelSync,
+ importDataOverride: {
+ privacy: VideoPrivacy.PUBLIC
+ }
+ })
+
+ children.push(job)
}
- }
- await JobQueue.Instance.createJobWithChildren(parent, children)
+ // Will update the channel sync status
+ const parent: CreateJobArgument = {
+ type: 'after-video-channel-import',
+ payload: {
+ channelSyncId: channelSync?.id
+ }
+ }
+
+ await JobQueue.Instance.createJobWithChildren(parent, children)
+ } catch (err) {
+ logger.error(`Failed to import channel ${channel.name}`, { err })
+ channelSync.state = VideoChannelSyncState.FAILED
+ await channelSync.save()
+ }
}
// ---------------------------------------------------------------------------
const acceptParameters = {
video,
commentBody: req.body,
- user: res.locals.oauth.token.User
+ user: res.locals.oauth.token.User,
+ req
}
let acceptedResult: AcceptResult
res.fail({
status: HttpStatusCode.FORBIDDEN_403,
- message: acceptedResult?.errorMessage || 'Refused local comment'
+ message: acceptedResult?.errorMessage || 'Comment has been rejected.'
})
return false
}
})
it('Should send a notification only to admin when there is a new instance follower', async function () {
- this.timeout(20000)
+ this.timeout(60000)
await servers[2].follows.follow({ hosts: [ servers[0].url ] })
})
it('Should upload the video on server 2 and propagate on each server', async function () {
- this.timeout(100000)
+ this.timeout(240000)
const user = {
username: 'user1',
let validId2: string
before(async function () {
- this.timeout(120_000)
+ this.timeout(360_000)
{
const { uuid } = await servers[0].videos.quickUpload({ name: 'video 1' })
let commands: PlaylistsCommand[]
before(async function () {
- this.timeout(120000)
+ this.timeout(240000)
servers = await createMultipleServers(3)
describe('Private and internal videos', function () {
it('Should upload a private and internal videos on server 1', async function () {
- this.timeout(10000)
+ this.timeout(50000)
for (const privacy of [ VideoPrivacy.PRIVATE, VideoPrivacy.INTERNAL ]) {
const attributes = { privacy }
--- /dev/null
+/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
+
+import { expect } from 'chai'
+import { HttpStatusCode } from '@shared/models'
+import {
+ cleanupTests,
+ createMultipleServers,
+ doubleFollow,
+ PeerTubeServer,
+ setAccessTokensToServers,
+ waitJobs
+} from '@shared/server-commands'
+
+describe('Official plugin Akismet', function () {
+ let servers: PeerTubeServer[]
+ let videoUUID: string
+
+ before(async function () {
+ this.timeout(30000)
+
+ servers = await createMultipleServers(2)
+ await setAccessTokensToServers(servers)
+
+ await servers[0].plugins.install({
+ npmName: 'peertube-plugin-akismet'
+ })
+
+ if (!process.env.AKISMET_KEY) throw new Error('Missing AKISMET_KEY from env')
+
+ await servers[0].plugins.updateSettings({
+ npmName: 'peertube-plugin-akismet',
+ settings: {
+ 'akismet-api-key': process.env.AKISMET_KEY
+ }
+ })
+
+ await doubleFollow(servers[0], servers[1])
+ })
+
+ describe('Local threads/replies', function () {
+
+ before(async function () {
+ const { uuid } = await servers[0].videos.quickUpload({ name: 'video 1' })
+ videoUUID = uuid
+ })
+
+ it('Should not detect a thread as spam', async function () {
+ await servers[0].comments.createThread({ videoId: videoUUID, text: 'comment' })
+ })
+
+ it('Should not detect a reply as spam', async function () {
+ await servers[0].comments.addReplyToLastThread({ text: 'reply' })
+ })
+
+ it('Should detect a thread as spam', async function () {
+ await servers[0].comments.createThread({
+ videoId: videoUUID,
+ text: 'akismet-guaranteed-spam',
+ expectedStatus: HttpStatusCode.FORBIDDEN_403
+ })
+ })
+
+ it('Should detect a thread as spam', async function () {
+ await servers[0].comments.createThread({ videoId: videoUUID, text: 'comment' })
+ await servers[0].comments.addReplyToLastThread({ text: 'akismet-guaranteed-spam', expectedStatus: HttpStatusCode.FORBIDDEN_403 })
+ })
+ })
+
+ describe('Remote threads/replies', function () {
+
+ before(async function () {
+ this.timeout(60000)
+
+ const { uuid } = await servers[0].videos.quickUpload({ name: 'video 1' })
+ videoUUID = uuid
+
+ await waitJobs(servers)
+ })
+
+ it('Should not detect a thread as spam', async function () {
+ this.timeout(30000)
+
+ await servers[1].comments.createThread({ videoId: videoUUID, text: 'remote comment 1' })
+ await waitJobs(servers)
+
+ const { data } = await servers[0].comments.listThreads({ videoId: videoUUID })
+ expect(data).to.have.lengthOf(1)
+ })
+
+ it('Should not detect a reply as spam', async function () {
+ this.timeout(30000)
+
+ await servers[1].comments.addReplyToLastThread({ text: 'I agree with you' })
+ await waitJobs(servers)
+
+ const { data } = await servers[0].comments.listThreads({ videoId: videoUUID })
+ expect(data).to.have.lengthOf(1)
+
+ const tree = await servers[0].comments.getThread({ videoId: videoUUID, threadId: data[0].id })
+ expect(tree.children).to.have.lengthOf(1)
+ })
+
+ it('Should detect a thread as spam', async function () {
+ this.timeout(30000)
+
+ await servers[1].comments.createThread({ videoId: videoUUID, text: 'akismet-guaranteed-spam' })
+ await waitJobs(servers)
+
+ const { data } = await servers[0].comments.listThreads({ videoId: videoUUID })
+ expect(data).to.have.lengthOf(1)
+ })
+
+ it('Should detect a thread as spam', async function () {
+ this.timeout(30000)
+
+ await servers[1].comments.addReplyToLastThread({ text: 'akismet-guaranteed-spam' })
+ await waitJobs(servers)
+
+ const { data } = await servers[0].comments.listThreads({ videoId: videoUUID })
+ expect(data).to.have.lengthOf(1)
+
+ const thread = data[0]
+ const tree = await servers[0].comments.getThread({ videoId: videoUUID, threadId: thread.id })
+ expect(tree.children).to.have.lengthOf(1)
+ })
+ })
+
+ describe('Signup', function () {
+
+ before(async function () {
+ await servers[0].config.updateExistingSubConfig({
+ newConfig: {
+ signup: {
+ enabled: true
+ }
+ }
+ })
+ })
+
+ it('Should allow signup', async function () {
+ await servers[0].users.register({
+ username: 'user1',
+ displayName: 'user 1'
+ })
+ })
+
+ it('Should detect a signup as SPAM', async function () {
+ await servers[0].users.register({
+ username: 'user2',
+ displayName: 'user 2',
+ email: 'akismet-guaranteed-spam@example.com',
+ expectedStatus: HttpStatusCode.FORBIDDEN_403
+ })
+ })
+ })
+
+ after(async function () {
+ await cleanupTests(servers)
+ })
+})
+import './akismet'
import './auth-ldap'
import './auto-block-videos'
import './auto-mute'
}
})
+ // ---------------------------------------------------------------------------
+
registerHook({
target: 'filter:api.video-thread.create.accept.result',
handler: ({ accepted }, { commentBody }) => checkCommentBadWord(accepted, commentBody)
handler: ({ accepted }, { commentBody }) => checkCommentBadWord(accepted, commentBody)
})
+ registerHook({
+ target: 'filter:activity-pub.remote-video-comment.create.accept.result',
+ handler: ({ accepted }, { comment }) => checkCommentBadWord(accepted, comment)
+ })
+
+ // ---------------------------------------------------------------------------
+
registerHook({
target: 'filter:api.video-threads.list.params',
handler: obj => addToCount(obj)
})
})
- it('Should run filter:api.videos.list.params', async function () {
- const { data } = await servers[0].videos.list({ start: 0, count: 2 })
+ describe('Videos', function () {
- // 2 plugins do +1 to the count parameter
- expect(data).to.have.lengthOf(4)
- })
+ it('Should run filter:api.videos.list.params', async function () {
+ const { data } = await servers[0].videos.list({ start: 0, count: 2 })
- it('Should run filter:api.videos.list.result', async function () {
- const { total } = await servers[0].videos.list({ start: 0, count: 0 })
+ // 2 plugins do +1 to the count parameter
+ expect(data).to.have.lengthOf(4)
+ })
- // Plugin do +1 to the total result
- expect(total).to.equal(11)
- })
+ it('Should run filter:api.videos.list.result', async function () {
+ const { total } = await servers[0].videos.list({ start: 0, count: 0 })
- it('Should run filter:api.video-playlist.videos.list.params', async function () {
- const { data } = await servers[0].playlists.listVideos({
- count: 2,
- playlistId: videoPlaylistUUID
+ // Plugin do +1 to the total result
+ expect(total).to.equal(11)
})
- // 1 plugin do +1 to the count parameter
- expect(data).to.have.lengthOf(3)
- })
+ it('Should run filter:api.video-playlist.videos.list.params', async function () {
+ const { data } = await servers[0].playlists.listVideos({
+ count: 2,
+ playlistId: videoPlaylistUUID
+ })
- it('Should run filter:api.video-playlist.videos.list.result', async function () {
- const { total } = await servers[0].playlists.listVideos({
- count: 0,
- playlistId: videoPlaylistUUID
+ // 1 plugin do +1 to the count parameter
+ expect(data).to.have.lengthOf(3)
})
- // Plugin do +1 to the total result
- expect(total).to.equal(11)
- })
+ it('Should run filter:api.video-playlist.videos.list.result', async function () {
+ const { total } = await servers[0].playlists.listVideos({
+ count: 0,
+ playlistId: videoPlaylistUUID
+ })
- it('Should run filter:api.accounts.videos.list.params', async function () {
- const { data } = await servers[0].videos.listByAccount({ handle: 'root', start: 0, count: 2 })
+ // Plugin do +1 to the total result
+ expect(total).to.equal(11)
+ })
- // 1 plugin do +1 to the count parameter
- expect(data).to.have.lengthOf(3)
- })
+ it('Should run filter:api.accounts.videos.list.params', async function () {
+ const { data } = await servers[0].videos.listByAccount({ handle: 'root', start: 0, count: 2 })
- it('Should run filter:api.accounts.videos.list.result', async function () {
- const { total } = await servers[0].videos.listByAccount({ handle: 'root', start: 0, count: 2 })
+ // 1 plugin do +1 to the count parameter
+ expect(data).to.have.lengthOf(3)
+ })
- // Plugin do +2 to the total result
- expect(total).to.equal(12)
- })
+ it('Should run filter:api.accounts.videos.list.result', async function () {
+ const { total } = await servers[0].videos.listByAccount({ handle: 'root', start: 0, count: 2 })
- it('Should run filter:api.video-channels.videos.list.params', async function () {
- const { data } = await servers[0].videos.listByChannel({ handle: 'root_channel', start: 0, count: 2 })
+ // Plugin do +2 to the total result
+ expect(total).to.equal(12)
+ })
- // 1 plugin do +3 to the count parameter
- expect(data).to.have.lengthOf(5)
- })
+ it('Should run filter:api.video-channels.videos.list.params', async function () {
+ const { data } = await servers[0].videos.listByChannel({ handle: 'root_channel', start: 0, count: 2 })
- it('Should run filter:api.video-channels.videos.list.result', async function () {
- const { total } = await servers[0].videos.listByChannel({ handle: 'root_channel', start: 0, count: 2 })
+ // 1 plugin do +3 to the count parameter
+ expect(data).to.have.lengthOf(5)
+ })
- // Plugin do +3 to the total result
- expect(total).to.equal(13)
- })
+ it('Should run filter:api.video-channels.videos.list.result', async function () {
+ const { total } = await servers[0].videos.listByChannel({ handle: 'root_channel', start: 0, count: 2 })
- it('Should run filter:api.user.me.videos.list.params', async function () {
- const { data } = await servers[0].videos.listMyVideos({ start: 0, count: 2 })
+ // Plugin do +3 to the total result
+ expect(total).to.equal(13)
+ })
- // 1 plugin do +4 to the count parameter
- expect(data).to.have.lengthOf(6)
- })
+ it('Should run filter:api.user.me.videos.list.params', async function () {
+ const { data } = await servers[0].videos.listMyVideos({ start: 0, count: 2 })
- it('Should run filter:api.user.me.videos.list.result', async function () {
- const { total } = await servers[0].videos.listMyVideos({ start: 0, count: 2 })
+ // 1 plugin do +4 to the count parameter
+ expect(data).to.have.lengthOf(6)
+ })
- // Plugin do +4 to the total result
- expect(total).to.equal(14)
- })
+ it('Should run filter:api.user.me.videos.list.result', async function () {
+ const { total } = await servers[0].videos.listMyVideos({ start: 0, count: 2 })
- it('Should run filter:api.video.get.result', async function () {
- const video = await servers[0].videos.get({ id: videoUUID })
- expect(video.name).to.contain('<3')
- })
+ // Plugin do +4 to the total result
+ expect(total).to.equal(14)
+ })
- it('Should run filter:api.video.upload.accept.result', async function () {
- await servers[0].videos.upload({ attributes: { name: 'video with bad word' }, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
+ it('Should run filter:api.video.get.result', async function () {
+ const video = await servers[0].videos.get({ id: videoUUID })
+ expect(video.name).to.contain('<3')
+ })
})
- it('Should run filter:api.live-video.create.accept.result', async function () {
- const attributes = {
- name: 'video with bad word',
- privacy: VideoPrivacy.PUBLIC,
- channelId: servers[0].store.channel.id
- }
+ describe('Video/live/import accept', function () {
- await servers[0].live.create({ fields: attributes, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
- })
-
- it('Should run filter:api.video.pre-import-url.accept.result', async function () {
- const attributes = {
- name: 'normal title',
- privacy: VideoPrivacy.PUBLIC,
- channelId: servers[0].store.channel.id,
- targetUrl: FIXTURE_URLS.goodVideo + 'bad'
- }
- await servers[0].imports.importVideo({ attributes, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
- })
+ it('Should run filter:api.video.upload.accept.result', async function () {
+ await servers[0].videos.upload({ attributes: { name: 'video with bad word' }, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
+ })
- it('Should run filter:api.video.pre-import-torrent.accept.result', async function () {
- const attributes = {
- name: 'bad torrent',
- privacy: VideoPrivacy.PUBLIC,
- channelId: servers[0].store.channel.id,
- torrentfile: 'video-720p.torrent' as any
- }
- await servers[0].imports.importVideo({ attributes, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
- })
+ it('Should run filter:api.live-video.create.accept.result', async function () {
+ const attributes = {
+ name: 'video with bad word',
+ privacy: VideoPrivacy.PUBLIC,
+ channelId: servers[0].store.channel.id
+ }
- it('Should run filter:api.video.post-import-url.accept.result', async function () {
- this.timeout(60000)
+ await servers[0].live.create({ fields: attributes, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
+ })
- let videoImportId: number
+ it('Should run filter:api.video.pre-import-url.accept.result', async function () {
+ const attributes = {
+ name: 'normal title',
+ privacy: VideoPrivacy.PUBLIC,
+ channelId: servers[0].store.channel.id,
+ targetUrl: FIXTURE_URLS.goodVideo + 'bad'
+ }
+ await servers[0].imports.importVideo({ attributes, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
+ })
- {
+ it('Should run filter:api.video.pre-import-torrent.accept.result', async function () {
const attributes = {
- name: 'title with bad word',
+ name: 'bad torrent',
privacy: VideoPrivacy.PUBLIC,
channelId: servers[0].store.channel.id,
- targetUrl: FIXTURE_URLS.goodVideo
+ torrentfile: 'video-720p.torrent' as any
}
- const body = await servers[0].imports.importVideo({ attributes })
- videoImportId = body.id
- }
+ await servers[0].imports.importVideo({ attributes, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
+ })
- await waitJobs(servers)
+ it('Should run filter:api.video.post-import-url.accept.result', async function () {
+ this.timeout(60000)
- {
- const body = await servers[0].imports.getMyVideoImports()
- const videoImports = body.data
+ let videoImportId: number
- const videoImport = videoImports.find(i => i.id === videoImportId)
+ {
+ const attributes = {
+ name: 'title with bad word',
+ privacy: VideoPrivacy.PUBLIC,
+ channelId: servers[0].store.channel.id,
+ targetUrl: FIXTURE_URLS.goodVideo
+ }
+ const body = await servers[0].imports.importVideo({ attributes })
+ videoImportId = body.id
+ }
- expect(videoImport.state.id).to.equal(VideoImportState.REJECTED)
- expect(videoImport.state.label).to.equal('Rejected')
- }
- })
+ await waitJobs(servers)
- it('Should run filter:api.video.post-import-torrent.accept.result', async function () {
- this.timeout(60000)
+ {
+ const body = await servers[0].imports.getMyVideoImports()
+ const videoImports = body.data
- let videoImportId: number
+ const videoImport = videoImports.find(i => i.id === videoImportId)
- {
- const attributes = {
- name: 'title with bad word',
- privacy: VideoPrivacy.PUBLIC,
- channelId: servers[0].store.channel.id,
- torrentfile: 'video-720p.torrent' as any
+ expect(videoImport.state.id).to.equal(VideoImportState.REJECTED)
+ expect(videoImport.state.label).to.equal('Rejected')
}
- const body = await servers[0].imports.importVideo({ attributes })
- videoImportId = body.id
- }
+ })
- await waitJobs(servers)
+ it('Should run filter:api.video.post-import-torrent.accept.result', async function () {
+ this.timeout(60000)
- {
- const { data: videoImports } = await servers[0].imports.getMyVideoImports()
+ let videoImportId: number
- const videoImport = videoImports.find(i => i.id === videoImportId)
+ {
+ const attributes = {
+ name: 'title with bad word',
+ privacy: VideoPrivacy.PUBLIC,
+ channelId: servers[0].store.channel.id,
+ torrentfile: 'video-720p.torrent' as any
+ }
+ const body = await servers[0].imports.importVideo({ attributes })
+ videoImportId = body.id
+ }
- expect(videoImport.state.id).to.equal(VideoImportState.REJECTED)
- expect(videoImport.state.label).to.equal('Rejected')
- }
- })
+ await waitJobs(servers)
+
+ {
+ const { data: videoImports } = await servers[0].imports.getMyVideoImports()
+
+ const videoImport = videoImports.find(i => i.id === videoImportId)
- it('Should run filter:api.video-thread.create.accept.result', async function () {
- await servers[0].comments.createThread({
- videoId: videoUUID,
- text: 'comment with bad word',
- expectedStatus: HttpStatusCode.FORBIDDEN_403
+ expect(videoImport.state.id).to.equal(VideoImportState.REJECTED)
+ expect(videoImport.state.label).to.equal('Rejected')
+ }
})
})
- it('Should run filter:api.video-comment-reply.create.accept.result', async function () {
- const created = await servers[0].comments.createThread({ videoId: videoUUID, text: 'thread' })
- threadId = created.id
+ describe('Video comments accept', function () {
- await servers[0].comments.addReply({
- videoId: videoUUID,
- toCommentId: threadId,
- text: 'comment with bad word',
- expectedStatus: HttpStatusCode.FORBIDDEN_403
+ it('Should run filter:api.video-thread.create.accept.result', async function () {
+ await servers[0].comments.createThread({
+ videoId: videoUUID,
+ text: 'comment with bad word',
+ expectedStatus: HttpStatusCode.FORBIDDEN_403
+ })
})
- await servers[0].comments.addReply({
- videoId: videoUUID,
- toCommentId: threadId,
- text: 'comment with good word',
- expectedStatus: HttpStatusCode.OK_200
+
+ it('Should run filter:api.video-comment-reply.create.accept.result', async function () {
+ const created = await servers[0].comments.createThread({ videoId: videoUUID, text: 'thread' })
+ threadId = created.id
+
+ await servers[0].comments.addReply({
+ videoId: videoUUID,
+ toCommentId: threadId,
+ text: 'comment with bad word',
+ expectedStatus: HttpStatusCode.FORBIDDEN_403
+ })
+ await servers[0].comments.addReply({
+ videoId: videoUUID,
+ toCommentId: threadId,
+ text: 'comment with good word',
+ expectedStatus: HttpStatusCode.OK_200
+ })
})
- })
- it('Should run filter:api.video-threads.list.params', async function () {
- const { data } = await servers[0].comments.listThreads({ videoId: videoUUID, start: 0, count: 0 })
+ it('Should run filter:activity-pub.remote-video-comment.create.accept.result on a thread creation', async function () {
+ this.timeout(30000)
- // our plugin do +1 to the count parameter
- expect(data).to.have.lengthOf(1)
- })
+ await servers[1].comments.createThread({ videoId: videoUUID, text: 'comment with bad word' })
- it('Should run filter:api.video-threads.list.result', async function () {
- const { total } = await servers[0].comments.listThreads({ videoId: videoUUID, start: 0, count: 0 })
+ await waitJobs(servers)
- // Plugin do +1 to the total result
- expect(total).to.equal(2)
- })
+ {
+ const thread = await servers[0].comments.listThreads({ videoId: videoUUID })
+ expect(thread.data).to.have.lengthOf(1)
+ expect(thread.data[0].text).to.not.include(' bad ')
+ }
+
+ {
+ const thread = await servers[1].comments.listThreads({ videoId: videoUUID })
+ expect(thread.data).to.have.lengthOf(2)
+ }
+ })
- it('Should run filter:api.video-thread-comments.list.params')
+ it('Should run filter:activity-pub.remote-video-comment.create.accept.result on a reply creation', async function () {
+ this.timeout(30000)
- it('Should run filter:api.video-thread-comments.list.result', async function () {
- const thread = await servers[0].comments.getThread({ videoId: videoUUID, threadId })
+ const { data } = await servers[1].comments.listThreads({ videoId: videoUUID })
+ const threadIdServer2 = data.find(t => t.text === 'thread').id
- expect(thread.comment.text.endsWith(' <3')).to.be.true
+ await servers[1].comments.addReply({
+ videoId: videoUUID,
+ toCommentId: threadIdServer2,
+ text: 'comment with bad word'
+ })
+
+ await waitJobs(servers)
+
+ {
+ const tree = await servers[0].comments.getThread({ videoId: videoUUID, threadId })
+ expect(tree.children).to.have.lengthOf(1)
+ expect(tree.children[0].comment.text).to.not.include(' bad ')
+ }
+
+ {
+ const tree = await servers[1].comments.getThread({ videoId: videoUUID, threadId: threadIdServer2 })
+ expect(tree.children).to.have.lengthOf(2)
+ }
+ })
})
- it('Should run filter:api.overviews.videos.list.{params,result}', async function () {
- await servers[0].overviews.getVideos({ page: 1 })
+ describe('Video comments', function () {
+
+ it('Should run filter:api.video-threads.list.params', async function () {
+ const { data } = await servers[0].comments.listThreads({ videoId: videoUUID, start: 0, count: 0 })
+
+ // our plugin do +1 to the count parameter
+ expect(data).to.have.lengthOf(1)
+ })
- // 3 because we get 3 samples per page
- await servers[0].servers.waitUntilLog('Run hook filter:api.overviews.videos.list.params', 3)
- await servers[0].servers.waitUntilLog('Run hook filter:api.overviews.videos.list.result', 3)
+ it('Should run filter:api.video-threads.list.result', async function () {
+ const { total } = await servers[0].comments.listThreads({ videoId: videoUUID, start: 0, count: 0 })
+
+ // Plugin do +1 to the total result
+ expect(total).to.equal(2)
+ })
+
+ it('Should run filter:api.video-thread-comments.list.params')
+
+ it('Should run filter:api.video-thread-comments.list.result', async function () {
+ const thread = await servers[0].comments.getThread({ videoId: videoUUID, threadId })
+
+ expect(thread.comment.text.endsWith(' <3')).to.be.true
+ })
+
+ it('Should run filter:api.overviews.videos.list.{params,result}', async function () {
+ await servers[0].overviews.getVideos({ page: 1 })
+
+ // 3 because we get 3 samples per page
+ await servers[0].servers.waitUntilLog('Run hook filter:api.overviews.videos.list.params', 3)
+ await servers[0].servers.waitUntilLog('Run hook filter:api.overviews.videos.list.result', 3)
+ })
})
describe('filter:video.auto-blacklist.result', function () {
'filter:job-queue.process.result': true,
'filter:transcoding.manual.resolutions-to-transcode.result': true,
- 'filter:transcoding.auto.resolutions-to-transcode.result': true
+ 'filter:transcoding.auto.resolutions-to-transcode.result': true,
+
+ 'filter:activity-pub.remote-video-comment.create.accept.result': true
}
export type ServerFilterHookName = keyof typeof serverFilterHookObject
username: string
password?: string
displayName?: string
+ email?: string
channel?: {
name: string
displayName: string
}
}) {
- const { username, password = 'password', displayName, channel } = options
+ const { username, password = 'password', displayName, channel, email = username + '@example.com' } = options
const path = '/api/v1/users/register'
return this.postBodyRequest({
fields: {
username,
password,
- email: username + '@example.com',
+ email,
displayName,
channel
},
### PeerTube instance
-**Check the changelog (in particular BREAKING CHANGES!):** https://github.com/Chocobozzz/PeerTube/blob/develop/CHANGELOG.md
+**Check the changelog (in particular the *IMPORTANT NOTES* section):** https://github.com/Chocobozzz/PeerTube/blob/develop/CHANGELOG.md
#### Auto