export function extractDataUriMime(uri: string): string { return uri.substring(uri.indexOf(':') + 1, uri.indexOf(';')) } export function getResizedDimensions( originalDims: { width: number height: number }, maxDimension: number, ) { if ( originalDims.width <= maxDimension && originalDims.height <= maxDimension ) { return originalDims } const ratio = Math.min( maxDimension / originalDims.width, maxDimension / originalDims.height, ) return { width: Math.round(originalDims.width * ratio), height: Math.round(originalDims.height * ratio), } } // Fairly accurate estimate that is more performant // than decoding and checking length of URI export function getDataUriSize(uri: string): number { return Math.round((uri.length * 3) / 4) } export function isUriImage(uri: string): boolean { return /\.(jpg|jpeg|png|webp).*$/.test(uri) } export function blobToDataUri(blob: Blob): Promise { return new Promise((resolve, reject) => { const reader = new FileReader() reader.onloadend = () => { if (typeof reader.result === 'string') { resolve(reader.result) } else { reject(new Error('Failed to read blob')) } } reader.onerror = reject reader.readAsDataURL(blob) }) } export type ImgproxyPreset = | 'default' | 'avatar_thumbnail' | 'avatar' | 'banner' | 'feed_fullsize' | 'feed_thumbnail' | 'download' // Using capturing groups here instead of lookbehinds in order to support older versions of Safari. // https://bugs.webkit.org/show_bug.cgi?id=174931 const IMGPROXY_PRESET_RE = /(\/img\/)(default|avatar_thumbnail|avatar|banner|feed_fullsize|feed_thumbnail|download)(\/)/ /** * Replaces any imgproxy preset in a CDN URI with the given preset. */ export function convertCdnPreset(uri: string, preset: ImgproxyPreset): string { return uri.replace(IMGPROXY_PRESET_RE, `$1${preset}$3`) } /** Preserve non-CDN URLs, including blobs and already-resolved originals. */ export function modifyImageFormat(uri: string, format: string) { let url: URL try { url = new URL(uri) } catch { return uri } const match = url.pathname.match( /^\/img\/[^/]+\/plain\/(did:[^/]+)\/([^/@]+)(?:@[^/]+)?$/, ) if (!match) return uri if (format === 'original') { const original = new URL( 'https://bsky.social/xrpc/com.atproto.sync.getBlob', ) original.searchParams.set('did', decodeURIComponent(match[1])) original.searchParams.set('cid', match[2]) return original.toString() } url.pathname = url.pathname.replace(/(?:@[^/]*)?$/, `@${format}`) return url.toString() } export function getDownloadImageUri(uri: string, format: string) { return modifyImageFormat(convertCdnPreset(uri, 'download'), format) }