Limit unnecessary thumbnail generation
This commit is contained in:
parent
9c97d3e73c
commit
74e327a6c4
14 changed files with 132 additions and 81 deletions
|
@ -7,7 +7,7 @@ import * as durations from '../util/durations/index.js';
|
|||
import { createLogger } from '../logging/log.js';
|
||||
import type { AttachmentBackfillResponseSyncEvent } from '../textsecure/messageReceiverEvents.js';
|
||||
import {
|
||||
type AttachmentDownloadJobTypeType,
|
||||
type MessageAttachmentType,
|
||||
type AttachmentDownloadJobType,
|
||||
type CoreAttachmentDownloadJobType,
|
||||
AttachmentDownloadUrgency,
|
||||
|
@ -82,7 +82,7 @@ export { isPermanentlyUndownloadable };
|
|||
// Type for adding a new job
|
||||
export type NewAttachmentDownloadJobType = {
|
||||
attachment: AttachmentType;
|
||||
attachmentType: AttachmentDownloadJobTypeType;
|
||||
attachmentType: MessageAttachmentType;
|
||||
isManualDownload: boolean;
|
||||
messageId: string;
|
||||
receivedAt: number;
|
||||
|
@ -808,10 +808,13 @@ export async function runDownloadAttachmentJobInner({
|
|||
},
|
||||
});
|
||||
|
||||
const upgradedAttachment = await dependencies.processNewAttachment({
|
||||
...omit(attachment, ['error', 'pending']),
|
||||
...downloadedAttachment,
|
||||
});
|
||||
const upgradedAttachment = await dependencies.processNewAttachment(
|
||||
{
|
||||
...omit(attachment, ['error', 'pending']),
|
||||
...downloadedAttachment,
|
||||
},
|
||||
attachmentType
|
||||
);
|
||||
|
||||
const isShowingLightbox = (): boolean => {
|
||||
const lightboxState = window.reduxStore.getState().lightbox;
|
||||
|
|
|
@ -13,7 +13,7 @@ import {
|
|||
getUndownloadedAttachmentSignature,
|
||||
} from '../../types/Attachment.js';
|
||||
import {
|
||||
type AttachmentDownloadJobTypeType,
|
||||
type MessageAttachmentType,
|
||||
AttachmentDownloadUrgency,
|
||||
} from '../../types/AttachmentDownload.js';
|
||||
import { AttachmentDownloadSource } from '../../sql/Interface.js';
|
||||
|
@ -353,7 +353,7 @@ export class AttachmentBackfill {
|
|||
}
|
||||
|
||||
public static isEnabledForJob(
|
||||
jobType: AttachmentDownloadJobTypeType,
|
||||
jobType: MessageAttachmentType,
|
||||
message: Pick<ReadonlyMessageAttributesType, 'type'>
|
||||
): boolean {
|
||||
if (message.type === 'story') {
|
||||
|
@ -456,7 +456,7 @@ export class AttachmentBackfill {
|
|||
|
||||
export function isPermanentlyUndownloadable(
|
||||
attachment: AttachmentType,
|
||||
disposition: AttachmentDownloadJobTypeType,
|
||||
disposition: MessageAttachmentType,
|
||||
message: Pick<ReadonlyMessageAttributesType, 'type'>
|
||||
): boolean {
|
||||
// Attachment is downloadable or user have not failed to download it yet
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
import lodash from 'lodash';
|
||||
import { createLogger } from '../logging/log.js';
|
||||
import * as Bytes from '../Bytes.js';
|
||||
import type { AttachmentDownloadJobTypeType } from '../types/AttachmentDownload.js';
|
||||
import type { MessageAttachmentType } from '../types/AttachmentDownload.js';
|
||||
|
||||
import type { AttachmentType } from '../types/Attachment.js';
|
||||
import {
|
||||
|
@ -69,7 +69,7 @@ export async function addAttachmentToMessage(
|
|||
messageId: string,
|
||||
attachment: AttachmentType,
|
||||
jobLogId: string,
|
||||
{ type }: { type: AttachmentDownloadJobTypeType }
|
||||
{ type }: { type: MessageAttachmentType }
|
||||
): Promise<void> {
|
||||
const logPrefix = `${jobLogId}/addAttachmentToMessage`;
|
||||
const message = await getMessageById(messageId);
|
||||
|
|
|
@ -245,10 +245,13 @@ export class ReleaseNotesFetcher {
|
|||
);
|
||||
|
||||
const processedAttachment =
|
||||
await window.Signal.Migrations.processNewAttachment({
|
||||
...localAttachment,
|
||||
contentType: stringToMIMEType(contentType),
|
||||
});
|
||||
await window.Signal.Migrations.processNewAttachment(
|
||||
{
|
||||
...localAttachment,
|
||||
contentType: stringToMIMEType(contentType),
|
||||
},
|
||||
'attachment'
|
||||
);
|
||||
|
||||
return { hydratedNote, processedAttachment };
|
||||
}
|
||||
|
|
13
ts/signal.ts
13
ts/signal.ts
|
@ -58,6 +58,7 @@ import type {
|
|||
} from './types/message/LinkPreviews.js';
|
||||
import type { StickerType, StickerWithHydratedData } from './types/Stickers.js';
|
||||
import { beforeNavigateService } from './services/BeforeNavigate.js';
|
||||
import type { MessageAttachmentType } from './types/AttachmentDownload.js';
|
||||
|
||||
type EncryptedReader = (
|
||||
attachment: Partial<AddressableAttachmentType>
|
||||
|
@ -122,7 +123,10 @@ type MigrationsModuleType = {
|
|||
name: string;
|
||||
baseDir?: string;
|
||||
}) => Promise<null | { fullPath: string; name: string }>;
|
||||
processNewAttachment: (attachment: AttachmentType) => Promise<AttachmentType>;
|
||||
processNewAttachment: (
|
||||
attachment: AttachmentType,
|
||||
attachmentType: MessageAttachmentType
|
||||
) => Promise<AttachmentType>;
|
||||
processNewSticker: (stickerData: Uint8Array) => Promise<
|
||||
LocalAttachmentV2Type & {
|
||||
width: number;
|
||||
|
@ -327,8 +331,11 @@ export function initializeMigrations({
|
|||
readStickerData,
|
||||
readTempData,
|
||||
saveAttachmentToDisk,
|
||||
processNewAttachment: (attachment: AttachmentType) =>
|
||||
MessageType.processNewAttachment(attachment, {
|
||||
processNewAttachment: (
|
||||
attachment: AttachmentType,
|
||||
attachmentType: MessageAttachmentType
|
||||
) =>
|
||||
MessageType.processNewAttachment(attachment, attachmentType, {
|
||||
writeNewAttachmentData,
|
||||
makeObjectUrl,
|
||||
revokeObjectUrl,
|
||||
|
|
|
@ -49,7 +49,7 @@ import type {
|
|||
} from '../types/CallLink.js';
|
||||
import type {
|
||||
AttachmentDownloadJobType,
|
||||
AttachmentDownloadJobTypeType,
|
||||
MessageAttachmentType,
|
||||
} from '../types/AttachmentDownload.js';
|
||||
import type {
|
||||
GroupSendEndorsementsData,
|
||||
|
@ -651,7 +651,7 @@ export const MESSAGE_ATTACHMENT_COLUMNS = [
|
|||
|
||||
export type MessageAttachmentDBType = {
|
||||
messageId: string;
|
||||
attachmentType: AttachmentDownloadJobTypeType;
|
||||
attachmentType: MessageAttachmentType;
|
||||
orderInMessage: number;
|
||||
editHistoryIndex: number | null;
|
||||
conversationId: string;
|
||||
|
|
|
@ -86,7 +86,7 @@ import {
|
|||
} from '../types/AttachmentBackup.js';
|
||||
import {
|
||||
attachmentDownloadJobSchema,
|
||||
type AttachmentDownloadJobTypeType,
|
||||
type MessageAttachmentType,
|
||||
type AttachmentDownloadJobType,
|
||||
} from '../types/AttachmentDownload.js';
|
||||
import type {
|
||||
|
@ -2700,7 +2700,7 @@ function saveMessageAttachment({
|
|||
sentAt: number;
|
||||
receivedAt: number;
|
||||
receivedAtMs: number | undefined;
|
||||
attachmentType: AttachmentDownloadJobTypeType;
|
||||
attachmentType: MessageAttachmentType;
|
||||
attachment: AttachmentType;
|
||||
orderInMessage: number;
|
||||
editHistoryIndex: number | null;
|
||||
|
|
|
@ -6,9 +6,9 @@ import * as z from 'zod';
|
|||
|
||||
import type { LoggerType } from '../../types/Logging.js';
|
||||
import {
|
||||
attachmentDownloadTypeSchema,
|
||||
messageAttachmentTypeSchema,
|
||||
type AttachmentDownloadJobType,
|
||||
type AttachmentDownloadJobTypeType,
|
||||
type MessageAttachmentType,
|
||||
} from '../../types/AttachmentDownload.js';
|
||||
import type { AttachmentType } from '../../types/Attachment.js';
|
||||
import { jsonToObject, objectToJSON, sql } from '../util.js';
|
||||
|
@ -28,7 +28,7 @@ export type _AttachmentDownloadJobTypeV1030 = {
|
|||
messageId: string;
|
||||
pending: number;
|
||||
timestamp: number;
|
||||
type: AttachmentDownloadJobTypeType;
|
||||
type: MessageAttachmentType;
|
||||
};
|
||||
|
||||
const attachmentDownloadJobSchemaV1040 = z
|
||||
|
@ -36,7 +36,7 @@ const attachmentDownloadJobSchemaV1040 = z
|
|||
attachment: z
|
||||
.object({ size: z.number(), contentType: MIMETypeSchema })
|
||||
.passthrough(),
|
||||
attachmentType: attachmentDownloadTypeSchema,
|
||||
attachmentType: messageAttachmentTypeSchema,
|
||||
ciphertextSize: z.number(),
|
||||
contentType: MIMETypeSchema,
|
||||
digest: z.string(),
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
import { z } from 'zod';
|
||||
import { convertUndefinedToNull } from '../../util/dropNull.js';
|
||||
import { attachmentDownloadTypeSchema } from '../../types/AttachmentDownload.js';
|
||||
import { messageAttachmentTypeSchema } from '../../types/AttachmentDownload.js';
|
||||
import { APPLICATION_OCTET_STREAM } from '../../types/MIME.js';
|
||||
import type { MessageAttachmentDBType } from '../Interface.js';
|
||||
|
||||
|
@ -35,7 +35,7 @@ export const permissiveMessageAttachmentSchema = z.object({
|
|||
messageId: z.string(),
|
||||
messageType: z.string(),
|
||||
editHistoryIndex: z.number(),
|
||||
attachmentType: attachmentDownloadTypeSchema,
|
||||
attachmentType: messageAttachmentTypeSchema,
|
||||
orderInMessage: z.number(),
|
||||
conversationId: z.string(),
|
||||
sentAt: z.number().catch(0),
|
||||
|
|
|
@ -71,7 +71,7 @@ import {
|
|||
isIncremental,
|
||||
defaultBlurHash,
|
||||
} from '../../types/Attachment.js';
|
||||
import type { AttachmentDownloadJobTypeType } from '../../types/AttachmentDownload.js';
|
||||
import type { MessageAttachmentType } from '../../types/AttachmentDownload.js';
|
||||
import { type DefaultConversationColorType } from '../../types/Colors.js';
|
||||
import { ReadStatus } from '../../messages/MessageReadStatus.js';
|
||||
|
||||
|
@ -1852,7 +1852,7 @@ export function getPropsForEmbeddedContact(
|
|||
|
||||
export function getPropsForAttachment(
|
||||
attachment: AttachmentType,
|
||||
disposition: AttachmentDownloadJobTypeType,
|
||||
disposition: MessageAttachmentType,
|
||||
message: Pick<ReadonlyMessageAttributesType, 'type'>
|
||||
): AttachmentForUIType {
|
||||
const { path, pending, screenshot, thumbnail, thumbnailFromBackup } =
|
||||
|
|
|
@ -37,6 +37,7 @@ import {
|
|||
} from './Crypto.js';
|
||||
import { missingCaseError } from '../util/missingCaseError.js';
|
||||
import type { MakeVideoScreenshotResultType } from './VisualAttachment.js';
|
||||
import type { MessageAttachmentType } from './AttachmentDownload.js';
|
||||
|
||||
const {
|
||||
isNumber,
|
||||
|
@ -482,6 +483,7 @@ const THUMBNAIL_CONTENT_TYPE = MIME.IMAGE_PNG;
|
|||
|
||||
export async function captureDimensionsAndScreenshot(
|
||||
attachment: AttachmentType,
|
||||
options: { generateThumbnail: boolean },
|
||||
params: {
|
||||
writeNewAttachmentData: (
|
||||
data: Uint8Array
|
||||
|
@ -544,28 +546,35 @@ export async function captureDimensionsAndScreenshot(
|
|||
objectUrl: localUrl,
|
||||
logger,
|
||||
});
|
||||
const thumbnailBuffer = await blobToArrayBuffer(
|
||||
await makeImageThumbnail({
|
||||
size: THUMBNAIL_SIZE,
|
||||
objectUrl: localUrl,
|
||||
contentType: THUMBNAIL_CONTENT_TYPE,
|
||||
logger,
|
||||
})
|
||||
);
|
||||
let thumbnail: LocalAttachmentV2Type | undefined;
|
||||
|
||||
if (options.generateThumbnail) {
|
||||
const thumbnailBuffer = await blobToArrayBuffer(
|
||||
await makeImageThumbnail({
|
||||
size: THUMBNAIL_SIZE,
|
||||
objectUrl: localUrl,
|
||||
contentType: THUMBNAIL_CONTENT_TYPE,
|
||||
logger,
|
||||
})
|
||||
);
|
||||
|
||||
thumbnail = await writeNewAttachmentData(
|
||||
new Uint8Array(thumbnailBuffer)
|
||||
);
|
||||
}
|
||||
|
||||
const thumbnail = await writeNewAttachmentData(
|
||||
new Uint8Array(thumbnailBuffer)
|
||||
);
|
||||
return {
|
||||
...attachment,
|
||||
width,
|
||||
height,
|
||||
thumbnail: {
|
||||
...thumbnail,
|
||||
contentType: THUMBNAIL_CONTENT_TYPE,
|
||||
width: THUMBNAIL_SIZE,
|
||||
height: THUMBNAIL_SIZE,
|
||||
},
|
||||
thumbnail: thumbnail
|
||||
? {
|
||||
...thumbnail,
|
||||
contentType: THUMBNAIL_CONTENT_TYPE,
|
||||
width: THUMBNAIL_SIZE,
|
||||
height: THUMBNAIL_SIZE,
|
||||
}
|
||||
: undefined,
|
||||
};
|
||||
} catch (error) {
|
||||
logger.error(
|
||||
|
@ -597,18 +606,19 @@ export async function captureDimensionsAndScreenshot(
|
|||
new Uint8Array(screenshotBuffer)
|
||||
);
|
||||
|
||||
const thumbnailBuffer = await blobToArrayBuffer(
|
||||
await makeImageThumbnail({
|
||||
size: THUMBNAIL_SIZE,
|
||||
objectUrl: screenshotObjectUrl,
|
||||
contentType: THUMBNAIL_CONTENT_TYPE,
|
||||
logger,
|
||||
})
|
||||
);
|
||||
let thumbnail: LocalAttachmentV2Type | undefined;
|
||||
if (options.generateThumbnail) {
|
||||
const thumbnailBuffer = await blobToArrayBuffer(
|
||||
await makeImageThumbnail({
|
||||
size: THUMBNAIL_SIZE,
|
||||
objectUrl: screenshotObjectUrl,
|
||||
contentType: THUMBNAIL_CONTENT_TYPE,
|
||||
logger,
|
||||
})
|
||||
);
|
||||
|
||||
const thumbnail = await writeNewAttachmentData(
|
||||
new Uint8Array(thumbnailBuffer)
|
||||
);
|
||||
thumbnail = await writeNewAttachmentData(new Uint8Array(thumbnailBuffer));
|
||||
}
|
||||
|
||||
return {
|
||||
...attachment,
|
||||
|
@ -619,12 +629,14 @@ export async function captureDimensionsAndScreenshot(
|
|||
width,
|
||||
height,
|
||||
},
|
||||
thumbnail: {
|
||||
...thumbnail,
|
||||
contentType: THUMBNAIL_CONTENT_TYPE,
|
||||
width: THUMBNAIL_SIZE,
|
||||
height: THUMBNAIL_SIZE,
|
||||
},
|
||||
thumbnail: thumbnail
|
||||
? {
|
||||
...thumbnail,
|
||||
contentType: THUMBNAIL_CONTENT_TYPE,
|
||||
width: THUMBNAIL_SIZE,
|
||||
height: THUMBNAIL_SIZE,
|
||||
}
|
||||
: undefined,
|
||||
width,
|
||||
height,
|
||||
};
|
||||
|
@ -1421,3 +1433,12 @@ export function partitionBodyAndNormalAttachments<
|
|||
attachments: normalAttachments,
|
||||
};
|
||||
}
|
||||
|
||||
const MESSAGE_ATTACHMENT_TYPES_NEEDING_THUMBNAILS: Set<MessageAttachmentType> =
|
||||
new Set(['attachment', 'sticker']);
|
||||
|
||||
export function shouldGenerateThumbnailForAttachmentType(
|
||||
type: MessageAttachmentType
|
||||
): boolean {
|
||||
return MESSAGE_ATTACHMENT_TYPES_NEEDING_THUMBNAILS.has(type);
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ export enum MediaTier {
|
|||
BACKUP = 'backup',
|
||||
}
|
||||
|
||||
export const attachmentDownloadTypeSchema = z.enum([
|
||||
export const messageAttachmentTypeSchema = z.enum([
|
||||
'long-message',
|
||||
'attachment',
|
||||
'preview',
|
||||
|
@ -23,13 +23,11 @@ export const attachmentDownloadTypeSchema = z.enum([
|
|||
'sticker',
|
||||
]);
|
||||
|
||||
export type AttachmentDownloadJobTypeType = z.infer<
|
||||
typeof attachmentDownloadTypeSchema
|
||||
>;
|
||||
export type MessageAttachmentType = z.infer<typeof messageAttachmentTypeSchema>;
|
||||
|
||||
export type CoreAttachmentDownloadJobType = {
|
||||
attachment: AttachmentType;
|
||||
attachmentType: AttachmentDownloadJobTypeType;
|
||||
attachmentType: MessageAttachmentType;
|
||||
ciphertextSize: number;
|
||||
contentType: MIMEType;
|
||||
attachmentSignature: string;
|
||||
|
@ -49,7 +47,7 @@ export const coreAttachmentDownloadJobSchema = z.object({
|
|||
attachment: z
|
||||
.object({ size: z.number(), contentType: MIMETypeSchema })
|
||||
.passthrough(),
|
||||
attachmentType: attachmentDownloadTypeSchema,
|
||||
attachmentType: messageAttachmentTypeSchema,
|
||||
ciphertextSize: z.number(),
|
||||
contentType: MIMETypeSchema,
|
||||
attachmentSignature: z.string(),
|
||||
|
|
|
@ -16,6 +16,7 @@ import {
|
|||
removeSchemaVersion,
|
||||
replaceUnicodeOrderOverrides,
|
||||
replaceUnicodeV2,
|
||||
shouldGenerateThumbnailForAttachmentType,
|
||||
} from './Attachment.js';
|
||||
import type { MakeVideoScreenshotResultType } from './VisualAttachment.js';
|
||||
import * as Errors from './errors.js';
|
||||
|
@ -48,6 +49,7 @@ import { encryptLegacyAttachment } from '../util/encryptLegacyAttachment.js';
|
|||
import { deepClone } from '../util/deepClone.js';
|
||||
import * as Bytes from '../Bytes.js';
|
||||
import { isBodyTooLong } from '../util/longAttachment.js';
|
||||
import type { MessageAttachmentType } from './AttachmentDownload.js';
|
||||
|
||||
const { isFunction, isObject, identity } = lodash;
|
||||
|
||||
|
@ -493,7 +495,13 @@ const toVersion7 = _withSchemaVersion({
|
|||
|
||||
const toVersion8 = _withSchemaVersion({
|
||||
schemaVersion: 8,
|
||||
upgrade: _mapAttachments(captureDimensionsAndScreenshot),
|
||||
upgrade: _mapAttachments((attachment, context) =>
|
||||
captureDimensionsAndScreenshot(
|
||||
attachment,
|
||||
{ generateThumbnail: true },
|
||||
context
|
||||
)
|
||||
),
|
||||
});
|
||||
|
||||
const toVersion9 = _withSchemaVersion({
|
||||
|
@ -768,6 +776,7 @@ export const upgradeSchema = async (
|
|||
// downloaded out of band.
|
||||
export const processNewAttachment = async (
|
||||
attachment: AttachmentType,
|
||||
attachmentType: MessageAttachmentType,
|
||||
{
|
||||
writeNewAttachmentData,
|
||||
makeObjectUrl,
|
||||
|
@ -810,15 +819,22 @@ export const processNewAttachment = async (
|
|||
throw new TypeError('context.logger is required');
|
||||
}
|
||||
|
||||
const finalAttachment = await captureDimensionsAndScreenshot(attachment, {
|
||||
writeNewAttachmentData,
|
||||
makeObjectUrl,
|
||||
revokeObjectUrl,
|
||||
getImageDimensions,
|
||||
makeImageThumbnail,
|
||||
makeVideoScreenshot,
|
||||
logger,
|
||||
});
|
||||
const finalAttachment = await captureDimensionsAndScreenshot(
|
||||
attachment,
|
||||
{
|
||||
generateThumbnail:
|
||||
shouldGenerateThumbnailForAttachmentType(attachmentType),
|
||||
},
|
||||
{
|
||||
writeNewAttachmentData,
|
||||
makeObjectUrl,
|
||||
revokeObjectUrl,
|
||||
getImageDimensions,
|
||||
makeImageThumbnail,
|
||||
makeVideoScreenshot,
|
||||
logger,
|
||||
}
|
||||
);
|
||||
|
||||
return finalAttachment;
|
||||
};
|
||||
|
|
|
@ -73,7 +73,10 @@ export async function downloadOnboardingStory(): Promise<void> {
|
|||
...local,
|
||||
};
|
||||
|
||||
return window.Signal.Migrations.processNewAttachment(attachment);
|
||||
return window.Signal.Migrations.processNewAttachment(
|
||||
attachment,
|
||||
'attachment'
|
||||
);
|
||||
})
|
||||
);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue