/** Extract a readable message from googleapis / Gaxios errors. */ export function extractYouTubeErrorMessage(err: unknown): string { if (!err || typeof err !== "object") { return typeof err === "string" ? err : "Unknown YouTube error"; } const anyErr = err as { message?: string; errors?: Array<{ message?: string; reason?: string }>; response?: { data?: { error?: { message?: string; errors?: Array<{ message?: string; reason?: string }>; }; }; }; }; const nested = anyErr.response?.data?.error?.errors?.[0]?.message || anyErr.response?.data?.error?.message || anyErr.errors?.[0]?.message; if (nested?.trim()) return nested.trim(); if (anyErr.message?.trim()) return anyErr.message.trim(); return "Unknown YouTube error"; } export function isYouTubeUploadLimitError(message: string) { const lower = message.toLowerCase(); return ( lower.includes("exceeded the number of videos") || lower.includes("uploadLimitExceeded") || lower.includes("upload limit") || (lower.includes("quota") && lower.includes("exceeded") && lower.includes("youtube")) ); } export const YOUTUBE_UPLOAD_LIMIT_USER_MESSAGE = "YouTube upload limit reached: this Google/YouTube account has exceeded the number of videos " + "it may upload right now. This is YouTube's own daily limit, not your Songs2YT plan quota. " + "Try again later (often after 24 hours) or use a different YouTube channel."; /** User-facing message for the dashboard (distinct from Songs2YT plan quota). */ export function formatYouTubeErrorForUser(err: unknown): string { const raw = extractYouTubeErrorMessage(err); if (isYouTubeUploadLimitError(raw)) return YOUTUBE_UPLOAD_LIMIT_USER_MESSAGE; return raw; } /** Normalize a stored job-item error string for display in the UI. */ export function displayJobItemError(message: string | null | undefined): string | null { if (!message?.trim()) return null; if (isYouTubeUploadLimitError(message)) return YOUTUBE_UPLOAD_LIMIT_USER_MESSAGE; return message.trim(); }