filter-hooks.ts 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937
  1. /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
  2. import {
  3. HttpStatusCode,
  4. MyUser,
  5. PeerTubeProblemDocument,
  6. VideoDetails,
  7. VideoImportState,
  8. VideoPlaylist,
  9. VideoPlaylistPrivacy,
  10. VideoPrivacy
  11. } from '@peertube/peertube-models'
  12. import {
  13. PeerTubeServer,
  14. PluginsCommand,
  15. cleanupTests,
  16. createMultipleServers,
  17. doubleFollow,
  18. makeActivityPubGetRequest,
  19. makeGetRequest,
  20. makeRawRequest,
  21. setAccessTokensToServers,
  22. setDefaultVideoChannel,
  23. waitJobs
  24. } from '@peertube/peertube-server-commands'
  25. import { expectEndWith } from '@tests/shared/checks.js'
  26. import { expect } from 'chai'
  27. import { FIXTURE_URLS } from '../shared/fixture-urls.js'
  28. describe('Test plugin filter hooks', function () {
  29. let servers: PeerTubeServer[]
  30. let videoUUID: string
  31. let threadId: number
  32. let videoPlaylistUUID: string
  33. let importUserToken: string
  34. before(async function () {
  35. this.timeout(120000)
  36. servers = await createMultipleServers(2)
  37. await setAccessTokensToServers(servers)
  38. await setDefaultVideoChannel(servers)
  39. await doubleFollow(servers[0], servers[1])
  40. await servers[0].plugins.install({ path: PluginsCommand.getPluginTestPath() })
  41. await servers[0].plugins.install({ path: PluginsCommand.getPluginTestPath('-filter-translations') })
  42. {
  43. ({ uuid: videoPlaylistUUID } = await servers[0].playlists.create({
  44. attributes: {
  45. displayName: 'my super playlist',
  46. privacy: VideoPlaylistPrivacy.PUBLIC,
  47. description: 'my super description',
  48. videoChannelId: servers[0].store.channel.id
  49. }
  50. }))
  51. }
  52. for (let i = 0; i < 10; i++) {
  53. const video = await servers[0].videos.upload({ attributes: { name: 'default video ' + i } })
  54. await servers[0].playlists.addElement({ playlistId: videoPlaylistUUID, attributes: { videoId: video.id } })
  55. }
  56. const { data } = await servers[0].videos.list()
  57. videoUUID = data[0].uuid
  58. await servers[0].config.updateExistingConfig({
  59. newConfig: {
  60. live: { enabled: true },
  61. signup: { enabled: true },
  62. videoFile: {
  63. update: {
  64. enabled: true
  65. }
  66. },
  67. import: {
  68. videos: {
  69. http: { enabled: true },
  70. torrent: { enabled: true }
  71. }
  72. }
  73. }
  74. })
  75. {
  76. const { userId, token } = await servers[0].users.generate('to_import')
  77. importUserToken = token
  78. await servers[0].users.update({ userId, videoQuota: -1, videoQuotaDaily: -1 })
  79. await servers[0].userImports.importArchive({ userId, token, fixture: 'export-with-files.zip' })
  80. }
  81. // Root subscribes to itself
  82. await servers[0].subscriptions.add({ targetUri: 'root_channel@' + servers[0].host })
  83. await waitJobs(servers)
  84. })
  85. describe('Videos', function () {
  86. it('Should run filter:api.videos.list.params', async function () {
  87. const { data } = await servers[0].videos.list({ start: 0, count: 2 })
  88. // 2 plugins do +1 to the count parameter
  89. expect(data).to.have.lengthOf(4)
  90. })
  91. it('Should run filter:api.videos.list.result', async function () {
  92. const { total } = await servers[0].videos.list({ start: 0, count: 0 })
  93. // Plugin do +1 to the total result
  94. expect(total).to.equal(12)
  95. })
  96. it('Should run filter:api.video-playlist.videos.list.params', async function () {
  97. const { data } = await servers[0].playlists.listVideos({
  98. count: 2,
  99. playlistId: videoPlaylistUUID
  100. })
  101. // 1 plugin do +1 to the count parameter
  102. expect(data).to.have.lengthOf(3)
  103. })
  104. it('Should run filter:api.video-playlist.videos.list.result', async function () {
  105. const { total } = await servers[0].playlists.listVideos({
  106. count: 0,
  107. playlistId: videoPlaylistUUID
  108. })
  109. // Plugin do +1 to the total result
  110. expect(total).to.equal(11)
  111. })
  112. it('Should run filter:api.accounts.videos.list.params', async function () {
  113. const { data } = await servers[0].videos.listByAccount({ handle: 'root', start: 0, count: 2 })
  114. // 1 plugin do +1 to the count parameter
  115. expect(data).to.have.lengthOf(3)
  116. })
  117. it('Should run filter:api.accounts.videos.list.result', async function () {
  118. const { total } = await servers[0].videos.listByAccount({ handle: 'root', start: 0, count: 2 })
  119. // Plugin do +2 to the total result
  120. expect(total).to.equal(12)
  121. })
  122. it('Should run filter:api.video-channels.videos.list.params', async function () {
  123. const { data } = await servers[0].videos.listByChannel({ handle: 'root_channel', start: 0, count: 2 })
  124. // 1 plugin do +3 to the count parameter
  125. expect(data).to.have.lengthOf(5)
  126. })
  127. it('Should run filter:api.video-channels.videos.list.result', async function () {
  128. const { total } = await servers[0].videos.listByChannel({ handle: 'root_channel', start: 0, count: 2 })
  129. // Plugin do +3 to the total result
  130. expect(total).to.equal(13)
  131. })
  132. it('Should run filter:api.user.me.videos.list.params', async function () {
  133. const { data } = await servers[0].videos.listMyVideos({ start: 0, count: 2 })
  134. // 1 plugin do +4 to the count parameter
  135. expect(data).to.have.lengthOf(6)
  136. })
  137. it('Should run filter:api.user.me.videos.list.result', async function () {
  138. const { total } = await servers[0].videos.listMyVideos({ start: 0, count: 2 })
  139. // Plugin do +4 to the total result
  140. expect(total).to.equal(14)
  141. })
  142. it('Should run filter:api.user.me.subscription-videos.list.params', async function () {
  143. const { data } = await servers[0].videos.listMySubscriptionVideos({ start: 0, count: 2 })
  144. // 1 plugin do +1 to the count parameter
  145. expect(data).to.have.lengthOf(3)
  146. })
  147. it('Should run filter:api.user.me.subscription-videos.list.result', async function () {
  148. const { total } = await servers[0].videos.listMySubscriptionVideos({ start: 0, count: 2 })
  149. // Plugin do +4 to the total result
  150. expect(total).to.equal(14)
  151. })
  152. it('Should run filter:api.video.get.result', async function () {
  153. const video = await servers[0].videos.get({ id: videoUUID })
  154. expect(video.name).to.contain('<3')
  155. })
  156. })
  157. describe('Video/live/import accept', function () {
  158. it('Should run filter:api.video.upload.accept.result', async function () {
  159. const options = { attributes: { name: 'video with bad word' }, expectedStatus: HttpStatusCode.FORBIDDEN_403 }
  160. await servers[0].videos.upload({ mode: 'legacy', ...options })
  161. await servers[0].videos.upload({ mode: 'resumable', ...options })
  162. })
  163. it('Should run filter:api.video.update-file.accept.result', async function () {
  164. const res = await servers[0].videos.replaceSourceFile({
  165. videoId: videoUUID,
  166. fixture: 'video_short1.webm',
  167. completedExpectedStatus: HttpStatusCode.FORBIDDEN_403
  168. })
  169. expect((res as any)?.error).to.equal('no webm')
  170. })
  171. it('Should run filter:api.live-video.create.accept.result', async function () {
  172. const attributes = {
  173. name: 'video with bad word',
  174. privacy: VideoPrivacy.PUBLIC,
  175. channelId: servers[0].store.channel.id
  176. }
  177. await servers[0].live.create({ fields: attributes, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
  178. })
  179. it('Should run filter:api.video.pre-import-url.accept.result', async function () {
  180. const attributes = {
  181. name: 'normal title',
  182. privacy: VideoPrivacy.PUBLIC,
  183. channelId: servers[0].store.channel.id,
  184. targetUrl: FIXTURE_URLS.goodVideo + 'bad'
  185. }
  186. await servers[0].videoImports.importVideo({ attributes, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
  187. })
  188. it('Should run filter:api.video.pre-import-torrent.accept.result', async function () {
  189. const attributes = {
  190. name: 'bad torrent',
  191. privacy: VideoPrivacy.PUBLIC,
  192. channelId: servers[0].store.channel.id,
  193. torrentfile: 'video-720p.torrent' as any
  194. }
  195. await servers[0].videoImports.importVideo({ attributes, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
  196. })
  197. it('Should run filter:api.video.post-import-url.accept.result', async function () {
  198. this.timeout(60000)
  199. let videoImportId: number
  200. {
  201. const attributes = {
  202. name: 'title with bad word',
  203. privacy: VideoPrivacy.PUBLIC,
  204. channelId: servers[0].store.channel.id,
  205. targetUrl: FIXTURE_URLS.goodVideo
  206. }
  207. const body = await servers[0].videoImports.importVideo({ attributes })
  208. videoImportId = body.id
  209. }
  210. await waitJobs(servers)
  211. {
  212. const body = await servers[0].videoImports.getMyVideoImports()
  213. const videoImports = body.data
  214. const videoImport = videoImports.find(i => i.id === videoImportId)
  215. expect(videoImport.state.id).to.equal(VideoImportState.REJECTED)
  216. expect(videoImport.state.label).to.equal('Rejected')
  217. }
  218. })
  219. it('Should run filter:api.video.post-import-torrent.accept.result', async function () {
  220. this.timeout(60000)
  221. let videoImportId: number
  222. {
  223. const attributes = {
  224. name: 'title with bad word',
  225. privacy: VideoPrivacy.PUBLIC,
  226. channelId: servers[0].store.channel.id,
  227. torrentfile: 'video-720p.torrent' as any
  228. }
  229. const body = await servers[0].videoImports.importVideo({ attributes })
  230. videoImportId = body.id
  231. }
  232. await waitJobs(servers)
  233. {
  234. const { data: videoImports } = await servers[0].videoImports.getMyVideoImports()
  235. const videoImport = videoImports.find(i => i.id === videoImportId)
  236. expect(videoImport.state.id).to.equal(VideoImportState.REJECTED)
  237. expect(videoImport.state.label).to.equal('Rejected')
  238. }
  239. })
  240. it('Should run filter:api.video.user-import.video-attribute.result', async function () {
  241. const { data } = await servers[0].videos.listMyVideos({ token: importUserToken })
  242. expect(data).to.have.lengthOf(1)
  243. // We filter out video 1 in the plugin
  244. expect(data[0].name).to.not.equal('video 1')
  245. })
  246. })
  247. describe('Video comments accept', function () {
  248. it('Should run filter:api.video-thread.create.accept.result', async function () {
  249. await servers[0].comments.createThread({
  250. videoId: videoUUID,
  251. text: 'comment with bad word',
  252. expectedStatus: HttpStatusCode.FORBIDDEN_403
  253. })
  254. })
  255. it('Should run filter:api.video-comment-reply.create.accept.result', async function () {
  256. const created = await servers[0].comments.createThread({ videoId: videoUUID, text: 'thread' })
  257. threadId = created.id
  258. await servers[0].comments.addReply({
  259. videoId: videoUUID,
  260. toCommentId: threadId,
  261. text: 'comment with bad word',
  262. expectedStatus: HttpStatusCode.FORBIDDEN_403
  263. })
  264. await servers[0].comments.addReply({
  265. videoId: videoUUID,
  266. toCommentId: threadId,
  267. text: 'comment with good word',
  268. expectedStatus: HttpStatusCode.OK_200
  269. })
  270. })
  271. it('Should run filter:activity-pub.remote-video-comment.create.accept.result on a thread creation', async function () {
  272. this.timeout(30000)
  273. await servers[1].comments.createThread({ videoId: videoUUID, text: 'comment with bad word' })
  274. await waitJobs(servers)
  275. {
  276. const thread = await servers[0].comments.listThreads({ videoId: videoUUID })
  277. expect(thread.data).to.have.lengthOf(1)
  278. expect(thread.data[0].text).to.not.include(' bad ')
  279. }
  280. {
  281. const thread = await servers[1].comments.listThreads({ videoId: videoUUID })
  282. expect(thread.data).to.have.lengthOf(2)
  283. }
  284. })
  285. it('Should run filter:activity-pub.remote-video-comment.create.accept.result on a reply creation', async function () {
  286. this.timeout(30000)
  287. const { data } = await servers[1].comments.listThreads({ videoId: videoUUID })
  288. const threadIdServer2 = data.find(t => t.text === 'thread').id
  289. await servers[1].comments.addReply({
  290. videoId: videoUUID,
  291. toCommentId: threadIdServer2,
  292. text: 'comment with bad word'
  293. })
  294. await waitJobs(servers)
  295. {
  296. const tree = await servers[0].comments.getThread({ videoId: videoUUID, threadId })
  297. expect(tree.children).to.have.lengthOf(1)
  298. expect(tree.children[0].comment.text).to.not.include(' bad ')
  299. }
  300. {
  301. const tree = await servers[1].comments.getThread({ videoId: videoUUID, threadId: threadIdServer2 })
  302. expect(tree.children).to.have.lengthOf(2)
  303. }
  304. })
  305. })
  306. describe('Video comments', function () {
  307. it('Should run filter:api.video-threads.list.params', async function () {
  308. const { data } = await servers[0].comments.listThreads({ videoId: videoUUID, start: 0, count: 0 })
  309. // our plugin do +1 to the count parameter
  310. expect(data).to.have.lengthOf(1)
  311. })
  312. it('Should run filter:api.video-threads.list.result', async function () {
  313. const { total } = await servers[0].comments.listThreads({ videoId: videoUUID, start: 0, count: 0 })
  314. // Plugin do +1 to the total result
  315. expect(total).to.equal(2)
  316. })
  317. it('Should run filter:api.video-thread-comments.list.params')
  318. it('Should run filter:api.video-thread-comments.list.result', async function () {
  319. const thread = await servers[0].comments.getThread({ videoId: videoUUID, threadId })
  320. expect(thread.comment.text.endsWith(' <3')).to.be.true
  321. })
  322. it('Should run filter:api.overviews.videos.list.{params,result}', async function () {
  323. await servers[0].overviews.getVideos({ page: 1 })
  324. // 3 because we get 3 samples per page
  325. await servers[0].servers.waitUntilLog('Run hook filter:api.overviews.videos.list.params', 3)
  326. await servers[0].servers.waitUntilLog('Run hook filter:api.overviews.videos.list.result', 3)
  327. })
  328. })
  329. describe('filter:video.auto-blacklist.result', function () {
  330. async function checkIsBlacklisted (id: number | string, value: boolean) {
  331. const video = await servers[0].videos.getWithToken({ id })
  332. expect(video.blacklisted).to.equal(value)
  333. }
  334. it('Should blacklist on upload', async function () {
  335. const { uuid } = await servers[0].videos.upload({ attributes: { name: 'video please blacklist me' } })
  336. await checkIsBlacklisted(uuid, true)
  337. })
  338. it('Should blacklist on import', async function () {
  339. this.timeout(15000)
  340. const attributes = {
  341. name: 'video please blacklist me',
  342. targetUrl: FIXTURE_URLS.goodVideo,
  343. channelId: servers[0].store.channel.id
  344. }
  345. const body = await servers[0].videoImports.importVideo({ attributes })
  346. await checkIsBlacklisted(body.video.uuid, true)
  347. })
  348. it('Should blacklist on update', async function () {
  349. const { uuid } = await servers[0].videos.upload({ attributes: { name: 'video' } })
  350. await checkIsBlacklisted(uuid, false)
  351. await servers[0].videos.update({ id: uuid, attributes: { name: 'please blacklist me' } })
  352. await checkIsBlacklisted(uuid, true)
  353. })
  354. it('Should blacklist on remote upload', async function () {
  355. this.timeout(120000)
  356. const { uuid } = await servers[1].videos.upload({ attributes: { name: 'remote please blacklist me' } })
  357. await waitJobs(servers)
  358. await checkIsBlacklisted(uuid, true)
  359. })
  360. it('Should blacklist on remote update', async function () {
  361. this.timeout(120000)
  362. const { uuid } = await servers[1].videos.upload({ attributes: { name: 'video' } })
  363. await waitJobs(servers)
  364. await checkIsBlacklisted(uuid, false)
  365. await servers[1].videos.update({ id: uuid, attributes: { name: 'please blacklist me' } })
  366. await waitJobs(servers)
  367. await checkIsBlacklisted(uuid, true)
  368. })
  369. })
  370. describe('Users', function () {
  371. it('Should run filter:api.user.me.get.result', async function () {
  372. const user = await servers[0].users.getMyInfo() as MyUser & { customParam: string }
  373. expect(user.customParam).to.equal('Customized')
  374. })
  375. })
  376. describe('Should run filter:api.user.signup.allowed.result', function () {
  377. before(async function () {
  378. await servers[0].config.updateExistingConfig({ newConfig: { signup: { requiresApproval: false } } })
  379. })
  380. it('Should run on config endpoint', async function () {
  381. const body = await servers[0].config.getConfig()
  382. expect(body.signup.allowed).to.be.true
  383. })
  384. it('Should allow a signup', async function () {
  385. await servers[0].registrations.register({ username: 'john1' })
  386. })
  387. it('Should not allow a signup', async function () {
  388. const res = await servers[0].registrations.register({
  389. username: 'jma 1',
  390. expectedStatus: HttpStatusCode.FORBIDDEN_403
  391. })
  392. expect(res.body.error).to.equal('No jma 1')
  393. })
  394. })
  395. describe('Should run filter:api.user.request-signup.allowed.result', function () {
  396. before(async function () {
  397. await servers[0].config.updateExistingConfig({ newConfig: { signup: { requiresApproval: true } } })
  398. })
  399. it('Should run on config endpoint', async function () {
  400. const body = await servers[0].config.getConfig()
  401. expect(body.signup.allowed).to.be.true
  402. })
  403. it('Should allow a signup request', async function () {
  404. await servers[0].registrations.requestRegistration({ username: 'john2', registrationReason: 'tt' })
  405. })
  406. it('Should not allow a signup request', async function () {
  407. const body = await servers[0].registrations.requestRegistration({
  408. username: 'jma 2',
  409. registrationReason: 'tt',
  410. expectedStatus: HttpStatusCode.FORBIDDEN_403
  411. })
  412. expect((body as unknown as PeerTubeProblemDocument).error).to.equal('No jma 2')
  413. })
  414. })
  415. describe('Download hooks', function () {
  416. const downloadVideos: VideoDetails[] = []
  417. let downloadVideo2Token: string
  418. before(async function () {
  419. this.timeout(120000)
  420. await servers[0].config.enableMinimumTranscoding({ hls: true, webVideo: true })
  421. const uuids: string[] = []
  422. for (const name of [ 'bad torrent', 'bad file', 'bad playlist file' ]) {
  423. const uuid = (await servers[0].videos.quickUpload({ name })).uuid
  424. uuids.push(uuid)
  425. }
  426. await waitJobs(servers)
  427. for (const uuid of uuids) {
  428. downloadVideos.push(await servers[0].videos.get({ id: uuid }))
  429. }
  430. downloadVideo2Token = await servers[0].videoToken.getVideoFileToken({ videoId: downloadVideos[2].uuid })
  431. })
  432. it('Should run filter:api.download.torrent.allowed.result', async function () {
  433. const res = await makeRawRequest({ url: downloadVideos[0].files[0].torrentDownloadUrl, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
  434. expect(res.body.error).to.equal('Liu Bei')
  435. await makeRawRequest({ url: downloadVideos[1].files[0].torrentDownloadUrl, expectedStatus: HttpStatusCode.OK_200 })
  436. await makeRawRequest({ url: downloadVideos[2].files[0].torrentDownloadUrl, expectedStatus: HttpStatusCode.OK_200 })
  437. })
  438. it('Should run filter:api.download.video.allowed.result', async function () {
  439. {
  440. const refused = downloadVideos[1].files[0].fileDownloadUrl
  441. const allowed = [
  442. downloadVideos[0].files[0].fileDownloadUrl,
  443. downloadVideos[2].files[0].fileDownloadUrl
  444. ]
  445. const res = await makeRawRequest({ url: refused, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
  446. expect(res.body.error).to.equal('Cao Cao')
  447. for (const url of allowed) {
  448. await makeRawRequest({ url, expectedStatus: HttpStatusCode.OK_200 })
  449. await makeRawRequest({ url, expectedStatus: HttpStatusCode.OK_200 })
  450. }
  451. }
  452. {
  453. const refused = downloadVideos[2].streamingPlaylists[0].files[0].fileDownloadUrl
  454. const allowed = [
  455. downloadVideos[2].files[0].fileDownloadUrl,
  456. downloadVideos[0].streamingPlaylists[0].files[0].fileDownloadUrl,
  457. downloadVideos[1].streamingPlaylists[0].files[0].fileDownloadUrl
  458. ]
  459. // Only streaming playlist is refuse
  460. const res = await makeRawRequest({ url: refused, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
  461. expect(res.body.error).to.equal('Sun Jian')
  462. // But not we there is a user in res
  463. await makeRawRequest({ url: refused, token: servers[0].accessToken, expectedStatus: HttpStatusCode.OK_200 })
  464. await makeRawRequest({ url: refused, query: { videoFileToken: downloadVideo2Token }, expectedStatus: HttpStatusCode.OK_200 })
  465. // Other files work
  466. for (const url of allowed) {
  467. await makeRawRequest({ url, expectedStatus: HttpStatusCode.OK_200 })
  468. }
  469. }
  470. })
  471. })
  472. describe('Embed filters', function () {
  473. const embedVideos: VideoDetails[] = []
  474. const embedPlaylists: VideoPlaylist[] = []
  475. before(async function () {
  476. this.timeout(60000)
  477. await servers[0].config.disableTranscoding()
  478. for (const name of [ 'bad embed', 'good embed' ]) {
  479. {
  480. const uuid = (await servers[0].videos.quickUpload({ name })).uuid
  481. embedVideos.push(await servers[0].videos.get({ id: uuid }))
  482. }
  483. {
  484. const attributes = { displayName: name, videoChannelId: servers[0].store.channel.id, privacy: VideoPlaylistPrivacy.PUBLIC }
  485. const { id } = await servers[0].playlists.create({ attributes })
  486. const playlist = await servers[0].playlists.get({ playlistId: id })
  487. embedPlaylists.push(playlist)
  488. }
  489. }
  490. })
  491. it('Should run filter:html.embed.video.allowed.result', async function () {
  492. const res = await makeGetRequest({ url: servers[0].url, path: embedVideos[0].embedPath, expectedStatus: HttpStatusCode.OK_200 })
  493. expect(res.text).to.equal('Lu Bu')
  494. })
  495. it('Should run filter:html.embed.video-playlist.allowed.result', async function () {
  496. const res = await makeGetRequest({ url: servers[0].url, path: embedPlaylists[0].embedPath, expectedStatus: HttpStatusCode.OK_200 })
  497. expect(res.text).to.equal('Diao Chan')
  498. })
  499. })
  500. describe('Client HTML filters', function () {
  501. let videoUUID: string
  502. before(async function () {
  503. this.timeout(60000)
  504. const { uuid } = await servers[0].videos.quickUpload({ name: 'html video' })
  505. videoUUID = uuid
  506. })
  507. it('Should run filter:html.client.json-ld.result', async function () {
  508. const res = await makeGetRequest({ url: servers[0].url, path: '/w/' + videoUUID, expectedStatus: HttpStatusCode.OK_200 })
  509. expect(res.text).to.contain('"recordedAt":"http://example.com/recordedAt"')
  510. })
  511. it('Should not run filter:html.client.json-ld.result with an account', async function () {
  512. const res = await makeGetRequest({ url: servers[0].url, path: '/a/root', expectedStatus: HttpStatusCode.OK_200 })
  513. expect(res.text).not.to.contain('"recordedAt":"http://example.com/recordedAt"')
  514. })
  515. })
  516. describe('Search filters', function () {
  517. before(async function () {
  518. await servers[0].config.updateExistingConfig({
  519. newConfig: {
  520. search: {
  521. searchIndex: {
  522. enabled: true,
  523. isDefaultSearch: false,
  524. disableLocalSearch: false
  525. }
  526. }
  527. }
  528. })
  529. })
  530. it('Should run filter:api.search.videos.local.list.{params,result}', async function () {
  531. await servers[0].search.advancedVideoSearch({
  532. search: {
  533. search: 'Sun Quan'
  534. }
  535. })
  536. await servers[0].servers.waitUntilLog('Run hook filter:api.search.videos.local.list.params', 1)
  537. await servers[0].servers.waitUntilLog('Run hook filter:api.search.videos.local.list.result', 1)
  538. })
  539. it('Should run filter:api.search.videos.index.list.{params,result}', async function () {
  540. await servers[0].search.advancedVideoSearch({
  541. search: {
  542. search: 'Sun Quan',
  543. searchTarget: 'search-index'
  544. }
  545. })
  546. await servers[0].servers.waitUntilLog('Run hook filter:api.search.videos.local.list.params', 1)
  547. await servers[0].servers.waitUntilLog('Run hook filter:api.search.videos.local.list.result', 1)
  548. await servers[0].servers.waitUntilLog('Run hook filter:api.search.videos.index.list.params', 1)
  549. await servers[0].servers.waitUntilLog('Run hook filter:api.search.videos.index.list.result', 1)
  550. })
  551. it('Should run filter:api.search.video-channels.local.list.{params,result}', async function () {
  552. await servers[0].search.advancedChannelSearch({
  553. search: {
  554. search: 'Sun Ce'
  555. }
  556. })
  557. await servers[0].servers.waitUntilLog('Run hook filter:api.search.video-channels.local.list.params', 1)
  558. await servers[0].servers.waitUntilLog('Run hook filter:api.search.video-channels.local.list.result', 1)
  559. })
  560. it('Should run filter:api.search.video-channels.index.list.{params,result}', async function () {
  561. await servers[0].search.advancedChannelSearch({
  562. search: {
  563. search: 'Sun Ce',
  564. searchTarget: 'search-index'
  565. }
  566. })
  567. await servers[0].servers.waitUntilLog('Run hook filter:api.search.video-channels.local.list.params', 1)
  568. await servers[0].servers.waitUntilLog('Run hook filter:api.search.video-channels.local.list.result', 1)
  569. await servers[0].servers.waitUntilLog('Run hook filter:api.search.video-channels.index.list.params', 1)
  570. await servers[0].servers.waitUntilLog('Run hook filter:api.search.video-channels.index.list.result', 1)
  571. })
  572. it('Should run filter:api.search.video-playlists.local.list.{params,result}', async function () {
  573. await servers[0].search.advancedPlaylistSearch({
  574. search: {
  575. search: 'Sun Jian'
  576. }
  577. })
  578. await servers[0].servers.waitUntilLog('Run hook filter:api.search.video-playlists.local.list.params', 1)
  579. await servers[0].servers.waitUntilLog('Run hook filter:api.search.video-playlists.local.list.result', 1)
  580. })
  581. it('Should run filter:api.search.video-playlists.index.list.{params,result}', async function () {
  582. await servers[0].search.advancedPlaylistSearch({
  583. search: {
  584. search: 'Sun Jian',
  585. searchTarget: 'search-index'
  586. }
  587. })
  588. await servers[0].servers.waitUntilLog('Run hook filter:api.search.video-playlists.local.list.params', 1)
  589. await servers[0].servers.waitUntilLog('Run hook filter:api.search.video-playlists.local.list.result', 1)
  590. await servers[0].servers.waitUntilLog('Run hook filter:api.search.video-playlists.index.list.params', 1)
  591. await servers[0].servers.waitUntilLog('Run hook filter:api.search.video-playlists.index.list.result', 1)
  592. })
  593. })
  594. describe('Upload/import/live attributes filters', function () {
  595. before(async function () {
  596. await servers[0].config.enableLive({ transcoding: false, allowReplay: false })
  597. await servers[0].config.enableVideoImports()
  598. await servers[0].config.disableTranscoding()
  599. })
  600. it('Should run filter:api.video.upload.video-attribute.result', async function () {
  601. for (const mode of [ 'legacy' as 'legacy', 'resumable' as 'resumable' ]) {
  602. const { id } = await servers[0].videos.upload({ attributes: { name: 'video', description: 'upload' }, mode })
  603. const video = await servers[0].videos.get({ id })
  604. expect(video.description).to.equal('upload - filter:api.video.upload.video-attribute.result')
  605. }
  606. })
  607. it('Should run filter:api.video.import-url.video-attribute.result', async function () {
  608. const attributes = {
  609. name: 'video',
  610. description: 'import url',
  611. channelId: servers[0].store.channel.id,
  612. targetUrl: FIXTURE_URLS.goodVideo,
  613. privacy: VideoPrivacy.PUBLIC
  614. }
  615. const { video: { id } } = await servers[0].videoImports.importVideo({ attributes })
  616. const video = await servers[0].videos.get({ id })
  617. expect(video.description).to.equal('import url - filter:api.video.import-url.video-attribute.result')
  618. })
  619. it('Should run filter:api.video.import-torrent.video-attribute.result', async function () {
  620. const attributes = {
  621. name: 'video',
  622. description: 'import torrent',
  623. channelId: servers[0].store.channel.id,
  624. magnetUri: FIXTURE_URLS.magnet,
  625. privacy: VideoPrivacy.PUBLIC
  626. }
  627. const { video: { id } } = await servers[0].videoImports.importVideo({ attributes })
  628. const video = await servers[0].videos.get({ id })
  629. expect(video.description).to.equal('import torrent - filter:api.video.import-torrent.video-attribute.result')
  630. })
  631. it('Should run filter:api.video.live.video-attribute.result', async function () {
  632. const fields = {
  633. name: 'live',
  634. description: 'live',
  635. channelId: servers[0].store.channel.id,
  636. privacy: VideoPrivacy.PUBLIC
  637. }
  638. const { id } = await servers[0].live.create({ fields })
  639. const video = await servers[0].videos.get({ id })
  640. expect(video.description).to.equal('live - filter:api.video.live.video-attribute.result')
  641. })
  642. it('Should run filter:api.video.user-import.video-attribute.result', async function () {
  643. this.timeout(60000)
  644. const { data } = await servers[0].videos.listMyVideos({ token: importUserToken })
  645. for (const video of data) {
  646. expectEndWith(video.description, ' - filter:api.video.user-import.video-attribute.result')
  647. }
  648. })
  649. })
  650. describe('Stats filters', function () {
  651. it('Should run filter:api.server.stats.get.result', async function () {
  652. const data = await servers[0].stats.get()
  653. expect((data as any).customStats).to.equal(14)
  654. })
  655. })
  656. describe('Job queue filters', function () {
  657. let videoUUID: string
  658. before(async function () {
  659. this.timeout(120_000)
  660. await servers[0].config.enableMinimumTranscoding()
  661. const { uuid } = await servers[0].videos.quickUpload({ name: 'studio' })
  662. const video = await servers[0].videos.get({ id: uuid })
  663. expect(video.duration).at.least(2)
  664. videoUUID = video.uuid
  665. await waitJobs(servers)
  666. await servers[0].config.enableStudio()
  667. })
  668. it('Should run filter:job-queue.process.params', async function () {
  669. this.timeout(120_000)
  670. await servers[0].videoStudio.createEditionTasks({
  671. videoId: videoUUID,
  672. tasks: [
  673. {
  674. name: 'add-intro',
  675. options: {
  676. file: 'video_very_short_240p.mp4'
  677. }
  678. }
  679. ]
  680. })
  681. await waitJobs(servers)
  682. await servers[0].servers.waitUntilLog('Run hook filter:job-queue.process.params', 1, false)
  683. const video = await servers[0].videos.get({ id: videoUUID })
  684. expect(video.duration).at.most(2)
  685. })
  686. it('Should run filter:job-queue.process.result', async function () {
  687. await servers[0].servers.waitUntilLog('Run hook filter:job-queue.process.result', 1, false)
  688. })
  689. })
  690. describe('Transcoding filters', async function () {
  691. it('Should run filter:transcoding.auto.resolutions-to-transcode.result', async function () {
  692. const { uuid } = await servers[0].videos.quickUpload({ name: 'transcode-filter' })
  693. await waitJobs(servers)
  694. const video = await servers[0].videos.get({ id: uuid })
  695. expect(video.files).to.have.lengthOf(2)
  696. expect(video.files.find(f => f.resolution.id === 100 as any)).to.exist
  697. })
  698. })
  699. describe('Video channel filters', async function () {
  700. it('Should run filter:api.video-channels.list.params', async function () {
  701. const { data } = await servers[0].channels.list({ start: 0, count: 0 })
  702. // plugin do +1 to the count parameter
  703. expect(data).to.have.lengthOf(1)
  704. })
  705. it('Should run filter:api.video-channels.list.result', async function () {
  706. const { total } = await servers[0].channels.list({ start: 0, count: 1 })
  707. // plugin do +1 to the total parameter
  708. expect(total).to.equal(6)
  709. })
  710. it('Should run filter:api.video-channel.get.result', async function () {
  711. const channel = await servers[0].channels.get({ channelName: 'root_channel' })
  712. expect(channel.displayName).to.equal('Main root channel <3')
  713. })
  714. })
  715. describe('Activity Pub', function () {
  716. it('Should run filter:activity-pub.activity.context.build.result', async function () {
  717. const { body } = await makeActivityPubGetRequest(servers[0].url, '/w/' + videoUUID)
  718. expect(body.type).to.equal('Video')
  719. expect(body['@context'].some(c => {
  720. return typeof c === 'object' && c.recordedAt === 'https://schema.org/recordedAt'
  721. })).to.be.true
  722. })
  723. it('Should run filter:activity-pub.video.json-ld.build.result', async function () {
  724. const { body } = await makeActivityPubGetRequest(servers[0].url, '/w/' + videoUUID)
  725. expect(body.name).to.equal('default video 0')
  726. expect(body.videoName).to.equal('default video 0')
  727. })
  728. })
  729. after(async function () {
  730. await cleanupTests(servers)
  731. })
  732. })