]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - server/lib/activitypub/actors/image.ts
Fix live quota tests
[github/Chocobozzz/PeerTube.git] / server / lib / activitypub / actors / image.ts
1 import { Transaction } from 'sequelize/types'
2 import { logger } from '@server/helpers/logger'
3 import { ActorImageModel } from '@server/models/actor/actor-image'
4 import { MActorImage, MActorImages } from '@server/types/models'
5 import { ActorImageType } from '@shared/models'
6
7 type ImageInfo = {
8 name: string
9 fileUrl: string
10 height: number
11 width: number
12 onDisk?: boolean
13 }
14
15 async function updateActorImages (actor: MActorImages, type: ActorImageType, imagesInfo: ImageInfo[], t: Transaction) {
16 const getAvatarsOrBanners = () => {
17 const result = type === ActorImageType.AVATAR
18 ? actor.Avatars
19 : actor.Banners
20
21 return result || []
22 }
23
24 if (imagesInfo.length === 0) {
25 await deleteActorImages(actor, type, t)
26 }
27
28 // Cleanup old images that did not have a width
29 for (const oldImageModel of getAvatarsOrBanners()) {
30 if (oldImageModel.width) continue
31
32 await safeDeleteActorImage(actor, oldImageModel, type, t)
33 }
34
35 for (const imageInfo of imagesInfo) {
36 const oldImageModel = getAvatarsOrBanners().find(i => imageInfo.width && i.width === imageInfo.width)
37
38 if (oldImageModel) {
39 // Don't update the avatar if the file URL did not change
40 if (imageInfo.fileUrl && oldImageModel.fileUrl === imageInfo.fileUrl) {
41 continue
42 }
43
44 await safeDeleteActorImage(actor, oldImageModel, type, t)
45 }
46
47 const imageModel = await ActorImageModel.create({
48 filename: imageInfo.name,
49 onDisk: imageInfo.onDisk ?? false,
50 fileUrl: imageInfo.fileUrl,
51 height: imageInfo.height,
52 width: imageInfo.width,
53 type,
54 actorId: actor.id
55 }, { transaction: t })
56
57 addActorImage(actor, type, imageModel)
58 }
59
60 return actor
61 }
62
63 async function deleteActorImages (actor: MActorImages, type: ActorImageType, t: Transaction) {
64 try {
65 const association = buildAssociationName(type)
66
67 for (const image of actor[association]) {
68 await image.destroy({ transaction: t })
69 }
70
71 actor[association] = []
72 } catch (err) {
73 logger.error('Cannot remove old image of actor %s.', actor.url, { err })
74 }
75
76 return actor
77 }
78
79 async function safeDeleteActorImage (actor: MActorImages, toDelete: MActorImage, type: ActorImageType, t: Transaction) {
80 try {
81 await toDelete.destroy({ transaction: t })
82
83 const association = buildAssociationName(type)
84 actor[association] = actor[association].filter(image => image.id !== toDelete.id)
85 } catch (err) {
86 logger.error('Cannot remove old actor image of actor %s.', actor.url, { err })
87 }
88 }
89
90 // ---------------------------------------------------------------------------
91
92 export {
93 ImageInfo,
94
95 updateActorImages,
96 deleteActorImages
97 }
98
99 // ---------------------------------------------------------------------------
100
101 function addActorImage (actor: MActorImages, type: ActorImageType, imageModel: MActorImage) {
102 const association = buildAssociationName(type)
103 if (!actor[association]) actor[association] = []
104
105 actor[association].push(imageModel)
106 }
107
108 function buildAssociationName (type: ActorImageType) {
109 return type === ActorImageType.AVATAR
110 ? 'Avatars'
111 : 'Banners'
112 }