/** * Normalize an Instagram handle from various input formats to a pure username. * * Handles these formats: * - "https://www.instagram.com/banobagi_ps/" → "banobagi_ps" * - "https://instagram.com/banobagi_ps?hl=en" → "banobagi_ps" * - "http://instagram.com/banobagi_ps" → "banobagi_ps" * - "instagram.com/banobagi_ps" → "banobagi_ps" * - "@banobagi_ps" → "banobagi_ps" * - "banobagi_ps" → "banobagi_ps" * - null / undefined / "" → null */ export function normalizeInstagramHandle( raw: string | null | undefined, ): string | null { if (!raw || typeof raw !== "string") return null; let handle = raw.trim(); if (!handle) return null; // If it contains "instagram.com", extract the first path segment if (handle.includes("instagram.com")) { try { // Add protocol if missing so URL constructor works const urlStr = handle.startsWith("http") ? handle : `https://${handle}`; const url = new URL(urlStr); // pathname is like "/banobagi_ps/" or "/banobagi_ps" const segments = url.pathname.split("/").filter(Boolean); handle = segments[0] || ""; } catch { // URL parsing failed — try regex fallback const match = handle.match(/instagram\.com\/([^/?#]+)/); handle = match?.[1] || ""; } } // Strip leading @ if (handle.startsWith("@")) { handle = handle.slice(1); } // Strip trailing slash handle = handle.replace(/\/+$/, ""); return handle || null; } /** * Normalize a YouTube channel identifier from various URL formats. * * Returns an object with the best identifier type for API lookup: * - "https://www.youtube.com/@banobagips" → { type: 'handle', value: 'banobagips' } * - "https://youtube.com/c/banobagips" → { type: 'username', value: 'banobagips' } * - "https://youtube.com/user/banobagi" → { type: 'username', value: 'banobagi' } * - "https://youtube.com/channel/UCxxxx" → { type: 'channelId', value: 'UCxxxx' } * - "@banobagips" → { type: 'handle', value: 'banobagips' } * - "UCxxxx" → { type: 'channelId', value: 'UCxxxx' } * - "banobagips" → { type: 'username', value: 'banobagips' } */ export function normalizeYouTubeChannel( raw: string | null | undefined, ): { type: 'handle' | 'username' | 'channelId'; value: string } | null { if (!raw || typeof raw !== "string") return null; let input = raw.trim(); if (!input) return null; // Parse YouTube URLs if (input.includes("youtube.com") || input.includes("youtu.be")) { try { const urlStr = input.startsWith("http") ? input : `https://${input}`; const url = new URL(urlStr); const segments = url.pathname.split("/").filter(Boolean); if (segments[0] === "channel" && segments[1]?.startsWith("UC")) { return { type: "channelId", value: segments[1] }; } if (segments[0] === "c" && segments[1]) { return { type: "username", value: segments[1] }; } if (segments[0] === "user" && segments[1]) { return { type: "username", value: segments[1] }; } if (segments[0]?.startsWith("@")) { return { type: "handle", value: segments[0].slice(1) }; } // Fallback: first path segment if (segments[0]) { return { type: "username", value: segments[0] }; } } catch { // URL parsing failed } } // Non-URL formats if (input.startsWith("@")) { return { type: "handle", value: input.slice(1) }; } if (input.startsWith("UC") && input.length > 20) { return { type: "channelId", value: input }; } return { type: "username", value: input }; }