signal-desktop/ts/util/getStoryDuration.ts
2022-11-28 17:35:15 -08:00

80 lines
2.2 KiB
TypeScript

// Copyright 2022 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type { AttachmentType } from '../types/Attachment';
import {
hasFailed,
hasNotResolved,
isDownloaded,
isGIF,
isVideo,
} from '../types/Attachment';
import { count } from './grapheme';
import { SECOND } from './durations';
const DEFAULT_DURATION = 5 * SECOND;
const MAX_VIDEO_DURATION = 30 * SECOND;
export async function getStoryDuration(
attachment: AttachmentType
): Promise<number | undefined> {
if (hasFailed(attachment)) {
return DEFAULT_DURATION;
}
if (attachment.textAttachment) {
// Minimum 5 seconds. +1 second for every 15 characters past the first
// 15 characters (round up).
// For text stories that include a link, +2 seconds to the playback time.
const length = attachment.textAttachment.text
? count(attachment.textAttachment.text)
: 0;
const additionalSeconds = (Math.ceil(length / 15) - 1) * SECOND;
const linkPreviewSeconds = attachment.textAttachment.preview
? 2 * SECOND
: 0;
return DEFAULT_DURATION + additionalSeconds + linkPreviewSeconds;
}
if (!isDownloaded(attachment) || hasNotResolved(attachment)) {
return;
}
if (isGIF([attachment]) || isVideo([attachment])) {
const videoEl = document.createElement('video');
if (!attachment.url) {
return DEFAULT_DURATION;
}
videoEl.src = attachment.url;
await new Promise<void>(resolve => {
function resolveAndRemove() {
resolve();
videoEl.removeEventListener('loadedmetadata', resolveAndRemove);
}
videoEl.addEventListener('loadedmetadata', resolveAndRemove);
});
const duration = Math.ceil(videoEl.duration * SECOND);
if (isGIF([attachment])) {
// GIFs: Loop gifs 3 times or play for 5 seconds, whichever is longer.
return Math.min(
Math.max(duration * 3, DEFAULT_DURATION),
MAX_VIDEO_DURATION
);
}
// Video max duration: 30 seconds
return Math.min(duration, MAX_VIDEO_DURATION);
}
if (attachment.caption) {
const length = count(attachment.caption);
const additionalSeconds = (Math.ceil(length / 15) - 1) * SECOND;
return DEFAULT_DURATION + additionalSeconds;
}
return DEFAULT_DURATION;
}