"use client"; import React, { useState, useRef, useEffect, useCallback, useMemo } from "react"; import { Play, Pause, RotateCcw, RotateCw, Volume2, VolumeX, Download, Share2, Check, Headphones, Loader2, Sparkles, ChevronDown, ChevronUp } from "lucide-react"; import SafeImage from "./SafeImage"; interface PodcastInlinePlayerProps { podcast: { audioUrl?: string; title?: string; description?: string; cover?: string; productName?: string; fallbackCover?: string; }; } const PLAYBACK_RATES = [0.5, 0.75, 1, 1.25, 1.5, 1.75, 2, 2.5]; const WAVEFORM_BAR_COUNT = 44; export default function PodcastInlinePlayer({ podcast }: PodcastInlinePlayerProps) { const audioRef = useRef(null); const waveformRef = useRef(null); const rateMenuRef = useRef(null); const [isPlaying, setIsPlaying] = useState(false); const [currentTime, setCurrentTime] = useState(0); const [duration, setDuration] = useState(0); const [volume, setVolume] = useState(1); const [isMuted, setIsMuted] = useState(false); const [playbackRate, setPlaybackRate] = useState(1); const [showRateMenu, setShowRateMenu] = useState(false); const [isBuffering, setIsBuffering] = useState(false); const [isCopied, setIsCopied] = useState(false); const [isExpandedDesc, setIsExpandedDesc] = useState(false); // Close rate menu when clicking outside useEffect(() => { function handleClickOutside(e: MouseEvent) { if (rateMenuRef.current && !rateMenuRef.current.contains(e.target as Node)) { setShowRateMenu(false); } } if (showRateMenu) { document.addEventListener("mousedown", handleClickOutside); } return () => { document.removeEventListener("mousedown", handleClickOutside); }; }, [showRateMenu]); // Deterministic heights for waveform visualization const waveformHeights = useMemo(() => { const seed = (podcast?.title || "canina").split("").reduce((acc, char) => acc + char.charCodeAt(0), 0); return Array.from({ length: WAVEFORM_BAR_COUNT }).map((_, i) => { const pseudo = Math.sin((i + 1) * 0.45 + seed) * 0.5 + 0.5; return Math.max(22, Math.min(95, Math.floor(pseudo * 75 + 20))); }); }, [podcast?.title]); const togglePlay = useCallback(() => { if (!audioRef.current) return; if (audioRef.current.paused) { audioRef.current.play().catch(console.error); setIsPlaying(true); } else { audioRef.current.pause(); setIsPlaying(false); } }, []); const seekToPercent = (percent: number) => { if (audioRef.current && duration > 0) { const newTime = (percent / 100) * duration; audioRef.current.currentTime = Math.max(0, Math.min(duration, newTime)); setCurrentTime(newTime); } }; const handleWaveformClick = (e: React.MouseEvent) => { if (!waveformRef.current || duration <= 0) return; const rect = waveformRef.current.getBoundingClientRect(); const clickX = e.clientX - rect.left; const percent = Math.max(0, Math.min(100, (clickX / rect.width) * 100)); seekToPercent(percent); }; const skipTime = (seconds: number) => { if (audioRef.current) { audioRef.current.currentTime = Math.max(0, Math.min(audioRef.current.duration || 0, audioRef.current.currentTime + seconds)); } }; const handleRateChange = (rate: number) => { if (audioRef.current) { audioRef.current.playbackRate = rate; setPlaybackRate(rate); setShowRateMenu(false); } }; const toggleMute = () => { if (audioRef.current) { audioRef.current.muted = !isMuted; setIsMuted(!isMuted); } }; const handleVolumeChange = (e: React.ChangeEvent) => { const val = Number(e.target.value); setVolume(val); if (audioRef.current) { audioRef.current.volume = val; if (val === 0) { setIsMuted(true); audioRef.current.muted = true; } else { setIsMuted(false); audioRef.current.muted = false; } } }; const handleShare = async () => { const shareUrl = typeof window !== 'undefined' ? window.location.href : ''; if (navigator.share) { try { await navigator.share({ title: podcast?.title || 'پادکست و بررسی بالینی مکمل کنینا', text: podcast?.description || '', url: shareUrl }); } catch { // Fallback } } else { try { await navigator.clipboard.writeText(shareUrl); setIsCopied(true); setTimeout(() => setIsCopied(false), 2500); } catch (err) { console.error('Copy failed:', err); } } }; const handleDownload = () => { if (!podcast?.audioUrl) return; const a = document.createElement('a'); a.href = podcast.audioUrl; a.download = `${podcast.title || 'canina-podcast'}.mp3`; a.target = '_blank'; a.rel = 'noopener noreferrer'; document.body.appendChild(a); a.click(); document.body.removeChild(a); }; const formatTime = (secs: number) => { if (isNaN(secs) || secs < 0) return "00:00"; const mins = Math.floor(secs / 60); const remainingSecs = Math.floor(secs % 60); return `${mins.toString().padStart(2, "0")}:${remainingSecs.toString().padStart(2, "0")}`; }; const displayCover = podcast.cover || podcast.fallbackCover || '/images/default-podcast.png'; const progressPercent = duration > 0 ? (currentTime / duration) * 100 : 0; const volumePercent = isMuted ? 0 : volume * 100; return (
{/* Hidden Audio Tag */}