diff options
-rw-r--r-- | scripts/regenerate-thumbnails.ts | 5 | ||||
-rw-r--r-- | server/helpers/image-utils.ts | 6 | ||||
-rw-r--r-- | server/initializers/constants.ts | 2 | ||||
-rw-r--r-- | server/initializers/migrations/0635-actor-image-size.ts | 35 | ||||
-rw-r--r-- | server/lib/activitypub/actor.ts | 35 | ||||
-rw-r--r-- | server/lib/activitypub/videos.ts | 38 | ||||
-rw-r--r-- | server/lib/actor-image.ts | 2 | ||||
-rw-r--r-- | server/lib/thumbnail.ts | 10 | ||||
-rw-r--r-- | server/models/account/actor-image.ts | 17 | ||||
-rw-r--r-- | server/models/activitypub/actor.ts | 7 | ||||
-rw-r--r-- | server/tests/api/videos/video-channels.ts | 11 | ||||
-rw-r--r-- | shared/extra-utils/miscs/sql.ts | 6 |
12 files changed, 126 insertions, 48 deletions
diff --git a/scripts/regenerate-thumbnails.ts b/scripts/regenerate-thumbnails.ts index 0213b8a22..b95343c0b 100644 --- a/scripts/regenerate-thumbnails.ts +++ b/scripts/regenerate-thumbnails.ts | |||
@@ -4,12 +4,11 @@ registerTSPaths() | |||
4 | import * as Bluebird from 'bluebird' | 4 | import * as Bluebird from 'bluebird' |
5 | import * as program from 'commander' | 5 | import * as program from 'commander' |
6 | import { pathExists, remove } from 'fs-extra' | 6 | import { pathExists, remove } from 'fs-extra' |
7 | import { processImage } from '@server/helpers/image-utils' | 7 | import { generateImageFilename, processImage } from '@server/helpers/image-utils' |
8 | import { THUMBNAILS_SIZE } from '@server/initializers/constants' | 8 | import { THUMBNAILS_SIZE } from '@server/initializers/constants' |
9 | import { VideoModel } from '@server/models/video/video' | 9 | import { VideoModel } from '@server/models/video/video' |
10 | import { MVideo } from '@server/types/models' | 10 | import { MVideo } from '@server/types/models' |
11 | import { initDatabaseModels } from '@server/initializers/database' | 11 | import { initDatabaseModels } from '@server/initializers/database' |
12 | import { ActorImageModel } from '@server/models/account/actor-image' | ||
13 | 12 | ||
14 | program | 13 | program |
15 | .description('Regenerate local thumbnails using preview files') | 14 | .description('Regenerate local thumbnails using preview files') |
@@ -52,7 +51,7 @@ async function processVideo (videoArg: MVideo) { | |||
52 | const oldPath = thumbnail.getPath() | 51 | const oldPath = thumbnail.getPath() |
53 | 52 | ||
54 | // Update thumbnail | 53 | // Update thumbnail |
55 | thumbnail.filename = ActorImageModel.generateFilename() | 54 | thumbnail.filename = generateImageFilename() |
56 | thumbnail.width = size.width | 55 | thumbnail.width = size.width |
57 | thumbnail.height = size.height | 56 | thumbnail.height = size.height |
58 | 57 | ||
diff --git a/server/helpers/image-utils.ts b/server/helpers/image-utils.ts index 9285c12fc..6f6f8d4da 100644 --- a/server/helpers/image-utils.ts +++ b/server/helpers/image-utils.ts | |||
@@ -1,9 +1,14 @@ | |||
1 | import { copy, readFile, remove, rename } from 'fs-extra' | 1 | import { copy, readFile, remove, rename } from 'fs-extra' |
2 | import * as Jimp from 'jimp' | 2 | import * as Jimp from 'jimp' |
3 | import { extname } from 'path' | 3 | import { extname } from 'path' |
4 | import { v4 as uuidv4 } from 'uuid' | ||
4 | import { convertWebPToJPG, processGIF } from './ffmpeg-utils' | 5 | import { convertWebPToJPG, processGIF } from './ffmpeg-utils' |
5 | import { logger } from './logger' | 6 | import { logger } from './logger' |
6 | 7 | ||
8 | function generateImageFilename (extension = '.jpg') { | ||
9 | return uuidv4() + extension | ||
10 | } | ||
11 | |||
7 | async function processImage ( | 12 | async function processImage ( |
8 | path: string, | 13 | path: string, |
9 | destination: string, | 14 | destination: string, |
@@ -31,6 +36,7 @@ async function processImage ( | |||
31 | // --------------------------------------------------------------------------- | 36 | // --------------------------------------------------------------------------- |
32 | 37 | ||
33 | export { | 38 | export { |
39 | generateImageFilename, | ||
34 | processImage | 40 | processImage |
35 | } | 41 | } |
36 | 42 | ||
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts index 2637213a4..1802257df 100644 --- a/server/initializers/constants.ts +++ b/server/initializers/constants.ts | |||
@@ -24,7 +24,7 @@ import { CONFIG, registerConfigChangedHandler } from './config' | |||
24 | 24 | ||
25 | // --------------------------------------------------------------------------- | 25 | // --------------------------------------------------------------------------- |
26 | 26 | ||
27 | const LAST_MIGRATION_VERSION = 630 | 27 | const LAST_MIGRATION_VERSION = 635 |
28 | 28 | ||
29 | // --------------------------------------------------------------------------- | 29 | // --------------------------------------------------------------------------- |
30 | 30 | ||
diff --git a/server/initializers/migrations/0635-actor-image-size.ts b/server/initializers/migrations/0635-actor-image-size.ts new file mode 100644 index 000000000..d7c5da8c3 --- /dev/null +++ b/server/initializers/migrations/0635-actor-image-size.ts | |||
@@ -0,0 +1,35 @@ | |||
1 | import * as Sequelize from 'sequelize' | ||
2 | |||
3 | async function up (utils: { | ||
4 | transaction: Sequelize.Transaction | ||
5 | queryInterface: Sequelize.QueryInterface | ||
6 | sequelize: Sequelize.Sequelize | ||
7 | db: any | ||
8 | }): Promise<void> { | ||
9 | { | ||
10 | const data = { | ||
11 | type: Sequelize.INTEGER, | ||
12 | defaultValue: null, | ||
13 | allowNull: true | ||
14 | } | ||
15 | await utils.queryInterface.addColumn('actorImage', 'height', data) | ||
16 | } | ||
17 | |||
18 | { | ||
19 | const data = { | ||
20 | type: Sequelize.INTEGER, | ||
21 | defaultValue: null, | ||
22 | allowNull: true | ||
23 | } | ||
24 | await utils.queryInterface.addColumn('actorImage', 'width', data) | ||
25 | } | ||
26 | } | ||
27 | |||
28 | function down (options) { | ||
29 | throw new Error('Not implemented.') | ||
30 | } | ||
31 | |||
32 | export { | ||
33 | up, | ||
34 | down | ||
35 | } | ||
diff --git a/server/lib/activitypub/actor.ts b/server/lib/activitypub/actor.ts index 917fed6ec..eec951d4e 100644 --- a/server/lib/activitypub/actor.ts +++ b/server/lib/activitypub/actor.ts | |||
@@ -170,7 +170,13 @@ async function updateActorInstance (actorInstance: ActorModel, attributes: Activ | |||
170 | } | 170 | } |
171 | } | 171 | } |
172 | 172 | ||
173 | type ImageInfo = { name: string, onDisk?: boolean, fileUrl: string } | 173 | type ImageInfo = { |
174 | name: string | ||
175 | fileUrl: string | ||
176 | height: number | ||
177 | width: number | ||
178 | onDisk?: boolean | ||
179 | } | ||
174 | async function updateActorImageInstance (actor: MActorImages, type: ActorImageType, imageInfo: ImageInfo | null, t: Transaction) { | 180 | async function updateActorImageInstance (actor: MActorImages, type: ActorImageType, imageInfo: ImageInfo | null, t: Transaction) { |
175 | const oldImageModel = type === ActorImageType.AVATAR | 181 | const oldImageModel = type === ActorImageType.AVATAR |
176 | ? actor.Avatar | 182 | ? actor.Avatar |
@@ -194,7 +200,9 @@ async function updateActorImageInstance (actor: MActorImages, type: ActorImageTy | |||
194 | filename: imageInfo.name, | 200 | filename: imageInfo.name, |
195 | onDisk: imageInfo.onDisk ?? false, | 201 | onDisk: imageInfo.onDisk ?? false, |
196 | fileUrl: imageInfo.fileUrl, | 202 | fileUrl: imageInfo.fileUrl, |
197 | type: type | 203 | height: imageInfo.height, |
204 | width: imageInfo.width, | ||
205 | type | ||
198 | }, { transaction: t }) | 206 | }, { transaction: t }) |
199 | 207 | ||
200 | setActorImage(actor, type, imageModel) | 208 | setActorImage(actor, type, imageModel) |
@@ -257,6 +265,8 @@ function getImageInfoIfExists (actorJSON: ActivityPubActor, type: ActorImageType | |||
257 | return { | 265 | return { |
258 | name: uuidv4() + extension, | 266 | name: uuidv4() + extension, |
259 | fileUrl: icon.url, | 267 | fileUrl: icon.url, |
268 | height: icon.height, | ||
269 | width: icon.width, | ||
260 | type | 270 | type |
261 | } | 271 | } |
262 | } | 272 | } |
@@ -408,6 +418,8 @@ function saveActorAndServerAndModelIfNotExist ( | |||
408 | const avatar = await ActorImageModel.create({ | 418 | const avatar = await ActorImageModel.create({ |
409 | filename: result.avatar.name, | 419 | filename: result.avatar.name, |
410 | fileUrl: result.avatar.fileUrl, | 420 | fileUrl: result.avatar.fileUrl, |
421 | width: result.avatar.width, | ||
422 | height: result.avatar.height, | ||
411 | onDisk: false, | 423 | onDisk: false, |
412 | type: ActorImageType.AVATAR | 424 | type: ActorImageType.AVATAR |
413 | }, { transaction: t }) | 425 | }, { transaction: t }) |
@@ -420,6 +432,8 @@ function saveActorAndServerAndModelIfNotExist ( | |||
420 | const banner = await ActorImageModel.create({ | 432 | const banner = await ActorImageModel.create({ |
421 | filename: result.banner.name, | 433 | filename: result.banner.name, |
422 | fileUrl: result.banner.fileUrl, | 434 | fileUrl: result.banner.fileUrl, |
435 | width: result.banner.width, | ||
436 | height: result.banner.height, | ||
423 | onDisk: false, | 437 | onDisk: false, |
424 | type: ActorImageType.BANNER | 438 | type: ActorImageType.BANNER |
425 | }, { transaction: t }) | 439 | }, { transaction: t }) |
@@ -470,20 +484,21 @@ function saveActorAndServerAndModelIfNotExist ( | |||
470 | } | 484 | } |
471 | } | 485 | } |
472 | 486 | ||
487 | type ImageResult = { | ||
488 | name: string | ||
489 | fileUrl: string | ||
490 | height: number | ||
491 | width: number | ||
492 | } | ||
493 | |||
473 | type FetchRemoteActorResult = { | 494 | type FetchRemoteActorResult = { |
474 | actor: MActor | 495 | actor: MActor |
475 | name: string | 496 | name: string |
476 | summary: string | 497 | summary: string |
477 | support?: string | 498 | support?: string |
478 | playlists?: string | 499 | playlists?: string |
479 | avatar?: { | 500 | avatar?: ImageResult |
480 | name: string | 501 | banner?: ImageResult |
481 | fileUrl: string | ||
482 | } | ||
483 | banner?: { | ||
484 | name: string | ||
485 | fileUrl: string | ||
486 | } | ||
487 | attributedTo: ActivityPubAttributedTo[] | 502 | attributedTo: ActivityPubAttributedTo[] |
488 | } | 503 | } |
489 | async function fetchRemoteActor (actorUrl: string): Promise<{ statusCode?: number, result: FetchRemoteActorResult }> { | 504 | async function fetchRemoteActor (actorUrl: string): Promise<{ statusCode?: number, result: FetchRemoteActorResult }> { |
diff --git a/server/lib/activitypub/videos.ts b/server/lib/activitypub/videos.ts index 492b97b9e..9014791c0 100644 --- a/server/lib/activitypub/videos.ts +++ b/server/lib/activitypub/videos.ts | |||
@@ -1,9 +1,8 @@ | |||
1 | import * as Bluebird from 'bluebird' | 1 | import * as Bluebird from 'bluebird' |
2 | import { maxBy, minBy } from 'lodash' | 2 | import { maxBy, minBy } from 'lodash' |
3 | import * as magnetUtil from 'magnet-uri' | 3 | import * as magnetUtil from 'magnet-uri' |
4 | import { basename, join } from 'path' | 4 | import { basename } from 'path' |
5 | import { Transaction } from 'sequelize/types' | 5 | import { Transaction } from 'sequelize/types' |
6 | import { ActorImageModel } from '@server/models/account/actor-image' | ||
7 | import { TrackerModel } from '@server/models/server/tracker' | 6 | import { TrackerModel } from '@server/models/server/tracker' |
8 | import { VideoLiveModel } from '@server/models/video/video-live' | 7 | import { VideoLiveModel } from '@server/models/video/video-live' |
9 | import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' | 8 | import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' |
@@ -17,7 +16,7 @@ import { | |||
17 | ActivityUrlObject, | 16 | ActivityUrlObject, |
18 | ActivityVideoUrlObject | 17 | ActivityVideoUrlObject |
19 | } from '../../../shared/index' | 18 | } from '../../../shared/index' |
20 | import { ActivityIconObject, ActivityTrackerUrlObject, VideoObject } from '../../../shared/models/activitypub/objects' | 19 | import { ActivityTrackerUrlObject, VideoObject } from '../../../shared/models/activitypub/objects' |
21 | import { VideoPrivacy } from '../../../shared/models/videos' | 20 | import { VideoPrivacy } from '../../../shared/models/videos' |
22 | import { ThumbnailType } from '../../../shared/models/videos/thumbnail.type' | 21 | import { ThumbnailType } from '../../../shared/models/videos/thumbnail.type' |
23 | import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type' | 22 | import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type' |
@@ -35,7 +34,6 @@ import { doJSONRequest, PeerTubeRequestError } from '../../helpers/requests' | |||
35 | import { fetchVideoByUrl, getExtFromMimetype, VideoFetchByUrlType } from '../../helpers/video' | 34 | import { fetchVideoByUrl, getExtFromMimetype, VideoFetchByUrlType } from '../../helpers/video' |
36 | import { | 35 | import { |
37 | ACTIVITY_PUB, | 36 | ACTIVITY_PUB, |
38 | LAZY_STATIC_PATHS, | ||
39 | MIMETYPES, | 37 | MIMETYPES, |
40 | P2P_MEDIA_LOADER_PEER_VERSION, | 38 | P2P_MEDIA_LOADER_PEER_VERSION, |
41 | PREVIEWS_SIZE, | 39 | PREVIEWS_SIZE, |
@@ -368,13 +366,13 @@ async function updateVideoFromAP (options: { | |||
368 | 366 | ||
369 | if (thumbnailModel) await videoUpdated.addAndSaveThumbnail(thumbnailModel, t) | 367 | if (thumbnailModel) await videoUpdated.addAndSaveThumbnail(thumbnailModel, t) |
370 | 368 | ||
371 | if (videoUpdated.getPreview()) { | 369 | const previewIcon = getPreviewFromIcons(videoObject) |
372 | const previewUrl = getPreviewUrl(getPreviewFromIcons(videoObject), video) | 370 | if (videoUpdated.getPreview() && previewIcon) { |
373 | const previewModel = createPlaceholderThumbnail({ | 371 | const previewModel = createPlaceholderThumbnail({ |
374 | fileUrl: previewUrl, | 372 | fileUrl: previewIcon.url, |
375 | video, | 373 | video, |
376 | type: ThumbnailType.PREVIEW, | 374 | type: ThumbnailType.PREVIEW, |
377 | size: PREVIEWS_SIZE | 375 | size: previewIcon |
378 | }) | 376 | }) |
379 | await videoUpdated.addAndSaveThumbnail(previewModel, t) | 377 | await videoUpdated.addAndSaveThumbnail(previewModel, t) |
380 | } | 378 | } |
@@ -629,15 +627,17 @@ async function createVideo (videoObject: VideoObject, channel: MChannelAccountLi | |||
629 | 627 | ||
630 | if (thumbnailModel) await videoCreated.addAndSaveThumbnail(thumbnailModel, t) | 628 | if (thumbnailModel) await videoCreated.addAndSaveThumbnail(thumbnailModel, t) |
631 | 629 | ||
632 | const previewUrl = getPreviewUrl(getPreviewFromIcons(videoObject), videoCreated) | 630 | const previewIcon = getPreviewFromIcons(videoObject) |
633 | const previewModel = createPlaceholderThumbnail({ | 631 | if (previewIcon) { |
634 | fileUrl: previewUrl, | 632 | const previewModel = createPlaceholderThumbnail({ |
635 | video: videoCreated, | 633 | fileUrl: previewIcon.url, |
636 | type: ThumbnailType.PREVIEW, | 634 | video: videoCreated, |
637 | size: PREVIEWS_SIZE | 635 | type: ThumbnailType.PREVIEW, |
638 | }) | 636 | size: previewIcon |
637 | }) | ||
639 | 638 | ||
640 | if (thumbnailModel) await videoCreated.addAndSaveThumbnail(previewModel, t) | 639 | await videoCreated.addAndSaveThumbnail(previewModel, t) |
640 | } | ||
641 | 641 | ||
642 | // Process files | 642 | // Process files |
643 | const videoFileAttributes = videoFileActivityUrlToDBAttributes(videoCreated, videoObject.url) | 643 | const videoFileAttributes = videoFileActivityUrlToDBAttributes(videoCreated, videoObject.url) |
@@ -897,12 +897,6 @@ function getPreviewFromIcons (videoObject: VideoObject) { | |||
897 | return maxBy(validIcons, 'width') | 897 | return maxBy(validIcons, 'width') |
898 | } | 898 | } |
899 | 899 | ||
900 | function getPreviewUrl (previewIcon: ActivityIconObject, video: MVideoWithHost) { | ||
901 | return previewIcon | ||
902 | ? previewIcon.url | ||
903 | : buildRemoteVideoBaseUrl(video, join(LAZY_STATIC_PATHS.PREVIEWS, ActorImageModel.generateFilename())) | ||
904 | } | ||
905 | |||
906 | function getTrackerUrls (object: VideoObject, video: MVideoWithHost) { | 900 | function getTrackerUrls (object: VideoObject, video: MVideoWithHost) { |
907 | let wsFound = false | 901 | let wsFound = false |
908 | 902 | ||
diff --git a/server/lib/actor-image.ts b/server/lib/actor-image.ts index fa1a2a18a..f271f0b5b 100644 --- a/server/lib/actor-image.ts +++ b/server/lib/actor-image.ts | |||
@@ -34,6 +34,8 @@ async function updateLocalActorImageFile ( | |||
34 | const actorImageInfo = { | 34 | const actorImageInfo = { |
35 | name: imageName, | 35 | name: imageName, |
36 | fileUrl: null, | 36 | fileUrl: null, |
37 | height: imageSize.height, | ||
38 | width: imageSize.width, | ||
37 | onDisk: true | 39 | onDisk: true |
38 | } | 40 | } |
39 | 41 | ||
diff --git a/server/lib/thumbnail.ts b/server/lib/thumbnail.ts index e1176ac08..cfee69cfc 100644 --- a/server/lib/thumbnail.ts +++ b/server/lib/thumbnail.ts | |||
@@ -1,8 +1,8 @@ | |||
1 | import { join } from 'path' | 1 | import { join } from 'path' |
2 | import { ActorImageModel } from '@server/models/account/actor-image' | 2 | |
3 | import { ThumbnailType } from '../../shared/models/videos/thumbnail.type' | 3 | import { ThumbnailType } from '../../shared/models/videos/thumbnail.type' |
4 | import { generateImageFromVideoFile } from '../helpers/ffmpeg-utils' | 4 | import { generateImageFromVideoFile } from '../helpers/ffmpeg-utils' |
5 | import { processImage } from '../helpers/image-utils' | 5 | import { generateImageFilename, processImage } from '../helpers/image-utils' |
6 | import { downloadImage } from '../helpers/requests' | 6 | import { downloadImage } from '../helpers/requests' |
7 | import { CONFIG } from '../initializers/config' | 7 | import { CONFIG } from '../initializers/config' |
8 | import { ASSETS_PATH, PREVIEWS_SIZE, THUMBNAILS_SIZE } from '../initializers/constants' | 8 | import { ASSETS_PATH, PREVIEWS_SIZE, THUMBNAILS_SIZE } from '../initializers/constants' |
@@ -12,7 +12,7 @@ import { MThumbnail } from '../types/models/video/thumbnail' | |||
12 | import { MVideoPlaylistThumbnail } from '../types/models/video/video-playlist' | 12 | import { MVideoPlaylistThumbnail } from '../types/models/video/video-playlist' |
13 | import { getVideoFilePath } from './video-paths' | 13 | import { getVideoFilePath } from './video-paths' |
14 | 14 | ||
15 | type ImageSize = { height: number, width: number } | 15 | type ImageSize = { height?: number, width?: number } |
16 | 16 | ||
17 | function createPlaylistMiniatureFromExisting (options: { | 17 | function createPlaylistMiniatureFromExisting (options: { |
18 | inputPath: string | 18 | inputPath: string |
@@ -201,7 +201,7 @@ function buildMetadataFromVideo (video: MVideoThumbnail, type: ThumbnailType, si | |||
201 | : undefined | 201 | : undefined |
202 | 202 | ||
203 | if (type === ThumbnailType.MINIATURE) { | 203 | if (type === ThumbnailType.MINIATURE) { |
204 | const filename = ActorImageModel.generateFilename() | 204 | const filename = generateImageFilename() |
205 | const basePath = CONFIG.STORAGE.THUMBNAILS_DIR | 205 | const basePath = CONFIG.STORAGE.THUMBNAILS_DIR |
206 | 206 | ||
207 | return { | 207 | return { |
@@ -215,7 +215,7 @@ function buildMetadataFromVideo (video: MVideoThumbnail, type: ThumbnailType, si | |||
215 | } | 215 | } |
216 | 216 | ||
217 | if (type === ThumbnailType.PREVIEW) { | 217 | if (type === ThumbnailType.PREVIEW) { |
218 | const filename = ActorImageModel.generateFilename() | 218 | const filename = generateImageFilename() |
219 | const basePath = CONFIG.STORAGE.PREVIEWS_DIR | 219 | const basePath = CONFIG.STORAGE.PREVIEWS_DIR |
220 | 220 | ||
221 | return { | 221 | return { |
diff --git a/server/models/account/actor-image.ts b/server/models/account/actor-image.ts index f7438991a..ae05b4969 100644 --- a/server/models/account/actor-image.ts +++ b/server/models/account/actor-image.ts | |||
@@ -1,7 +1,6 @@ | |||
1 | import { remove } from 'fs-extra' | 1 | import { remove } from 'fs-extra' |
2 | import { join } from 'path' | 2 | import { join } from 'path' |
3 | import { AfterDestroy, AllowNull, Column, CreatedAt, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' | 3 | import { AfterDestroy, AllowNull, Column, CreatedAt, Default, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' |
4 | import { v4 as uuidv4 } from 'uuid' | ||
5 | import { MActorImageFormattable } from '@server/types/models' | 4 | import { MActorImageFormattable } from '@server/types/models' |
6 | import { ActorImageType } from '@shared/models' | 5 | import { ActorImageType } from '@shared/models' |
7 | import { ActorImage } from '../../../shared/models/actors/actor-image.model' | 6 | import { ActorImage } from '../../../shared/models/actors/actor-image.model' |
@@ -27,6 +26,16 @@ export class ActorImageModel extends Model { | |||
27 | filename: string | 26 | filename: string |
28 | 27 | ||
29 | @AllowNull(true) | 28 | @AllowNull(true) |
29 | @Default(null) | ||
30 | @Column | ||
31 | height: number | ||
32 | |||
33 | @AllowNull(true) | ||
34 | @Default(null) | ||
35 | @Column | ||
36 | width: number | ||
37 | |||
38 | @AllowNull(true) | ||
30 | @Is('ActorImageFileUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'fileUrl', true)) | 39 | @Is('ActorImageFileUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'fileUrl', true)) |
31 | @Column | 40 | @Column |
32 | fileUrl: string | 41 | fileUrl: string |
@@ -54,10 +63,6 @@ export class ActorImageModel extends Model { | |||
54 | .catch(err => logger.error('Cannot remove actor image file %s.', instance.filename, err)) | 63 | .catch(err => logger.error('Cannot remove actor image file %s.', instance.filename, err)) |
55 | } | 64 | } |
56 | 65 | ||
57 | static generateFilename () { | ||
58 | return uuidv4() + '.jpg' | ||
59 | } | ||
60 | |||
61 | static loadByName (filename: string) { | 66 | static loadByName (filename: string) { |
62 | const query = { | 67 | const query = { |
63 | where: { | 68 | where: { |
diff --git a/server/models/activitypub/actor.ts b/server/models/activitypub/actor.ts index 6595f11e2..a6c724f26 100644 --- a/server/models/activitypub/actor.ts +++ b/server/models/activitypub/actor.ts | |||
@@ -570,16 +570,21 @@ export class ActorModel extends Model { | |||
570 | icon = { | 570 | icon = { |
571 | type: 'Image', | 571 | type: 'Image', |
572 | mediaType: MIMETYPES.IMAGE.EXT_MIMETYPE[extension], | 572 | mediaType: MIMETYPES.IMAGE.EXT_MIMETYPE[extension], |
573 | height: this.Avatar.height, | ||
574 | width: this.Avatar.width, | ||
573 | url: this.getAvatarUrl() | 575 | url: this.getAvatarUrl() |
574 | } | 576 | } |
575 | } | 577 | } |
576 | 578 | ||
577 | if (this.bannerId) { | 579 | if (this.bannerId) { |
578 | const extension = extname((this as MActorAPChannel).Banner.filename) | 580 | const banner = (this as MActorAPChannel).Banner |
581 | const extension = extname(banner.filename) | ||
579 | 582 | ||
580 | image = { | 583 | image = { |
581 | type: 'Image', | 584 | type: 'Image', |
582 | mediaType: MIMETYPES.IMAGE.EXT_MIMETYPE[extension], | 585 | mediaType: MIMETYPES.IMAGE.EXT_MIMETYPE[extension], |
586 | height: banner.height, | ||
587 | width: banner.width, | ||
583 | url: this.getBannerUrl() | 588 | url: this.getBannerUrl() |
584 | } | 589 | } |
585 | } | 590 | } |
diff --git a/server/tests/api/videos/video-channels.ts b/server/tests/api/videos/video-channels.ts index e50582218..d12d58e75 100644 --- a/server/tests/api/videos/video-channels.ts +++ b/server/tests/api/videos/video-channels.ts | |||
@@ -2,12 +2,14 @@ | |||
2 | 2 | ||
3 | import 'mocha' | 3 | import 'mocha' |
4 | import * as chai from 'chai' | 4 | import * as chai from 'chai' |
5 | import { basename } from 'path' | ||
5 | import { | 6 | import { |
6 | cleanupTests, | 7 | cleanupTests, |
7 | createUser, | 8 | createUser, |
8 | deleteVideoChannelImage, | 9 | deleteVideoChannelImage, |
9 | doubleFollow, | 10 | doubleFollow, |
10 | flushAndRunMultipleServers, | 11 | flushAndRunMultipleServers, |
12 | getActorImage, | ||
11 | getVideo, | 13 | getVideo, |
12 | getVideoChannel, | 14 | getVideoChannel, |
13 | getVideoChannelVideos, | 15 | getVideoChannelVideos, |
@@ -31,6 +33,7 @@ import { | |||
31 | } from '../../../../shared/extra-utils/index' | 33 | } from '../../../../shared/extra-utils/index' |
32 | import { waitJobs } from '../../../../shared/extra-utils/server/jobs' | 34 | import { waitJobs } from '../../../../shared/extra-utils/server/jobs' |
33 | import { User, Video, VideoChannel, VideoDetails } from '../../../../shared/index' | 35 | import { User, Video, VideoChannel, VideoDetails } from '../../../../shared/index' |
36 | import { ACTOR_IMAGES_SIZE } from '@server/initializers/constants' | ||
34 | 37 | ||
35 | const expect = chai.expect | 38 | const expect = chai.expect |
36 | 39 | ||
@@ -288,6 +291,10 @@ describe('Test video channels', function () { | |||
288 | const videoChannel = await findChannel(server, secondVideoChannelId) | 291 | const videoChannel = await findChannel(server, secondVideoChannelId) |
289 | 292 | ||
290 | await testImage(server.url, 'avatar-resized', videoChannel.avatar.path, '.png') | 293 | await testImage(server.url, 'avatar-resized', videoChannel.avatar.path, '.png') |
294 | |||
295 | const row = await getActorImage(server.internalServerNumber, basename(videoChannel.avatar.path)) | ||
296 | expect(row.height).to.equal(ACTOR_IMAGES_SIZE.AVATARS.height) | ||
297 | expect(row.width).to.equal(ACTOR_IMAGES_SIZE.AVATARS.width) | ||
291 | } | 298 | } |
292 | }) | 299 | }) |
293 | 300 | ||
@@ -311,6 +318,10 @@ describe('Test video channels', function () { | |||
311 | const videoChannel = res.body | 318 | const videoChannel = res.body |
312 | 319 | ||
313 | await testImage(server.url, 'banner-resized', videoChannel.banner.path) | 320 | await testImage(server.url, 'banner-resized', videoChannel.banner.path) |
321 | |||
322 | const row = await getActorImage(server.internalServerNumber, basename(videoChannel.banner.path)) | ||
323 | expect(row.height).to.equal(ACTOR_IMAGES_SIZE.BANNERS.height) | ||
324 | expect(row.width).to.equal(ACTOR_IMAGES_SIZE.BANNERS.width) | ||
314 | } | 325 | } |
315 | }) | 326 | }) |
316 | 327 | ||
diff --git a/shared/extra-utils/miscs/sql.ts b/shared/extra-utils/miscs/sql.ts index 35e493456..65a0aa5fe 100644 --- a/shared/extra-utils/miscs/sql.ts +++ b/shared/extra-utils/miscs/sql.ts | |||
@@ -82,6 +82,11 @@ async function countVideoViewsOf (internalServerNumber: number, uuid: string) { | |||
82 | return parseInt(total + '', 10) | 82 | return parseInt(total + '', 10) |
83 | } | 83 | } |
84 | 84 | ||
85 | function getActorImage (internalServerNumber: number, filename: string) { | ||
86 | return selectQuery(internalServerNumber, `SELECT * FROM "actorImage" WHERE filename = '${filename}'`) | ||
87 | .then(rows => rows[0]) | ||
88 | } | ||
89 | |||
85 | function selectQuery (internalServerNumber: number, query: string) { | 90 | function selectQuery (internalServerNumber: number, query: string) { |
86 | const seq = getSequelize(internalServerNumber) | 91 | const seq = getSequelize(internalServerNumber) |
87 | const options = { type: QueryTypes.SELECT as QueryTypes.SELECT } | 92 | const options = { type: QueryTypes.SELECT as QueryTypes.SELECT } |
@@ -146,6 +151,7 @@ export { | |||
146 | setPluginVersion, | 151 | setPluginVersion, |
147 | setPluginLatestVersion, | 152 | setPluginLatestVersion, |
148 | selectQuery, | 153 | selectQuery, |
154 | getActorImage, | ||
149 | deleteAll, | 155 | deleteAll, |
150 | setTokenField, | 156 | setTokenField, |
151 | updateQuery, | 157 | updateQuery, |