diff options
author | Chocobozzz <me@florianbigard.com> | 2019-08-09 11:32:40 +0200 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2019-08-09 11:32:40 +0200 |
commit | 557b13ae24019d9ab214bbea7eaa0f892c8f4b05 (patch) | |
tree | aa32396531acf93e3dfdb29880177813039ed77f /server/lib/activitypub | |
parent | c5407d7046168abb4098df1408e7aa84519cb61a (diff) | |
download | PeerTube-557b13ae24019d9ab214bbea7eaa0f892c8f4b05.tar.gz PeerTube-557b13ae24019d9ab214bbea7eaa0f892c8f4b05.tar.zst PeerTube-557b13ae24019d9ab214bbea7eaa0f892c8f4b05.zip |
Lazy load avatars
Diffstat (limited to 'server/lib/activitypub')
-rw-r--r-- | server/lib/activitypub/actor.ts | 67 | ||||
-rw-r--r-- | server/lib/activitypub/process/process-update.ts | 10 |
2 files changed, 46 insertions, 31 deletions
diff --git a/server/lib/activitypub/actor.ts b/server/lib/activitypub/actor.ts index 04296864b..9f5d12eb4 100644 --- a/server/lib/activitypub/actor.ts +++ b/server/lib/activitypub/actor.ts | |||
@@ -10,9 +10,9 @@ import { isActivityPubUrlValid } from '../../helpers/custom-validators/activityp | |||
10 | import { retryTransactionWrapper, updateInstanceWithAnother } from '../../helpers/database-utils' | 10 | import { retryTransactionWrapper, updateInstanceWithAnother } from '../../helpers/database-utils' |
11 | import { logger } from '../../helpers/logger' | 11 | import { logger } from '../../helpers/logger' |
12 | import { createPrivateAndPublicKeys } from '../../helpers/peertube-crypto' | 12 | import { createPrivateAndPublicKeys } from '../../helpers/peertube-crypto' |
13 | import { doRequest, downloadImage } from '../../helpers/requests' | 13 | import { doRequest } from '../../helpers/requests' |
14 | import { getUrlFromWebfinger } from '../../helpers/webfinger' | 14 | import { getUrlFromWebfinger } from '../../helpers/webfinger' |
15 | import { AVATARS_SIZE, MIMETYPES, WEBSERVER } from '../../initializers/constants' | 15 | import { MIMETYPES, WEBSERVER } from '../../initializers/constants' |
16 | import { AccountModel } from '../../models/account/account' | 16 | import { AccountModel } from '../../models/account/account' |
17 | import { ActorModel } from '../../models/activitypub/actor' | 17 | import { ActorModel } from '../../models/activitypub/actor' |
18 | import { AvatarModel } from '../../models/avatar/avatar' | 18 | import { AvatarModel } from '../../models/avatar/avatar' |
@@ -21,7 +21,6 @@ import { VideoChannelModel } from '../../models/video/video-channel' | |||
21 | import { JobQueue } from '../job-queue' | 21 | import { JobQueue } from '../job-queue' |
22 | import { getServerActor } from '../../helpers/utils' | 22 | import { getServerActor } from '../../helpers/utils' |
23 | import { ActorFetchByUrlType, fetchActorByUrl } from '../../helpers/actor' | 23 | import { ActorFetchByUrlType, fetchActorByUrl } from '../../helpers/actor' |
24 | import { CONFIG } from '../../initializers/config' | ||
25 | import { sequelizeTypescript } from '../../initializers/database' | 24 | import { sequelizeTypescript } from '../../initializers/database' |
26 | 25 | ||
27 | // Set account keys, this could be long so process after the account creation and do not block the client | 26 | // Set account keys, this could be long so process after the account creation and do not block the client |
@@ -141,25 +140,27 @@ async function updateActorInstance (actorInstance: ActorModel, attributes: Activ | |||
141 | actorInstance.followingUrl = attributes.following | 140 | actorInstance.followingUrl = attributes.following |
142 | } | 141 | } |
143 | 142 | ||
144 | async function updateActorAvatarInstance (actorInstance: ActorModel, avatarName: string, t: Transaction) { | 143 | async function updateActorAvatarInstance (actor: ActorModel, info: { name: string, onDisk: boolean, fileUrl: string }, t: Transaction) { |
145 | if (avatarName !== undefined) { | 144 | if (info.name !== undefined) { |
146 | if (actorInstance.avatarId) { | 145 | if (actor.avatarId) { |
147 | try { | 146 | try { |
148 | await actorInstance.Avatar.destroy({ transaction: t }) | 147 | await actor.Avatar.destroy({ transaction: t }) |
149 | } catch (err) { | 148 | } catch (err) { |
150 | logger.error('Cannot remove old avatar of actor %s.', actorInstance.url, { err }) | 149 | logger.error('Cannot remove old avatar of actor %s.', actor.url, { err }) |
151 | } | 150 | } |
152 | } | 151 | } |
153 | 152 | ||
154 | const avatar = await AvatarModel.create({ | 153 | const avatar = await AvatarModel.create({ |
155 | filename: avatarName | 154 | filename: info.name, |
155 | onDisk: info.onDisk, | ||
156 | fileUrl: info.fileUrl | ||
156 | }, { transaction: t }) | 157 | }, { transaction: t }) |
157 | 158 | ||
158 | actorInstance.set('avatarId', avatar.id) | 159 | actor.avatarId = avatar.id |
159 | actorInstance.Avatar = avatar | 160 | actor.Avatar = avatar |
160 | } | 161 | } |
161 | 162 | ||
162 | return actorInstance | 163 | return actor |
163 | } | 164 | } |
164 | 165 | ||
165 | async function fetchActorTotalItems (url: string) { | 166 | async function fetchActorTotalItems (url: string) { |
@@ -179,17 +180,17 @@ async function fetchActorTotalItems (url: string) { | |||
179 | } | 180 | } |
180 | } | 181 | } |
181 | 182 | ||
182 | async function fetchAvatarIfExists (actorJSON: ActivityPubActor) { | 183 | async function getAvatarInfoIfExists (actorJSON: ActivityPubActor) { |
183 | if ( | 184 | if ( |
184 | actorJSON.icon && actorJSON.icon.type === 'Image' && MIMETYPES.IMAGE.MIMETYPE_EXT[actorJSON.icon.mediaType] !== undefined && | 185 | actorJSON.icon && actorJSON.icon.type === 'Image' && MIMETYPES.IMAGE.MIMETYPE_EXT[actorJSON.icon.mediaType] !== undefined && |
185 | isActivityPubUrlValid(actorJSON.icon.url) | 186 | isActivityPubUrlValid(actorJSON.icon.url) |
186 | ) { | 187 | ) { |
187 | const extension = MIMETYPES.IMAGE.MIMETYPE_EXT[actorJSON.icon.mediaType] | 188 | const extension = MIMETYPES.IMAGE.MIMETYPE_EXT[actorJSON.icon.mediaType] |
188 | 189 | ||
189 | const avatarName = uuidv4() + extension | 190 | return { |
190 | await downloadImage(actorJSON.icon.url, CONFIG.STORAGE.AVATARS_DIR, avatarName, AVATARS_SIZE) | 191 | name: uuidv4() + extension, |
191 | 192 | fileUrl: actorJSON.icon.url | |
192 | return avatarName | 193 | } |
193 | } | 194 | } |
194 | 195 | ||
195 | return undefined | 196 | return undefined |
@@ -245,8 +246,14 @@ async function refreshActorIfNeeded ( | |||
245 | return sequelizeTypescript.transaction(async t => { | 246 | return sequelizeTypescript.transaction(async t => { |
246 | updateInstanceWithAnother(actor, result.actor) | 247 | updateInstanceWithAnother(actor, result.actor) |
247 | 248 | ||
248 | if (result.avatarName !== undefined) { | 249 | if (result.avatar !== undefined) { |
249 | await updateActorAvatarInstance(actor, result.avatarName, t) | 250 | const avatarInfo = { |
251 | name: result.avatar.name, | ||
252 | fileUrl: result.avatar.fileUrl, | ||
253 | onDisk: false | ||
254 | } | ||
255 | |||
256 | await updateActorAvatarInstance(actor, avatarInfo, t) | ||
250 | } | 257 | } |
251 | 258 | ||
252 | // Force update | 259 | // Force update |
@@ -279,7 +286,7 @@ export { | |||
279 | buildActorInstance, | 286 | buildActorInstance, |
280 | setAsyncActorKeys, | 287 | setAsyncActorKeys, |
281 | fetchActorTotalItems, | 288 | fetchActorTotalItems, |
282 | fetchAvatarIfExists, | 289 | getAvatarInfoIfExists, |
283 | updateActorInstance, | 290 | updateActorInstance, |
284 | refreshActorIfNeeded, | 291 | refreshActorIfNeeded, |
285 | updateActorAvatarInstance, | 292 | updateActorAvatarInstance, |
@@ -314,14 +321,17 @@ function saveActorAndServerAndModelIfNotExist ( | |||
314 | const [ server ] = await ServerModel.findOrCreate(serverOptions) | 321 | const [ server ] = await ServerModel.findOrCreate(serverOptions) |
315 | 322 | ||
316 | // Save our new account in database | 323 | // Save our new account in database |
317 | actor.set('serverId', server.id) | 324 | actor.serverId = server.id |
318 | 325 | ||
319 | // Avatar? | 326 | // Avatar? |
320 | if (result.avatarName) { | 327 | if (result.avatar) { |
321 | const avatar = await AvatarModel.create({ | 328 | const avatar = await AvatarModel.create({ |
322 | filename: result.avatarName | 329 | filename: result.avatar.name, |
330 | fileUrl: result.avatar.fileUrl, | ||
331 | onDisk: false | ||
323 | }, { transaction: t }) | 332 | }, { transaction: t }) |
324 | actor.set('avatarId', avatar.id) | 333 | |
334 | actor.avatarId = avatar.id | ||
325 | } | 335 | } |
326 | 336 | ||
327 | // Force the actor creation, sometimes Sequelize skips the save() when it thinks the instance already exists | 337 | // Force the actor creation, sometimes Sequelize skips the save() when it thinks the instance already exists |
@@ -355,7 +365,10 @@ type FetchRemoteActorResult = { | |||
355 | summary: string | 365 | summary: string |
356 | support?: string | 366 | support?: string |
357 | playlists?: string | 367 | playlists?: string |
358 | avatarName?: string | 368 | avatar?: { |
369 | name: string, | ||
370 | fileUrl: string | ||
371 | } | ||
359 | attributedTo: ActivityPubAttributedTo[] | 372 | attributedTo: ActivityPubAttributedTo[] |
360 | } | 373 | } |
361 | async function fetchRemoteActor (actorUrl: string): Promise<{ statusCode?: number, result: FetchRemoteActorResult }> { | 374 | async function fetchRemoteActor (actorUrl: string): Promise<{ statusCode?: number, result: FetchRemoteActorResult }> { |
@@ -399,7 +412,7 @@ async function fetchRemoteActor (actorUrl: string): Promise<{ statusCode?: numbe | |||
399 | followingUrl: actorJSON.following | 412 | followingUrl: actorJSON.following |
400 | }) | 413 | }) |
401 | 414 | ||
402 | const avatarName = await fetchAvatarIfExists(actorJSON) | 415 | const avatarInfo = await getAvatarInfoIfExists(actorJSON) |
403 | 416 | ||
404 | const name = actorJSON.name || actorJSON.preferredUsername | 417 | const name = actorJSON.name || actorJSON.preferredUsername |
405 | return { | 418 | return { |
@@ -407,7 +420,7 @@ async function fetchRemoteActor (actorUrl: string): Promise<{ statusCode?: numbe | |||
407 | result: { | 420 | result: { |
408 | actor, | 421 | actor, |
409 | name, | 422 | name, |
410 | avatarName, | 423 | avatar: avatarInfo, |
411 | summary: actorJSON.summary, | 424 | summary: actorJSON.summary, |
412 | support: actorJSON.support, | 425 | support: actorJSON.support, |
413 | playlists: actorJSON.playlists, | 426 | playlists: actorJSON.playlists, |
diff --git a/server/lib/activitypub/process/process-update.ts b/server/lib/activitypub/process/process-update.ts index e3c862221..414f9e375 100644 --- a/server/lib/activitypub/process/process-update.ts +++ b/server/lib/activitypub/process/process-update.ts | |||
@@ -6,7 +6,7 @@ import { sequelizeTypescript } from '../../../initializers' | |||
6 | import { AccountModel } from '../../../models/account/account' | 6 | import { AccountModel } from '../../../models/account/account' |
7 | import { ActorModel } from '../../../models/activitypub/actor' | 7 | import { ActorModel } from '../../../models/activitypub/actor' |
8 | import { VideoChannelModel } from '../../../models/video/video-channel' | 8 | import { VideoChannelModel } from '../../../models/video/video-channel' |
9 | import { fetchAvatarIfExists, updateActorAvatarInstance, updateActorInstance } from '../actor' | 9 | import { getAvatarInfoIfExists, updateActorAvatarInstance, updateActorInstance } from '../actor' |
10 | import { getOrCreateVideoAndAccountAndChannel, getOrCreateVideoChannelFromVideoObject, updateVideoFromAP } from '../videos' | 10 | import { getOrCreateVideoAndAccountAndChannel, getOrCreateVideoChannelFromVideoObject, updateVideoFromAP } from '../videos' |
11 | import { sanitizeAndCheckVideoTorrentObject } from '../../../helpers/custom-validators/activitypub/videos' | 11 | import { sanitizeAndCheckVideoTorrentObject } from '../../../helpers/custom-validators/activitypub/videos' |
12 | import { isCacheFileObjectValid } from '../../../helpers/custom-validators/activitypub/cache-file' | 12 | import { isCacheFileObjectValid } from '../../../helpers/custom-validators/activitypub/cache-file' |
@@ -105,7 +105,7 @@ async function processUpdateActor (actor: ActorModel, activity: ActivityUpdate) | |||
105 | let accountOrChannelFieldsSave: object | 105 | let accountOrChannelFieldsSave: object |
106 | 106 | ||
107 | // Fetch icon? | 107 | // Fetch icon? |
108 | const avatarName = await fetchAvatarIfExists(actorAttributesToUpdate) | 108 | const avatarInfo = await getAvatarInfoIfExists(actorAttributesToUpdate) |
109 | 109 | ||
110 | try { | 110 | try { |
111 | await sequelizeTypescript.transaction(async t => { | 111 | await sequelizeTypescript.transaction(async t => { |
@@ -118,8 +118,10 @@ async function processUpdateActor (actor: ActorModel, activity: ActivityUpdate) | |||
118 | 118 | ||
119 | await updateActorInstance(actor, actorAttributesToUpdate) | 119 | await updateActorInstance(actor, actorAttributesToUpdate) |
120 | 120 | ||
121 | if (avatarName !== undefined) { | 121 | if (avatarInfo !== undefined) { |
122 | await updateActorAvatarInstance(actor, avatarName, t) | 122 | const avatarOptions = Object.assign({}, avatarInfo, { onDisk: false }) |
123 | |||
124 | await updateActorAvatarInstance(actor, avatarOptions, t) | ||
123 | } | 125 | } |
124 | 126 | ||
125 | await actor.save({ transaction: t }) | 127 | await actor.save({ transaction: t }) |