aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--server/controllers/api/video-channel.ts24
-rw-r--r--server/controllers/api/videos/index.ts9
-rw-r--r--server/lib/model-loaders/video.ts10
-rw-r--r--server/tests/fixtures/peertube-plugin-test/main.js27
-rw-r--r--server/tests/plugins/action-hooks.ts33
-rw-r--r--server/tests/plugins/filter-hooks.ts24
-rw-r--r--shared/models/plugins/server/server-hook.model.ts14
7 files changed, 124 insertions, 17 deletions
diff --git a/server/controllers/api/video-channel.ts b/server/controllers/api/video-channel.ts
index 2454b1ec9..411ec8630 100644
--- a/server/controllers/api/video-channel.ts
+++ b/server/controllers/api/video-channel.ts
@@ -126,7 +126,7 @@ videoChannelRouter.delete('/:nameWithHost',
126 126
127videoChannelRouter.get('/:nameWithHost', 127videoChannelRouter.get('/:nameWithHost',
128 asyncMiddleware(videoChannelsNameWithHostValidator), 128 asyncMiddleware(videoChannelsNameWithHostValidator),
129 getVideoChannel 129 asyncMiddleware(getVideoChannel)
130) 130)
131 131
132videoChannelRouter.get('/:nameWithHost/video-playlists', 132videoChannelRouter.get('/:nameWithHost/video-playlists',
@@ -171,12 +171,19 @@ export {
171 171
172async function listVideoChannels (req: express.Request, res: express.Response) { 172async function listVideoChannels (req: express.Request, res: express.Response) {
173 const serverActor = await getServerActor() 173 const serverActor = await getServerActor()
174 const resultList = await VideoChannelModel.listForApi({ 174
175 const apiOptions = await Hooks.wrapObject({
175 actorId: serverActor.id, 176 actorId: serverActor.id,
176 start: req.query.start, 177 start: req.query.start,
177 count: req.query.count, 178 count: req.query.count,
178 sort: req.query.sort 179 sort: req.query.sort
179 }) 180 }, 'filter:api.video-channels.list.params')
181
182 const resultList = await Hooks.wrapPromiseFun(
183 VideoChannelModel.listForApi,
184 apiOptions,
185 'filter:api.video-channels.list.result'
186 )
180 187
181 return res.json(getFormattedObjects(resultList.data, resultList.total)) 188 return res.json(getFormattedObjects(resultList.data, resultList.total))
182} 189}
@@ -243,6 +250,8 @@ async function addVideoChannel (req: express.Request, res: express.Response) {
243 auditLogger.create(getAuditIdFromRes(res), new VideoChannelAuditView(videoChannelCreated.toFormattedJSON())) 250 auditLogger.create(getAuditIdFromRes(res), new VideoChannelAuditView(videoChannelCreated.toFormattedJSON()))
244 logger.info('Video channel %s created.', videoChannelCreated.Actor.url) 251 logger.info('Video channel %s created.', videoChannelCreated.Actor.url)
245 252
253 Hooks.runAction('action:api.video-channel.created', { videoChannel: videoChannelCreated, req, res })
254
246 return res.json({ 255 return res.json({
247 videoChannel: { 256 videoChannel: {
248 id: videoChannelCreated.id 257 id: videoChannelCreated.id
@@ -281,6 +290,8 @@ async function updateVideoChannel (req: express.Request, res: express.Response)
281 oldVideoChannelAuditKeys 290 oldVideoChannelAuditKeys
282 ) 291 )
283 292
293 Hooks.runAction('action:api.video-channel.updated', { videoChannel: videoChannelInstanceUpdated, req, res })
294
284 logger.info('Video channel %s updated.', videoChannelInstance.Actor.url) 295 logger.info('Video channel %s updated.', videoChannelInstance.Actor.url)
285 }) 296 })
286 } catch (err) { 297 } catch (err) {
@@ -310,6 +321,8 @@ async function removeVideoChannel (req: express.Request, res: express.Response)
310 321
311 await videoChannelInstance.destroy({ transaction: t }) 322 await videoChannelInstance.destroy({ transaction: t })
312 323
324 Hooks.runAction('action:api.video-channel.deleted', { videoChannel: videoChannelInstance, req, res })
325
313 auditLogger.delete(getAuditIdFromRes(res), new VideoChannelAuditView(videoChannelInstance.toFormattedJSON())) 326 auditLogger.delete(getAuditIdFromRes(res), new VideoChannelAuditView(videoChannelInstance.toFormattedJSON()))
314 logger.info('Video channel %s deleted.', videoChannelInstance.Actor.url) 327 logger.info('Video channel %s deleted.', videoChannelInstance.Actor.url)
315 }) 328 })
@@ -317,8 +330,9 @@ async function removeVideoChannel (req: express.Request, res: express.Response)
317 return res.type('json').status(HttpStatusCode.NO_CONTENT_204).end() 330 return res.type('json').status(HttpStatusCode.NO_CONTENT_204).end()
318} 331}
319 332
320function getVideoChannel (req: express.Request, res: express.Response) { 333async function getVideoChannel (req: express.Request, res: express.Response) {
321 const videoChannel = res.locals.videoChannel 334 const id = res.locals.videoChannel.id
335 const videoChannel = await Hooks.wrapObject(res.locals.videoChannel, 'filter:api.video-channel.get.result', { id })
322 336
323 if (videoChannel.isOutdated()) { 337 if (videoChannel.isOutdated()) {
324 JobQueue.Instance.createJob({ type: 'activitypub-refresher', payload: { type: 'actor', url: videoChannel.Actor.url } }) 338 JobQueue.Instance.createJob({ type: 'activitypub-refresher', payload: { type: 'actor', url: videoChannel.Actor.url } })
diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts
index d4e08293e..eca72c397 100644
--- a/server/controllers/api/videos/index.ts
+++ b/server/controllers/api/videos/index.ts
@@ -110,7 +110,7 @@ videosRouter.get('/:id',
110 optionalAuthenticate, 110 optionalAuthenticate,
111 asyncMiddleware(videosCustomGetValidator('for-api')), 111 asyncMiddleware(videosCustomGetValidator('for-api')),
112 asyncMiddleware(checkVideoFollowConstraints), 112 asyncMiddleware(checkVideoFollowConstraints),
113 getVideo 113 asyncMiddleware(getVideo)
114) 114)
115 115
116videosRouter.delete('/:id', 116videosRouter.delete('/:id',
@@ -144,8 +144,11 @@ function listVideoPrivacies (_req: express.Request, res: express.Response) {
144 res.json(VIDEO_PRIVACIES) 144 res.json(VIDEO_PRIVACIES)
145} 145}
146 146
147function getVideo (_req: express.Request, res: express.Response) { 147async function getVideo (_req: express.Request, res: express.Response) {
148 const video = res.locals.videoAPI 148 const videoId = res.locals.videoAPI.id
149 const userId = res.locals.oauth?.token.User.id
150
151 const video = await Hooks.wrapObject(res.locals.videoAPI, 'filter:api.video.get.result', { id: videoId, userId })
149 152
150 if (video.isOutdated()) { 153 if (video.isOutdated()) {
151 JobQueue.Instance.createJob({ type: 'activitypub-refresher', payload: { type: 'video', url: video.url } }) 154 JobQueue.Instance.createJob({ type: 'activitypub-refresher', payload: { type: 'video', url: video.url } })
diff --git a/server/lib/model-loaders/video.ts b/server/lib/model-loaders/video.ts
index cef6a367c..a64389a89 100644
--- a/server/lib/model-loaders/video.ts
+++ b/server/lib/model-loaders/video.ts
@@ -7,7 +7,7 @@ import {
7 MVideoImmutable, 7 MVideoImmutable,
8 MVideoThumbnail 8 MVideoThumbnail
9} from '@server/types/models' 9} from '@server/types/models'
10import { Hooks } from '../plugins/hooks' 10
11 11
12type VideoLoadType = 'for-api' | 'all' | 'only-video' | 'id' | 'none' | 'only-immutable-attributes' 12type VideoLoadType = 'for-api' | 'all' | 'only-video' | 'id' | 'none' | 'only-immutable-attributes'
13 13
@@ -27,13 +27,7 @@ function loadVideo (
27 userId?: number 27 userId?: number
28): Promise<MVideoFullLight | MVideoThumbnail | MVideoId | MVideoImmutable> { 28): Promise<MVideoFullLight | MVideoThumbnail | MVideoId | MVideoImmutable> {
29 29
30 if (fetchType === 'for-api') { 30 if (fetchType === 'for-api') return VideoModel.loadForGetAPI({ id, userId })
31 return Hooks.wrapPromiseFun(
32 VideoModel.loadForGetAPI,
33 { id, userId },
34 'filter:api.video.get.result'
35 )
36 }
37 31
38 if (fetchType === 'all') return VideoModel.loadFull(id, undefined, userId) 32 if (fetchType === 'all') return VideoModel.loadFull(id, undefined, userId)
39 33
diff --git a/server/tests/fixtures/peertube-plugin-test/main.js b/server/tests/fixtures/peertube-plugin-test/main.js
index c395ac7aa..8d7584eba 100644
--- a/server/tests/fixtures/peertube-plugin-test/main.js
+++ b/server/tests/fixtures/peertube-plugin-test/main.js
@@ -7,6 +7,10 @@ async function register ({ registerHook, registerSetting, settingsManager, stora
7 'action:api.video.uploaded', 7 'action:api.video.uploaded',
8 'action:api.video.viewed', 8 'action:api.video.viewed',
9 9
10 'action:api.video-channel.created',
11 'action:api.video-channel.updated',
12 'action:api.video-channel.deleted',
13
10 'action:api.live-video.created', 14 'action:api.live-video.created',
11 15
12 'action:api.video-thread.created', 16 'action:api.video-thread.created',
@@ -93,6 +97,29 @@ async function register ({ registerHook, registerSetting, settingsManager, stora
93 } 97 }
94 }) 98 })
95 99
100 // ---------------------------------------------------------------------------
101
102 registerHook({
103 target: 'filter:api.video-channels.list.params',
104 handler: obj => addToCount(obj, 1)
105 })
106
107 registerHook({
108 target: 'filter:api.video-channels.list.result',
109 handler: obj => addToTotal(obj, 1)
110 })
111
112 registerHook({
113 target: 'filter:api.video-channel.get.result',
114 handler: channel => {
115 channel.name += ' <3'
116
117 return channel
118 }
119 })
120
121 // ---------------------------------------------------------------------------
122
96 for (const hook of [ 'filter:api.video.upload.accept.result', 'filter:api.live-video.create.accept.result' ]) { 123 for (const hook of [ 'filter:api.video.upload.accept.result', 'filter:api.live-video.create.accept.result' ]) {
97 registerHook({ 124 registerHook({
98 target: hook, 125 target: hook,
diff --git a/server/tests/plugins/action-hooks.ts b/server/tests/plugins/action-hooks.ts
index 57ede2701..209db95a4 100644
--- a/server/tests/plugins/action-hooks.ts
+++ b/server/tests/plugins/action-hooks.ts
@@ -65,6 +65,39 @@ describe('Test plugin action hooks', function () {
65 65
66 await checkHook('action:api.video.viewed') 66 await checkHook('action:api.video.viewed')
67 }) 67 })
68
69 it('Should run action:api.video.deleted', async function () {
70 await servers[0].videos.remove({ id: videoUUID })
71
72 await checkHook('action:api.video.deleted')
73 })
74
75 after(async function () {
76 const { uuid } = await servers[0].videos.quickUpload({ name: 'video' })
77 videoUUID = uuid
78 })
79 })
80
81 describe('Video channel hooks', function () {
82 const channelName = 'my_super_channel'
83
84 it('Should run action:api.video-channel.created', async function () {
85 await servers[0].channels.create({ attributes: { name: channelName } })
86
87 await checkHook('action:api.video-channel.created')
88 })
89
90 it('Should run action:api.video-channel.updated', async function () {
91 await servers[0].channels.update({ channelName, attributes: { displayName: 'my display name' } })
92
93 await checkHook('action:api.video-channel.updated')
94 })
95
96 it('Should run action:api.video-channel.deleted', async function () {
97 await servers[0].channels.delete({ channelName })
98
99 await checkHook('action:api.video-channel.deleted')
100 })
68 }) 101 })
69 102
70 describe('Live hooks', function () { 103 describe('Live hooks', function () {
diff --git a/server/tests/plugins/filter-hooks.ts b/server/tests/plugins/filter-hooks.ts
index 5ed24a58e..015459ead 100644
--- a/server/tests/plugins/filter-hooks.ts
+++ b/server/tests/plugins/filter-hooks.ts
@@ -295,7 +295,7 @@ describe('Test plugin filter hooks', function () {
295 await servers[0].servers.waitUntilLog('Run hook filter:api.overviews.videos.list.result', 3) 295 await servers[0].servers.waitUntilLog('Run hook filter:api.overviews.videos.list.result', 3)
296 }) 296 })
297 297
298 describe('Should run filter:video.auto-blacklist.result', function () { 298 describe('filter:video.auto-blacklist.result', function () {
299 299
300 async function checkIsBlacklisted (id: number | string, value: boolean) { 300 async function checkIsBlacklisted (id: number | string, value: boolean) {
301 const video = await servers[0].videos.getWithToken({ id }) 301 const video = await servers[0].videos.getWithToken({ id })
@@ -691,6 +691,28 @@ describe('Test plugin filter hooks', function () {
691 }) 691 })
692 }) 692 })
693 693
694 describe('Video channel filters', async function () {
695
696 it('Should run filter:api.video-channels.list.params', async function () {
697 const { data } = await servers[0].channels.list({ start: 0, count: 0 })
698
699 // plugin do +1 to the count parameter
700 expect(data).to.have.lengthOf(1)
701 })
702
703 it('Should run filter:api.video-channels.list.result', async function () {
704 const { total } = await servers[0].channels.list({ start: 0, count: 1 })
705
706 // plugin do +1 to the total parameter
707 expect(total).to.equal(4)
708 })
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
694 after(async function () { 716 after(async function () {
695 await cleanupTests(servers) 717 await cleanupTests(servers)
696 }) 718 })
diff --git a/shared/models/plugins/server/server-hook.model.ts b/shared/models/plugins/server/server-hook.model.ts
index 08da95177..5f3a5be10 100644
--- a/shared/models/plugins/server/server-hook.model.ts
+++ b/shared/models/plugins/server/server-hook.model.ts
@@ -45,6 +45,13 @@ export const serverFilterHookObject = {
45 // Used to get detailed video information (video watch page for example) 45 // Used to get detailed video information (video watch page for example)
46 'filter:api.video.get.result': true, 46 'filter:api.video.get.result': true,
47 47
48 // Filter params/results when listing video channels
49 'filter:api.video-channels.list.params': true,
50 'filter:api.video-channels.list.result': true,
51
52 // Filter the result when getting a video channel
53 'filter:api.video-channel.get.result': true,
54
48 // Filter the result of the accept upload/live, import via torrent/url functions 55 // Filter the result of the accept upload/live, import via torrent/url functions
49 // If this function returns false then the upload is aborted with an error 56 // If this function returns false then the upload is aborted with an error
50 'filter:api.video.upload.accept.result': true, 57 'filter:api.video.upload.accept.result': true,
@@ -116,6 +123,13 @@ export const serverActionHookObject = {
116 // Fired when a local video is viewed 123 // Fired when a local video is viewed
117 'action:api.video.viewed': true, 124 'action:api.video.viewed': true,
118 125
126 // Fired when a video channel is created
127 'action:api.video-channel.created': true,
128 // Fired when a video channel is updated
129 'action:api.video-channel.updated': true,
130 // Fired when a video channel is deleted
131 'action:api.video-channel.deleted': true,
132
119 // Fired when a live video is created 133 // Fired when a live video is created
120 'action:api.live-video.created': true, 134 'action:api.live-video.created': true,
121 135