Something went wrong. Try again.
A Bsky-like frontend using the atprotocol natively.
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
import { getPdsUrlForDid, resolvePdsForDids, setPdsForDid } from './pdsResolver';import { getImageProxySettings } from '../settings/storage';
let activeProxyUrl: string | null = null;
export function setActiveProxyUrl(url: string | null): void { activeProxyUrl = url;}
export function getActiveProxyUrl(): string | null { return activeProxyUrl;}
const CDN_IMG_RE = /^https:\/\/cdn\.bsky\.app\/img\/[^/]+\/plain\/(did:[^/]+)\/([a-zA-Z0-9]+)$/;
const CDN_VIDEO_THUMB_RE = /^https:\/\/video\.cdn\.bsky\.app\/hls\/(did:[^/]+)\/([a-zA-Z0-9]+)\/thumbnail\.jpg$/;
const CDN_VIDEO_WATCH_THUMB_RE = /^https:\/\/video\.bsky\.app\/watch\/(did:[^/]+)\/([a-zA-Z0-9]+)\/thumbnail\.jpg$/;
export function parseCdnUrl(url: string): { did: string; cid: string } | null { let decoded: string; try { decoded = decodeURIComponent(url); } catch { decoded = url; }
let m = CDN_IMG_RE.exec(decoded); if (m) return { did: m[1], cid: m[2] };
m = CDN_VIDEO_THUMB_RE.exec(decoded); if (m) return { did: m[1], cid: m[2] };
m = CDN_VIDEO_WATCH_THUMB_RE.exec(decoded); if (m) return { did: m[1], cid: m[2] };
return null;}
let fallbackPdsUrl: string | null = null;
export function setBlobRewritePdsUrl(pdsUrl: string): void { fallbackPdsUrl = pdsUrl.replace(/\/$/, '');}
export function getBlobRewritePdsUrl(): string | null { return fallbackPdsUrl;}
export function initBlobRewrite(userDid: string, pdsUrl: string): void { const normalized = pdsUrl.replace(/\/$/, ''); fallbackPdsUrl = normalized; setPdsForDid(userDid, normalized);}
function collectDidsFromObject(data: unknown, seen = new Set<string>()): Set<string> { if (typeof data === 'string') { const parsed = parseCdnUrl(data); if (parsed) seen.add(parsed.did); return seen; }
if (!data || typeof data !== 'object') return seen;
if (Array.isArray(data)) { for (const item of data) collectDidsFromObject(item, seen); return seen; }
for (const val of Object.values(data as Record<string, unknown>)) { collectDidsFromObject(val, seen); } return seen;}
export function rewriteBlobUrl(url: string | undefined | null): string | undefined { if (!url) return undefined;
const parsed = parseCdnUrl(url); if (!parsed) return url;
const settings = getImageProxySettings(); if (settings.mode !== 'disabled' && activeProxyUrl) { if (activeProxyUrl === 'cdn.bsky.app' || activeProxyUrl === 'https://cdn.bsky.app') { return url; }
const isThumb = url.includes('/plain/') || url.includes('/thumbnail.jpg'); if (settings.mode === 'all' || (settings.mode === 'thumbnails' && isThumb)) { const original = new URL(url); return `${activeProxyUrl.replace(/\/$/, '')}${original.pathname}`; } }
const pds = getPdsUrlForDid(parsed.did); if (!pds) return url;
return `${pds}/xrpc/com.atproto.sync.getBlob?did=${encodeURIComponent(parsed.did)}&cid=${encodeURIComponent(parsed.cid)}`;}
export async function rewriteBlobUrls<T>(data: T): Promise<T> { if (!data || typeof data !== 'object') return data;
const dids = collectDidsFromObject(data); if (dids.size > 0) { await resolvePdsForDids([...dids]).catch(() => {}); }
if (typeof data === 'string') { return (rewriteBlobUrl(data) ?? data) as T; }
if (Array.isArray(data)) { for (let i = 0; i < data.length; i++) { rewriteBlobUrlsInPlace(data, i); } return data; }
const obj = data as Record<string, unknown>; for (const key of Object.keys(obj)) { rewriteBlobUrlsInPlace(obj, key); } return data;}
function rewriteBlobUrlsInPlace(container: object, key: string | number): void { const obj = container as Record<string | number, unknown>; const val = obj[key];
if (typeof val === 'string') { const rewritten = rewriteBlobUrl(val); if (rewritten !== val) { obj[key] = rewritten; } } else if (val && typeof val === 'object') { rewriteBlobUrls(val); }}