export function normalizeCanvasDurationSeconds(value: unknown) { return typeof value === 'number' && Number.isFinite(value) && value > 0 ? value : null; } export function formatCanvasDurationLabel(value: unknown) { const durationSeconds = normalizeCanvasDurationSeconds(value); if (durationSeconds === null) { return '--:--'; } const totalSeconds = Math.max(1, Math.round(durationSeconds)); const hours = Math.floor(totalSeconds / 3600); const minutes = Math.floor((totalSeconds % 3600) / 60); const seconds = totalSeconds % 60; const paddedSeconds = String(seconds).padStart(2, '0'); if (hours > 0) { return `${hours}:${String(minutes).padStart(2, '0')}:${paddedSeconds}`; } return `${minutes}:${paddedSeconds}`; } export function formatCanvasDurationMetric(value: unknown) { // 中文注释:音频对象没有视觉分辨率语义,画布角标统一显示时长。 return `时长 ${formatCanvasDurationLabel(value)}`; } type MediaPreviewTone = 'audio' | 'video'; type MediaPreviewMarker = { tone: MediaPreviewTone; variant: number; }; const MEDIA_PREVIEW_VARIANT_COUNT = 6; function hashMediaPreviewSeed(seed: string) { let hash = 0; for (let index = 0; index < seed.length; index += 1) { hash = (hash * 31 + seed.charCodeAt(index)) >>> 0; } return hash; } export function getCanvasMediaPreviewMarker({ mediaType, assetKind, title, src, }: { mediaType?: string | null; assetKind?: string | null; title?: string | null; src?: string | null; }): MediaPreviewMarker | null { if (mediaType !== 'audio' && mediaType !== 'video') { return null; } const seed = `${assetKind ?? ''}|${title ?? ''}|${src ?? ''}`; return { tone: mediaType, variant: hashMediaPreviewSeed(seed) % MEDIA_PREVIEW_VARIANT_COUNT, }; } export function getCanvasMediaPreviewClassName( marker: MediaPreviewMarker | null, ) { if (!marker) { return ''; } return [ 'image-canvas-editor__media-preview', `image-canvas-editor__media-preview--${marker.tone}`, `image-canvas-editor__media-preview--variant-${marker.variant}`, ].join(' '); }