]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/commitdiff
Add video AP hooks
authorChocobozzz <me@florianbigard.com>
Thu, 16 Mar 2023 09:36:33 +0000 (10:36 +0100)
committerChocobozzz <me@florianbigard.com>
Thu, 16 Mar 2023 09:36:33 +0000 (10:36 +0100)
server/lib/activitypub/videos/shared/creator.ts
server/lib/activitypub/videos/updater.ts
server/tests/fixtures/peertube-plugin-test/main.js
server/tests/plugins/action-hooks.ts
shared/models/plugins/server/server-hook.model.ts
support/doc/plugins/guide.md

index 07252fea20a215f0a1ff268a12140f98ccf25df7..77321d8a5cd2d327ac92d38ba6db9b4b38ce4c72 100644 (file)
@@ -1,6 +1,7 @@
 
 import { logger, loggerTagsFactory, LoggerTagsFn } from '@server/helpers/logger'
 import { sequelizeTypescript } from '@server/initializers/database'
+import { Hooks } from '@server/lib/plugins/hooks'
 import { autoBlacklistVideoIfNeeded } from '@server/lib/video-blacklist'
 import { VideoModel } from '@server/models/video/video'
 import { MThumbnail, MVideoFullLight, MVideoThumbnail } from '@server/types/models'
