/** * Normalizes any media/upload URL to use the local frontend Next.js media proxy (/api/uploads/...) * instead of directly exposing or calling backend domains (api.canina.ir, etc.). * * Benefits: * 1. Leverages Next.js server caching and optimization. * 2. Hides internal backend URLs and avoids ugly direct backend domain calls. * 3. Supports HTTP Range requests for seamless video/audio seeking and streaming. */ export function getMediaUrl(url?: string | null): string { if (!url) return ''; const trimmed = url.trim(); if (!trimmed) return ''; // Data URLs or blob URLs stay as-is if (trimmed.startsWith('data:') || trimmed.startsWith('blob:')) { return trimmed; } // Extract the clean upload path let uploadPath = ''; if (trimmed.startsWith('/api/uploads/')) { uploadPath = trimmed.replace(/^\/api\/uploads\//, ''); } else if (trimmed.startsWith('/api/media/')) { uploadPath = trimmed.replace(/^\/api\/media\//, ''); } else if (trimmed.startsWith('/uploads/')) { uploadPath = trimmed.replace(/^\/uploads\//, ''); } else if (trimmed.startsWith('uploads/')) { uploadPath = trimmed.replace(/^uploads\//, ''); } else if (trimmed.startsWith('/media/')) { uploadPath = trimmed.replace(/^\/media\//, ''); } else if (trimmed.startsWith('media/')) { uploadPath = trimmed.replace(/^media\//, ''); } else { const uploadsMatch = trimmed.match(/^https?:\/\/[^/]+\/uploads\/(.+)$/i); if (uploadsMatch && uploadsMatch[1]) { uploadPath = uploadsMatch[1]; } } if (uploadPath) { const isStage = typeof window !== 'undefined' && window.location.hostname.includes('stage'); const isDev = process.env.NODE_ENV === 'development'; const backendBase = process.env.NEXT_PUBLIC_API_URL?.replace(/\/api\/?$/, '') || (isDev ? 'http://localhost:4400' : isStage ? 'https://stageapi.canina.ir' : 'https://api.canina.ir'); return `${backendBase}/uploads/${uploadPath}`; } // External URLs (like unsplash, cdn, youtube, aparat) stay as-is return trimmed; }