From cc78f9850d390c32f0003121a36cfdd41f4495ad Mon Sep 17 00:00:00 2001 From: Paul Friederichsen Date: Wed, 27 Sep 2023 04:40:24 -0500 Subject: [PATCH 1/2] Fix scaling and limits for Mastodon and Pleroma/Akkoma - The max width and height are now calculated based on the maximum number of pixels provided by the instance. - max post length is now set by the max characters setting from the instance. - Adds support for a max filesize and max post length from Pleroma and Akkoma instances. - The chunk size is now determined by the max media attachments setting from Mastodon and Pleroma instances - Updates the accepted filetypes based on what standard Mastodon supports. Ideally this would be from the list of mime types provided by the instance though. --- .../websites/mastodon/mastodon.service.ts | 90 ++++++++++++------- 1 file changed, 59 insertions(+), 31 deletions(-) diff --git a/electron-app/src/server/websites/mastodon/mastodon.service.ts b/electron-app/src/server/websites/mastodon/mastodon.service.ts index 55f52516..99ad2e99 100644 --- a/electron-app/src/server/websites/mastodon/mastodon.service.ts +++ b/electron-app/src/server/websites/mastodon/mastodon.service.ts @@ -38,16 +38,21 @@ const INFO_KEY = 'INSTANCE INFO'; type MastodonInstanceInfo = { configuration: { - statuses: { + statuses?: { max_characters: number; max_media_attachments: number; }; - media_attachments: { + media_attachments?: { supported_mime_types: string[]; image_size_limit: number; video_size_limit: number; + image_matrix_limit: number; + video_matrix_limit: number; }; }; + upload_limit?: number; // Pleroma, Akkoma + max_toot_chars?: number; // Pleroma, Akkoma + max_media_attachments?: number; //Pleroma }; @Injectable() @@ -64,13 +69,26 @@ export class Mastodon extends Website { 'jpeg', 'jpg', 'gif', - 'swf', - 'flv', + 'webp', + 'avif', + 'heic', + 'heif', 'mp4', + 'webm', + 'm4v', + 'mov', 'doc', 'rtf', 'txt', 'mp3', + 'wav', + 'ogg', + 'oga', + 'opus', + 'aac', + 'm4a', + '3gp', + 'wma', ]; async checkLoginStatus(data: UserAccountEntity): Promise { @@ -94,20 +112,26 @@ export class Mastodon extends Website { getScalingOptions(file: FileRecord, accountId: string): ScalingOptions { const instanceInfo: MastodonInstanceInfo = this.getAccountInfo(accountId, INFO_KEY); - return instanceInfo?.configuration?.media_attachments - ? { - maxHeight: 4000, - maxWidth: 4000, - maxSize: - file.type === FileSubmissionType.IMAGE - ? instanceInfo.configuration.media_attachments.image_size_limit - : instanceInfo.configuration.media_attachments.video_size_limit, - } - : { - maxHeight: 4000, - maxWidth: 4000, - maxSize: FileSize.MBtoBytes(300), - }; + if(instanceInfo?.configuration?.media_attachments) { + const maxPixels = file.type === FileSubmissionType.IMAGE + ? instanceInfo.configuration.media_attachments.image_matrix_limit + : instanceInfo.configuration.media_attachments.video_matrix_limit; + + return { + maxHeight: Math.round(Math.sqrt(maxPixels * (file.width / file.height))), + maxWidth: Math.round(Math.sqrt(maxPixels * (file.height / file.width))), + maxSize: + file.type === FileSubmissionType.IMAGE + ? instanceInfo.configuration.media_attachments.image_size_limit + : instanceInfo.configuration.media_attachments.video_size_limit, + } + } else if (instanceInfo?.upload_limit) { + return { + maxSize: instanceInfo?.upload_limit, + } + } else { + return undefined; + } } // Megaladon api has uploadMedia method, hovewer, it does not work with mastodon @@ -183,8 +207,11 @@ export class Mastodon extends Website { } const instanceInfo: MastodonInstanceInfo = this.getAccountInfo(data.part.accountId, INFO_KEY); - const chunkCount = instanceInfo?.configuration?.statuses?.max_media_attachments ?? 4; - const maxChars = instanceInfo?.configuration?.statuses?.max_characters ?? 500; + const chunkCount = + instanceInfo?.configuration?.statuses?.max_media_attachments ?? + instanceInfo?.max_media_attachments ?? + (instanceInfo?.upload_limit ? 1000 : 4); + const maxChars = instanceInfo?.configuration?.statuses?.max_characters ?? instanceInfo?.max_toot_chars ?? 500; const isSensitive = data.rating !== SubmissionRating.GENERAL; const chunks = _.chunk(uploadedMedias, chunkCount); @@ -297,7 +324,7 @@ export class Mastodon extends Website { if (description.length > maxChars) { warnings.push( - `Max description length allowed is ${maxChars} characters (for this Mastodon client).`, + `Max description length allowed is ${maxChars} characters (for this instance).`, ); } @@ -308,8 +335,6 @@ export class Mastodon extends Website { ), ]; - const maxImageSize = - instanceInfo?.configuration?.media_attachments?.image_size_limit ?? FileSize.MBtoBytes(50); files.forEach(file => { const { type, size, name, mimetype } = file; @@ -317,26 +342,29 @@ export class Mastodon extends Website { problems.push(`Does not support file format: (${name}) ${mimetype}.`); } - if (maxImageSize < size) { + const scalingOptions = this.getScalingOptions(file, submissionPart.accountId); + + if (scalingOptions && scalingOptions.maxSize < size) { if ( isAutoscaling && type === FileSubmissionType.IMAGE && ImageManipulator.isMimeType(mimetype) ) { - warnings.push(`${name} will be scaled down to ${FileSize.BytesToMB(maxImageSize)}MB`); + warnings.push(`${name} will be scaled down to ${FileSize.BytesToMB(scalingOptions.maxSize)}MB`); } else { - problems.push(`Mastodon limits ${mimetype} to ${FileSize.BytesToMB(maxImageSize)}MB`); + problems.push(`This instance limits ${mimetype} to ${FileSize.BytesToMB(scalingOptions.maxSize)}MB`); } } - // Check the image dimensions are not over 4000 x 4000 - this is the Mastodon server max if ( + scalingOptions && isAutoscaling && type === FileSubmissionType.IMAGE && - (file.height > 4000 || file.width > 4000) + scalingOptions.maxWidth && scalingOptions.maxHeight && + (file.height > scalingOptions.maxHeight || file.width > scalingOptions.maxWidth) ) { warnings.push( - `${name} will be scaled down to a maximum size of 4000x4000, while maintaining aspect ratio`, + `${name} will be scaled down to a maximum size of ${scalingOptions.maxWidth}x${scalingOptions.maxHeight}, while maintaining aspect ratio`, ); } }); @@ -367,10 +395,10 @@ export class Mastodon extends Website { submissionPart.accountId, INFO_KEY, ); - const maxChars = instanceInfo?.configuration?.statuses?.max_characters ?? 500; + const maxChars = instanceInfo?.configuration?.statuses?.max_characters ?? instanceInfo?.max_toot_chars ?? 500; if (description.length > maxChars) { warnings.push( - `Max description length allowed is ${maxChars} characters (for this Mastodon client).`, + `Max description length allowed is ${maxChars} characters (for this instance).`, ); } From 8fdd38b0e6d839d746472753ecda94ea2770dd61 Mon Sep 17 00:00:00 2001 From: Paul Friederichsen Date: Wed, 27 Sep 2023 14:27:55 -0500 Subject: [PATCH 2/2] Fix formatting --- .../websites/mastodon/mastodon.service.ts | 41 +++++++++++-------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/electron-app/src/server/websites/mastodon/mastodon.service.ts b/electron-app/src/server/websites/mastodon/mastodon.service.ts index 99ad2e99..1a95d7b0 100644 --- a/electron-app/src/server/websites/mastodon/mastodon.service.ts +++ b/electron-app/src/server/websites/mastodon/mastodon.service.ts @@ -112,11 +112,12 @@ export class Mastodon extends Website { getScalingOptions(file: FileRecord, accountId: string): ScalingOptions { const instanceInfo: MastodonInstanceInfo = this.getAccountInfo(accountId, INFO_KEY); - if(instanceInfo?.configuration?.media_attachments) { - const maxPixels = file.type === FileSubmissionType.IMAGE - ? instanceInfo.configuration.media_attachments.image_matrix_limit - : instanceInfo.configuration.media_attachments.video_matrix_limit; - + if (instanceInfo?.configuration?.media_attachments) { + const maxPixels = + file.type === FileSubmissionType.IMAGE + ? instanceInfo.configuration.media_attachments.image_matrix_limit + : instanceInfo.configuration.media_attachments.video_matrix_limit; + return { maxHeight: Math.round(Math.sqrt(maxPixels * (file.width / file.height))), maxWidth: Math.round(Math.sqrt(maxPixels * (file.height / file.width))), @@ -124,12 +125,12 @@ export class Mastodon extends Website { file.type === FileSubmissionType.IMAGE ? instanceInfo.configuration.media_attachments.image_size_limit : instanceInfo.configuration.media_attachments.video_size_limit, - } + }; } else if (instanceInfo?.upload_limit) { return { maxSize: instanceInfo?.upload_limit, - } - } else { + }; + } else { return undefined; } } @@ -207,11 +208,12 @@ export class Mastodon extends Website { } const instanceInfo: MastodonInstanceInfo = this.getAccountInfo(data.part.accountId, INFO_KEY); - const chunkCount = - instanceInfo?.configuration?.statuses?.max_media_attachments ?? - instanceInfo?.max_media_attachments ?? + const chunkCount = + instanceInfo?.configuration?.statuses?.max_media_attachments ?? + instanceInfo?.max_media_attachments ?? (instanceInfo?.upload_limit ? 1000 : 4); - const maxChars = instanceInfo?.configuration?.statuses?.max_characters ?? instanceInfo?.max_toot_chars ?? 500; + const maxChars = + instanceInfo?.configuration?.statuses?.max_characters ?? instanceInfo?.max_toot_chars ?? 500; const isSensitive = data.rating !== SubmissionRating.GENERAL; const chunks = _.chunk(uploadedMedias, chunkCount); @@ -335,7 +337,6 @@ export class Mastodon extends Website { ), ]; - files.forEach(file => { const { type, size, name, mimetype } = file; if (!WebsiteValidator.supportsFileType(file, this.acceptsFiles)) { @@ -350,9 +351,13 @@ export class Mastodon extends Website { type === FileSubmissionType.IMAGE && ImageManipulator.isMimeType(mimetype) ) { - warnings.push(`${name} will be scaled down to ${FileSize.BytesToMB(scalingOptions.maxSize)}MB`); + warnings.push( + `${name} will be scaled down to ${FileSize.BytesToMB(scalingOptions.maxSize)}MB`, + ); } else { - problems.push(`This instance limits ${mimetype} to ${FileSize.BytesToMB(scalingOptions.maxSize)}MB`); + problems.push( + `This instance limits ${mimetype} to ${FileSize.BytesToMB(scalingOptions.maxSize)}MB`, + ); } } @@ -360,7 +365,8 @@ export class Mastodon extends Website { scalingOptions && isAutoscaling && type === FileSubmissionType.IMAGE && - scalingOptions.maxWidth && scalingOptions.maxHeight && + scalingOptions.maxWidth && + scalingOptions.maxHeight && (file.height > scalingOptions.maxHeight || file.width > scalingOptions.maxWidth) ) { warnings.push( @@ -395,7 +401,8 @@ export class Mastodon extends Website { submissionPart.accountId, INFO_KEY, ); - const maxChars = instanceInfo?.configuration?.statuses?.max_characters ?? instanceInfo?.max_toot_chars ?? 500; + const maxChars = + instanceInfo?.configuration?.statuses?.max_characters ?? instanceInfo?.max_toot_chars ?? 500; if (description.length > maxChars) { warnings.push( `Max description length allowed is ${maxChars} characters (for this instance).`,