@@ -61,6 +62,8 @@ export class APVideoCreator extends APVideoAbstractBuilder {
 
         logger.info('Remote video with uuid %s inserted.', this.videoObject.uuid, this.lTags())
 
+        Hooks.runAction('action:activity-pub.remote-video.created', { video: videoCreated, videoAPObject: this.videoObject })
+
         return { autoBlacklisted, videoCreated }
       } catch (err) {
         // FIXME: Use rollback hook when https://github.com/sequelize/sequelize/pull/13038 is released
index 0bf32f4401c4150b90125399ea06c898d12afc52..3677dc3bb558abf90d47c024a0d00f68fa1d145c 100644 (file)
@@ -3,6 +3,7 @@ import { resetSequelizeInstance, runInReadCommittedTransaction } from '@server/h
 import { logger, loggerTagsFactory, LoggerTagsFn } from '@server/helpers/logger'
 import { Notifier } from '@server/lib/notifier'
 import { PeerTubeSocket } from '@server/lib/peertube-socket'
+import { Hooks } from '@server/lib/plugins/hooks'
 import { autoBlacklistVideoIfNeeded } from '@server/lib/video-blacklist'
 import { VideoLiveModel } from '@server/models/video/video-live'
 import { MActor, MChannelAccountLight, MChannelId, MVideoAccountLightBlacklistAllFiles, MVideoFullLight } from '@server/types/models'
@@ -81,6 +82,8 @@ export class APVideoUpdater extends APVideoAbstractBuilder {
         PeerTubeSocket.Instance.sendVideoLiveNewState(videoUpdated)
       }
 
+      Hooks.runAction('action:activity-pub.remote-video.updated', { video: videoUpdated, videoAPObject: this.videoObject })
+
       logger.info('Remote video with uuid %s updated', this.videoObject.uuid, this.lTags())
 
       return videoUpdated
index 60b8b3ccd3150fc618d098d2ed92d7441bc1e280..be0df6672dd3dc377d4eac790e73b799a3c5c627 100644 (file)
@@ -1,42 +1,53 @@
 async function register ({ registerHook, registerSetting, settingsManager, storageManager, peertubeHelpers }) {
-  const actionHooks = [
-    'action:application.listening',
-    'action:notifier.notification.created',
+  {
+    const actionHooks = [
+      'action:application.listening',
+      'action:notifier.notification.created',
 
-    'action:api.video.updated',
-    'action:api.video.deleted',
-    'action:api.video.uploaded',
-    'action:api.video.viewed',
+      'action:api.video.updated',
+      'action:api.video.deleted',
+      'action:api.video.uploaded',
+      'action:api.video.viewed',
 
-    'action:api.video-channel.created',
-    'action:api.video-channel.updated',
-    'action:api.video-channel.deleted',
+      'action:api.video-channel.created',
+      'action:api.video-channel.updated',
+      'action:api.video-channel.deleted',
 
-    'action:api.live-video.created',
+      'action:api.live-video.created',
 
-    'action:api.video-thread.created',
-    'action:api.video-comment-reply.created',
-    'action:api.video-comment.deleted',
+      'action:api.video-thread.created',
+      'action:api.video-comment-reply.created',
+      'action:api.video-comment.deleted',
 
-    'action:api.video-caption.created',
-    'action:api.video-caption.deleted',
+      'action:api.video-caption.created',
+      'action:api.video-caption.deleted',
 
-    'action:api.user.blocked',
-    'action:api.user.unblocked',
-    'action:api.user.registered',
-    'action:api.user.created',
-    'action:api.user.deleted',
-    'action:api.user.updated',
-    'action:api.user.oauth2-got-token',
+      'action:api.user.blocked',
+      'action:api.user.unblocked',
+      'action:api.user.registered',
+      'action:api.user.created',
+      'action:api.user.deleted',
+      'action:api.user.updated',
+      'action:api.user.oauth2-got-token',
 
-    'action:api.video-playlist-element.created'
-  ]
+      'action:api.video-playlist-element.created'
+    ]
 
-  for (const h of actionHooks) {
-    registerHook({
-      target: h,
-      handler: () => peertubeHelpers.logger.debug('Run hook %s.', h)
-    })
+    for (const h of actionHooks) {
+      registerHook({
+        target: h,
+        handler: () => peertubeHelpers.logger.debug('Run hook %s.', h)
+      })
+    }
+
+    for (const h of [ 'action:activity-pub.remote-video.created', 'action:activity-pub.remote-video.updated' ]) {
+      registerHook({
+        target: h,
+        handler: ({ video, videoAPObject }) => {
+          peertubeHelpers.logger.debug('Run hook %s - AP %s - video %s.', h, video.name, videoAPObject.name )
+        }
+      })
+    }
   }
 
   registerHook({
index a266ae7f14e0c59811dff62cc737a74364c204ba..98228f79de27ff0a8bf13177b6687656cfe24aaa 100644 (file)
@@ -4,6 +4,7 @@ import { ServerHookName, VideoPlaylistPrivacy, VideoPrivacy } from '@shared/mode
 import {
   cleanupTests,
   createMultipleServers,
+  doubleFollow,
   killallServers,
   PeerTubeServer,
   PluginsCommand,
@@ -36,6 +37,8 @@ describe('Test plugin action hooks', function () {
         enabled: true
       }
     })
+
+    await doubleFollow(servers[0], servers[1])
   })
 
   describe('Application hooks', function () {
@@ -231,6 +234,27 @@ describe('Test plugin action hooks', function () {
     })
   })
 
+  describe('Activity Pub hooks', function () {
+    let videoUUID: string
+
+    it('Should run action:activity-pub.remote-video.created', async function () {
+      this.timeout(30000)
+
+      const { uuid } = await servers[1].videos.quickUpload({ name: 'remote video' })
+      videoUUID = uuid
+
+      await servers[0].servers.waitUntilLog('action:activity-pub.remote-video.created - AP remote video - video remote video')
+    })
+
+    it('Should run action:activity-pub.remote-video.updated', async function () {
+      this.timeout(30000)
+
+      await servers[1].videos.update({ id: videoUUID, attributes: { name: 'remote video updated' } })
+
+      await servers[0].servers.waitUntilLog('action:activity-pub.remote-video.updated - AP remote video - video remote video')
+    })
+  })
+
   after(async function () {
     await cleanupTests(servers)
   })
index ca83672d02e0edf9f094339fef54d3b4e3b8a7b6..d2ebe936e332356e7d458ac9922b755c5924bb8e 100644 (file)
@@ -1,4 +1,4 @@
-// {hookType}:{api?}.{location}.{subLocation?}.{actionType}.{target}
+// {hookType}:{root}.{location}.{subLocation?}.{actionType}.{target}
 
 export const serverFilterHookObject = {
   // Filter params/result used to list videos for the REST API
@@ -184,7 +184,11 @@ export const serverActionHookObject = {
   'action:api.user.oauth2-got-token': true,
 
   // Fired when a video is added to a playlist
-  'action:api.video-playlist-element.created': true
+  'action:api.video-playlist-element.created': true,
+
+  // Fired when a remote video has been created/updated
+  'action:activity-pub.remote-video.created': true,
+  'action:activity-pub.remote-video.updated': true
 }
 
 export type ServerActionHookName = keyof typeof serverActionHookObject
index 337f3d97f45a4afc02c498bf450a2dd20d5516bb..c5e3236ca2874b756e2c6fe7e74cdd43f64d7f40 100644 (file)
@@ -16,6 +16,7 @@
     - [Add external auth methods](#add-external-auth-methods)
     - [Add new transcoding profiles](#add-new-transcoding-profiles)
     - [Server helpers](#server-helpers)
+    - [Federation](#federation)
   - [Client API (themes & plugins)](#client-api-themes--plugins)
     - [Get plugin static and router routes](#get-plugin-static-and-router-routes)
     - [Notifier](#notifier)
@@ -587,6 +588,49 @@ async function register ({
 
 See the [plugin API reference](https://docs.joinpeertube.org/api/plugins) to see the complete helpers list.
 
+#### Federation
+
+You can use some server hooks to federate plugin data to other PeerTube instances that may have installed your plugin.
+
+For example to federate additional video metadata:
+
+```js
+async function register ({ registerHook }) {
+
+  // Send plugin metadata to remote instances
+  // We also update the JSON LD context because we added a new field
+  {
+    registerHook({
+      target: 'filter:activity-pub.video.json-ld.build.result',
+      handler: async (jsonld, { video }) => {
+        return Object.assign(jsonld, { recordedAt: 'https://example.com/event' })
+      }
+    })
+
+    registerHook({
+      target: 'filter:activity-pub.activity.context.build.result',
+      handler: jsonld => {
+        return jsonld.concat([ { recordedAt: 'https://schema.org/recordedAt' } ])
+      }
+    })
+  }
+
+  // Save remote video metadata
+  {
+    for (const h of [ 'action:activity-pub.remote-video.created', 'action:activity-pub.remote-video.updated' ]) {
+      registerHook({
+        target: h,
+        handler: ({ video, videoAPObject }) => {
+          if (videoAPObject.recordedAt) {
+            // Save information about the video
+          }
+        }
+      })
+    }
+  }
+```
+
+
 ### Client API (themes & plugins)
 
 #### Get plugin static and router routes