@@ -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 | |||
@@ -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 | |||
@@ -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({ | |||
@@ -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) | |||
}) | |||
@@ -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 | |||
@@ -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 | |||