aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/lib/activitypub
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2021-06-17 16:02:38 +0200
committerChocobozzz <chocobozzz@cpy.re>2021-06-25 14:44:01 +0200
commit37a44fc915eef2140e22ceb96aba6b6eb2509007 (patch)
treedd4a370ecc96cf38c99b940261aadc27065da7ae /server/lib/activitypub
parent33eb19e5199cc9fa4d73c6675c97508e3e072ef9 (diff)
downloadPeerTube-37a44fc915eef2140e22ceb96aba6b6eb2509007.tar.gz
PeerTube-37a44fc915eef2140e22ceb96aba6b6eb2509007.tar.zst
PeerTube-37a44fc915eef2140e22ceb96aba6b6eb2509007.zip
Add ability to search playlists
Diffstat (limited to 'server/lib/activitypub')
-rw-r--r--server/lib/activitypub/actors/get.ts2
-rw-r--r--server/lib/activitypub/playlists/create-update.ts34
-rw-r--r--server/lib/activitypub/playlists/get.ts35
-rw-r--r--server/lib/activitypub/playlists/index.ts1
-rw-r--r--server/lib/activitypub/playlists/refresh.ts13
-rw-r--r--server/lib/activitypub/playlists/shared/object-to-model-attributes.ts6
-rw-r--r--server/lib/activitypub/process/process-create.ts2
-rw-r--r--server/lib/activitypub/process/process-update.ts2
-rw-r--r--server/lib/activitypub/videos/get.ts7
9 files changed, 76 insertions, 26 deletions
diff --git a/server/lib/activitypub/actors/get.ts b/server/lib/activitypub/actors/get.ts
index d7cf2b678..8681ea02a 100644
--- a/server/lib/activitypub/actors/get.ts
+++ b/server/lib/activitypub/actors/get.ts
@@ -116,7 +116,7 @@ async function scheduleOutboxFetchIfNeeded (actor: MActor, created: boolean, ref
116async function schedulePlaylistFetchIfNeeded (actor: MActorAccountId, created: boolean, accountPlaylistsUrl: string) { 116async function schedulePlaylistFetchIfNeeded (actor: MActorAccountId, created: boolean, accountPlaylistsUrl: string) {
117 // We created a new account: fetch the playlists 117 // We created a new account: fetch the playlists
118 if (created === true && actor.Account && accountPlaylistsUrl) { 118 if (created === true && actor.Account && accountPlaylistsUrl) {
119 const payload = { uri: accountPlaylistsUrl, accountId: actor.Account.id, type: 'account-playlists' as 'account-playlists' } 119 const payload = { uri: accountPlaylistsUrl, type: 'account-playlists' as 'account-playlists' }
120 await JobQueue.Instance.createJobWithPromise({ type: 'activitypub-http-fetcher', payload }) 120 await JobQueue.Instance.createJobWithPromise({ type: 'activitypub-http-fetcher', payload })
121 } 121 }
122} 122}
diff --git a/server/lib/activitypub/playlists/create-update.ts b/server/lib/activitypub/playlists/create-update.ts
index 37d748de4..ea3e61ac5 100644
--- a/server/lib/activitypub/playlists/create-update.ts
+++ b/server/lib/activitypub/playlists/create-update.ts
@@ -1,3 +1,5 @@
1import * as Bluebird from 'bluebird'
2import { getAPId } from '@server/helpers/activitypub'
1import { isArray } from '@server/helpers/custom-validators/misc' 3import { isArray } from '@server/helpers/custom-validators/misc'
2import { logger, loggerTagsFactory } from '@server/helpers/logger' 4import { logger, loggerTagsFactory } from '@server/helpers/logger'
3import { CRAWL_REQUEST_CONCURRENCY } from '@server/initializers/constants' 5import { CRAWL_REQUEST_CONCURRENCY } from '@server/initializers/constants'
@@ -6,7 +8,7 @@ import { updatePlaylistMiniatureFromUrl } from '@server/lib/thumbnail'
6import { VideoPlaylistModel } from '@server/models/video/video-playlist' 8import { VideoPlaylistModel } from '@server/models/video/video-playlist'
7import { VideoPlaylistElementModel } from '@server/models/video/video-playlist-element' 9import { VideoPlaylistElementModel } from '@server/models/video/video-playlist-element'
8import { FilteredModelAttributes } from '@server/types' 10import { FilteredModelAttributes } from '@server/types'
9import { MAccountDefault, MAccountId, MThumbnail, MVideoPlaylist, MVideoPlaylistFull } from '@server/types/models' 11import { MThumbnail, MVideoPlaylist, MVideoPlaylistFull, MVideoPlaylistVideosLength } from '@server/types/models'
10import { AttributesOnly } from '@shared/core-utils' 12import { AttributesOnly } from '@shared/core-utils'
11import { PlaylistObject } from '@shared/models' 13import { PlaylistObject } from '@shared/models'
12import { getOrCreateAPActor } from '../actors' 14import { getOrCreateAPActor } from '../actors'
@@ -19,11 +21,9 @@ import {
19 playlistObjectToDBAttributes 21 playlistObjectToDBAttributes
20} from './shared' 22} from './shared'
21 23
22import Bluebird = require('bluebird')
23
24const lTags = loggerTagsFactory('ap', 'video-playlist') 24const lTags = loggerTagsFactory('ap', 'video-playlist')
25 25
26async function createAccountPlaylists (playlistUrls: string[], account: MAccountDefault) { 26async function createAccountPlaylists (playlistUrls: string[]) {
27 await Bluebird.map(playlistUrls, async playlistUrl => { 27 await Bluebird.map(playlistUrls, async playlistUrl => {
28 try { 28 try {
29 const exists = await VideoPlaylistModel.doesPlaylistExist(playlistUrl) 29 const exists = await VideoPlaylistModel.doesPlaylistExist(playlistUrl)
@@ -35,19 +35,19 @@ async function createAccountPlaylists (playlistUrls: string[], account: MAccount
35 throw new Error(`Cannot refresh remote playlist ${playlistUrl}: invalid body.`) 35 throw new Error(`Cannot refresh remote playlist ${playlistUrl}: invalid body.`)
36 } 36 }
37 37
38 return createOrUpdateVideoPlaylist(playlistObject, account, playlistObject.to) 38 return createOrUpdateVideoPlaylist(playlistObject)
39 } catch (err) { 39 } catch (err) {
40 logger.warn('Cannot add playlist element %s.', playlistUrl, { err, ...lTags(playlistUrl) }) 40 logger.warn('Cannot add playlist element %s.', playlistUrl, { err, ...lTags(playlistUrl) })
41 } 41 }
42 }, { concurrency: CRAWL_REQUEST_CONCURRENCY }) 42 }, { concurrency: CRAWL_REQUEST_CONCURRENCY })
43} 43}
44 44
45async function createOrUpdateVideoPlaylist (playlistObject: PlaylistObject, byAccount: MAccountId, to: string[]) { 45async function createOrUpdateVideoPlaylist (playlistObject: PlaylistObject, to?: string[]) {
46 const playlistAttributes = playlistObjectToDBAttributes(playlistObject, byAccount, to) 46 const playlistAttributes = playlistObjectToDBAttributes(playlistObject, to || playlistObject.to)
47 47
48 await setVideoChannelIfNeeded(playlistObject, playlistAttributes) 48 await setVideoChannel(playlistObject, playlistAttributes)
49 49
50 const [ upsertPlaylist ] = await VideoPlaylistModel.upsert<MVideoPlaylist>(playlistAttributes, { returning: true }) 50 const [ upsertPlaylist ] = await VideoPlaylistModel.upsert<MVideoPlaylistVideosLength>(playlistAttributes, { returning: true })
51 51
52 const playlistElementUrls = await fetchElementUrls(playlistObject) 52 const playlistElementUrls = await fetchElementUrls(playlistObject)
53 53
@@ -56,7 +56,10 @@ async function createOrUpdateVideoPlaylist (playlistObject: PlaylistObject, byAc
56 56
57 await updatePlaylistThumbnail(playlistObject, playlist) 57 await updatePlaylistThumbnail(playlistObject, playlist)
58 58
59 return rebuildVideoPlaylistElements(playlistElementUrls, playlist) 59 const elementsLength = await rebuildVideoPlaylistElements(playlistElementUrls, playlist)
60 playlist.setVideosLength(elementsLength)
61
62 return playlist
60} 63}
61 64
62// --------------------------------------------------------------------------- 65// ---------------------------------------------------------------------------
@@ -68,10 +71,12 @@ export {
68 71
69// --------------------------------------------------------------------------- 72// ---------------------------------------------------------------------------
70 73
71async function setVideoChannelIfNeeded (playlistObject: PlaylistObject, playlistAttributes: AttributesOnly<VideoPlaylistModel>) { 74async function setVideoChannel (playlistObject: PlaylistObject, playlistAttributes: AttributesOnly<VideoPlaylistModel>) {
72 if (!isArray(playlistObject.attributedTo) || playlistObject.attributedTo.length !== 1) return 75 if (!isArray(playlistObject.attributedTo) || playlistObject.attributedTo.length !== 1) {
76 throw new Error('Not attributed to for playlist object ' + getAPId(playlistObject))
77 }
73 78
74 const actor = await getOrCreateAPActor(playlistObject.attributedTo[0]) 79 const actor = await getOrCreateAPActor(playlistObject.attributedTo[0], 'all')
75 80
76 if (!actor.VideoChannel) { 81 if (!actor.VideoChannel) {
77 logger.warn('Playlist "attributedTo" %s is not a video channel.', playlistObject.id, { playlistObject, ...lTags(playlistObject.id) }) 82 logger.warn('Playlist "attributedTo" %s is not a video channel.', playlistObject.id, { playlistObject, ...lTags(playlistObject.id) })
@@ -79,6 +84,7 @@ async function setVideoChannelIfNeeded (playlistObject: PlaylistObject, playlist
79 } 84 }
80 85
81 playlistAttributes.videoChannelId = actor.VideoChannel.id 86 playlistAttributes.videoChannelId = actor.VideoChannel.id
87 playlistAttributes.ownerAccountId = actor.VideoChannel.Account.id
82} 88}
83 89
84async function fetchElementUrls (playlistObject: PlaylistObject) { 90async function fetchElementUrls (playlistObject: PlaylistObject) {
@@ -128,7 +134,7 @@ async function rebuildVideoPlaylistElements (elementUrls: string[], playlist: MV
128 134
129 logger.info('Rebuilt playlist %s with %s elements.', playlist.url, elementsToCreate.length, lTags(playlist.uuid, playlist.url)) 135 logger.info('Rebuilt playlist %s with %s elements.', playlist.url, elementsToCreate.length, lTags(playlist.uuid, playlist.url))
130 136
131 return undefined 137 return elementsToCreate.length
132} 138}
133 139
134async function buildElementsDBAttributes (elementUrls: string[], playlist: MVideoPlaylist) { 140async function buildElementsDBAttributes (elementUrls: string[], playlist: MVideoPlaylist) {
diff --git a/server/lib/activitypub/playlists/get.ts b/server/lib/activitypub/playlists/get.ts
new file mode 100644
index 000000000..2c19c503a
--- /dev/null
+++ b/server/lib/activitypub/playlists/get.ts
@@ -0,0 +1,35 @@
1import { getAPId } from '@server/helpers/activitypub'
2import { VideoPlaylistModel } from '@server/models/video/video-playlist'
3import { MVideoPlaylistFullSummary } from '@server/types/models'
4import { APObject } from '@shared/models'
5import { createOrUpdateVideoPlaylist } from './create-update'
6import { scheduleRefreshIfNeeded } from './refresh'
7import { fetchRemoteVideoPlaylist } from './shared'
8
9async function getOrCreateAPVideoPlaylist (playlistObjectArg: APObject): Promise<MVideoPlaylistFullSummary> {
10 const playlistUrl = getAPId(playlistObjectArg)
11
12 const playlistFromDatabase = await VideoPlaylistModel.loadByUrlWithAccountAndChannelSummary(playlistUrl)
13
14 if (playlistFromDatabase) {
15 scheduleRefreshIfNeeded(playlistFromDatabase)
16
17 return playlistFromDatabase
18 }
19
20 const { playlistObject } = await fetchRemoteVideoPlaylist(playlistUrl)
21 if (!playlistObject) throw new Error('Cannot fetch remote playlist with url: ' + playlistUrl)
22
23 // playlistUrl is just an alias/rediraction, so process object id instead
24 if (playlistObject.id !== playlistUrl) return getOrCreateAPVideoPlaylist(playlistObject)
25
26 const playlistCreated = await createOrUpdateVideoPlaylist(playlistObject)
27
28 return playlistCreated
29}
30
31// ---------------------------------------------------------------------------
32
33export {
34 getOrCreateAPVideoPlaylist
35}
diff --git a/server/lib/activitypub/playlists/index.ts b/server/lib/activitypub/playlists/index.ts
index 2885830b4..e2470a674 100644
--- a/server/lib/activitypub/playlists/index.ts
+++ b/server/lib/activitypub/playlists/index.ts
@@ -1,2 +1,3 @@
1export * from './get'
1export * from './create-update' 2export * from './create-update'
2export * from './refresh' 3export * from './refresh'
diff --git a/server/lib/activitypub/playlists/refresh.ts b/server/lib/activitypub/playlists/refresh.ts
index 6f3a6be37..ef3cb3fe4 100644
--- a/server/lib/activitypub/playlists/refresh.ts
+++ b/server/lib/activitypub/playlists/refresh.ts
@@ -1,10 +1,17 @@
1import { logger, loggerTagsFactory } from '@server/helpers/logger' 1import { logger, loggerTagsFactory } from '@server/helpers/logger'
2import { PeerTubeRequestError } from '@server/helpers/requests' 2import { PeerTubeRequestError } from '@server/helpers/requests'
3import { MVideoPlaylistOwner } from '@server/types/models' 3import { JobQueue } from '@server/lib/job-queue'
4import { MVideoPlaylist, MVideoPlaylistOwner } from '@server/types/models'
4import { HttpStatusCode } from '@shared/core-utils' 5import { HttpStatusCode } from '@shared/core-utils'
5import { createOrUpdateVideoPlaylist } from './create-update' 6import { createOrUpdateVideoPlaylist } from './create-update'
6import { fetchRemoteVideoPlaylist } from './shared' 7import { fetchRemoteVideoPlaylist } from './shared'
7 8
9function scheduleRefreshIfNeeded (playlist: MVideoPlaylist) {
10 if (!playlist.isOutdated()) return
11
12 JobQueue.Instance.createJob({ type: 'activitypub-refresher', payload: { type: 'video-playlist', url: playlist.url } })
13}
14
8async function refreshVideoPlaylistIfNeeded (videoPlaylist: MVideoPlaylistOwner): Promise<MVideoPlaylistOwner> { 15async function refreshVideoPlaylistIfNeeded (videoPlaylist: MVideoPlaylistOwner): Promise<MVideoPlaylistOwner> {
9 if (!videoPlaylist.isOutdated()) return videoPlaylist 16 if (!videoPlaylist.isOutdated()) return videoPlaylist
10 17
@@ -22,8 +29,7 @@ async function refreshVideoPlaylistIfNeeded (videoPlaylist: MVideoPlaylistOwner)
22 return videoPlaylist 29 return videoPlaylist
23 } 30 }
24 31
25 const byAccount = videoPlaylist.OwnerAccount 32 await createOrUpdateVideoPlaylist(playlistObject)
26 await createOrUpdateVideoPlaylist(playlistObject, byAccount, playlistObject.to)
27 33
28 return videoPlaylist 34 return videoPlaylist
29 } catch (err) { 35 } catch (err) {
@@ -42,5 +48,6 @@ async function refreshVideoPlaylistIfNeeded (videoPlaylist: MVideoPlaylistOwner)
42} 48}
43 49
44export { 50export {
51 scheduleRefreshIfNeeded,
45 refreshVideoPlaylistIfNeeded 52 refreshVideoPlaylistIfNeeded
46} 53}
diff --git a/server/lib/activitypub/playlists/shared/object-to-model-attributes.ts b/server/lib/activitypub/playlists/shared/object-to-model-attributes.ts
index 6ec44485e..70fd335bc 100644
--- a/server/lib/activitypub/playlists/shared/object-to-model-attributes.ts
+++ b/server/lib/activitypub/playlists/shared/object-to-model-attributes.ts
@@ -1,11 +1,11 @@
1import { ACTIVITY_PUB } from '@server/initializers/constants' 1import { ACTIVITY_PUB } from '@server/initializers/constants'
2import { VideoPlaylistModel } from '@server/models/video/video-playlist' 2import { VideoPlaylistModel } from '@server/models/video/video-playlist'
3import { VideoPlaylistElementModel } from '@server/models/video/video-playlist-element' 3import { VideoPlaylistElementModel } from '@server/models/video/video-playlist-element'
4import { MAccountId, MVideoId, MVideoPlaylistId } from '@server/types/models' 4import { MVideoId, MVideoPlaylistId } from '@server/types/models'
5import { AttributesOnly } from '@shared/core-utils' 5import { AttributesOnly } from '@shared/core-utils'
6import { PlaylistElementObject, PlaylistObject, VideoPlaylistPrivacy } from '@shared/models' 6import { PlaylistElementObject, PlaylistObject, VideoPlaylistPrivacy } from '@shared/models'
7 7
8function playlistObjectToDBAttributes (playlistObject: PlaylistObject, byAccount: MAccountId, to: string[]) { 8function playlistObjectToDBAttributes (playlistObject: PlaylistObject, to: string[]) {
9 const privacy = to.includes(ACTIVITY_PUB.PUBLIC) 9 const privacy = to.includes(ACTIVITY_PUB.PUBLIC)
10 ? VideoPlaylistPrivacy.PUBLIC 10 ? VideoPlaylistPrivacy.PUBLIC
11 : VideoPlaylistPrivacy.UNLISTED 11 : VideoPlaylistPrivacy.UNLISTED
@@ -16,7 +16,7 @@ function playlistObjectToDBAttributes (playlistObject: PlaylistObject, byAccount
16 privacy, 16 privacy,
17 url: playlistObject.id, 17 url: playlistObject.id,
18 uuid: playlistObject.uuid, 18 uuid: playlistObject.uuid,
19 ownerAccountId: byAccount.id, 19 ownerAccountId: null,
20 videoChannelId: null, 20 videoChannelId: null,
21 createdAt: new Date(playlistObject.published), 21 createdAt: new Date(playlistObject.published),
22 updatedAt: new Date(playlistObject.updated) 22 updatedAt: new Date(playlistObject.updated)
diff --git a/server/lib/activitypub/process/process-create.ts b/server/lib/activitypub/process/process-create.ts
index 6b7f5aae8..70e048d6e 100644
--- a/server/lib/activitypub/process/process-create.ts
+++ b/server/lib/activitypub/process/process-create.ts
@@ -128,5 +128,5 @@ async function processCreatePlaylist (activity: ActivityCreate, byActor: MActorS
128 128
129 if (!byAccount) throw new Error('Cannot create video playlist with the non account actor ' + byActor.url) 129 if (!byAccount) throw new Error('Cannot create video playlist with the non account actor ' + byActor.url)
130 130
131 await createOrUpdateVideoPlaylist(playlistObject, byAccount, activity.to) 131 await createOrUpdateVideoPlaylist(playlistObject, activity.to)
132} 132}
diff --git a/server/lib/activitypub/process/process-update.ts b/server/lib/activitypub/process/process-update.ts
index aa80d5d09..f40008a6b 100644
--- a/server/lib/activitypub/process/process-update.ts
+++ b/server/lib/activitypub/process/process-update.ts
@@ -111,5 +111,5 @@ async function processUpdatePlaylist (byActor: MActorSignature, activity: Activi
111 111
112 if (!byAccount) throw new Error('Cannot update video playlist with the non account actor ' + byActor.url) 112 if (!byAccount) throw new Error('Cannot update video playlist with the non account actor ' + byActor.url)
113 113
114 await createOrUpdateVideoPlaylist(playlistObject, byAccount, activity.to) 114 await createOrUpdateVideoPlaylist(playlistObject, activity.to)
115} 115}
diff --git a/server/lib/activitypub/videos/get.ts b/server/lib/activitypub/videos/get.ts
index 7bb14adc4..f3e2f0625 100644
--- a/server/lib/activitypub/videos/get.ts
+++ b/server/lib/activitypub/videos/get.ts
@@ -3,6 +3,7 @@ import { retryTransactionWrapper } from '@server/helpers/database-utils'
3import { JobQueue } from '@server/lib/job-queue' 3import { JobQueue } from '@server/lib/job-queue'
4import { loadVideoByUrl, VideoLoadByUrlType } from '@server/lib/model-loaders' 4import { loadVideoByUrl, VideoLoadByUrlType } from '@server/lib/model-loaders'
5import { MVideoAccountLightBlacklistAllFiles, MVideoImmutable, MVideoThumbnail } from '@server/types/models' 5import { MVideoAccountLightBlacklistAllFiles, MVideoImmutable, MVideoThumbnail } from '@server/types/models'
6import { APObject } from '@shared/models'
6import { refreshVideoIfNeeded } from './refresh' 7import { refreshVideoIfNeeded } from './refresh'
7import { APVideoCreator, fetchRemoteVideo, SyncParam, syncVideoExternalAttributes } from './shared' 8import { APVideoCreator, fetchRemoteVideo, SyncParam, syncVideoExternalAttributes } from './shared'
8 9
@@ -13,21 +14,21 @@ type GetVideoResult <T> = Promise<{
13}> 14}>
14 15
15type GetVideoParamAll = { 16type GetVideoParamAll = {
16 videoObject: { id: string } | string 17 videoObject: APObject
17 syncParam?: SyncParam 18 syncParam?: SyncParam
18 fetchType?: 'all' 19 fetchType?: 'all'
19 allowRefresh?: boolean 20 allowRefresh?: boolean
20} 21}
21 22
22type GetVideoParamImmutable = { 23type GetVideoParamImmutable = {
23 videoObject: { id: string } | string 24 videoObject: APObject
24 syncParam?: SyncParam 25 syncParam?: SyncParam
25 fetchType: 'only-immutable-attributes' 26 fetchType: 'only-immutable-attributes'
26 allowRefresh: false 27 allowRefresh: false
27} 28}
28 29
29type GetVideoParamOther = { 30type GetVideoParamOther = {
30 videoObject: { id: string } | string 31 videoObject: APObject
31 syncParam?: SyncParam 32 syncParam?: SyncParam
32 fetchType?: 'all' | 'only-video' 33 fetchType?: 'all' | 'only-video'
33 allowRefresh?: boolean 34 allowRefresh?: boolean