import { useRef, useState, useEffect, useCallback } from "react"; import { Music2, RotateCcw, RotateCw, Play, Pause, VolumeOff, Volume2 } from "lucide-react"; import api from "@/utils/api.util"; import { MediaFallback } from "@/components/generic/MediaFallback"; // ─── Helpers ────────────────────────────────────────────────────────────────── const fmtTime = (s) => { if (!s || isNaN(s)) return "0:00"; const m = Math.floor(s / 60); const sec = Math.floor(s % 60); return `${m}:${sec < 10 ? "0" : ""}${sec}`; }; const SPEEDS = [0.5, 0.75, 1, 1.25, 1.5, 2]; const API_BASE = (import.meta.env.VITE_API_URL ?? "http://localhost:3024").replace(/\/$/, ""); // How often onWatchProgress may fire while playing (ms) — keeps the watch-progress // endpoint from getting hit on every timeupdate tick. onEnded still always reports // a final 100% immediately regardless of this window, so completion never lags. const WATCH_PROGRESS_THROTTLE_MS = 10000; // ─── AudioBlock (Client — secure) ──────────────────────────────────────────── // // S3/Garage: // 1. POST /client/media/token → get JWT token // 2. fetch(streamUrl, { credentials: "include" }) → get raw bytes // 3. URL.createObjectURL(blob) → blob:http://... URL // 4.