'use client'; import Link from 'next/link'; import { useState, useEffect, useCallback } from 'react'; import { getSavedVideos, type SavedVideo } from '../../storage'; import LoadingSpinner from '../../components/LoadingSpinner'; const DEFAULT_THUMBNAIL = 'https://i.ytimg.com/vi/default/hqdefault.jpg'; interface VideoData { id: string; title: string; uploader: string; thumbnail: string; view_count: number; duration: string; uploaded_date?: string; } interface Subscription { id: number; channel_id: string; channel_name: string; channel_avatar: string; } function formatViews(views: number): string { if (views >= 1000000) return (views / 1000000).toFixed(1) + 'M'; if (views >= 1000) return (views / 1000).toFixed(1) + 'K'; return views.toString(); } function getRelativeTime(id: string): string { const times = ['2 hours ago', '5 hours ago', '1 day ago', '3 days ago', '1 week ago', '2 weeks ago', '1 month ago']; const index = (id.charCodeAt(0) || 0) % times.length; return times[index]; } function HistoryVideoCard({ video }: { video: VideoData }) { const relativeTime = video.uploaded_date || getRelativeTime(video.id); const destination = `/watch?v=${video.id}`; const thumbnailSrc = video.thumbnail || DEFAULT_THUMBNAIL; const handleImageError = useCallback((e: React.SyntheticEvent) => { const img = e.target as HTMLImageElement; if (img.src !== DEFAULT_THUMBNAIL) { img.src = DEFAULT_THUMBNAIL; } }, []); return (
{video.title} {video.duration && (
{video.duration}
)}

{video.title}

{video.uploader}

{video.view_count > 0 && (

{formatViews(video.view_count)} views

)}
); } function SubscriptionCard({ subscription }: { subscription: Subscription }) { return (
{subscription.channel_avatar || (subscription.channel_name ? subscription.channel_name[0].toUpperCase() : '?')}
{subscription.channel_name || subscription.channel_id} ); } function SavedVideoCard({ video }: { video: SavedVideo }) { const destination = `/watch?v=${video.videoId}`; const thumbnailSrc = video.thumbnail || DEFAULT_THUMBNAIL; const handleImageError = useCallback((e: React.SyntheticEvent) => { const img = e.target as HTMLImageElement; if (img.src !== DEFAULT_THUMBNAIL) { img.src = DEFAULT_THUMBNAIL; } }, []); return (
{video.title}
Saved

{video.title}

{video.channelTitle}

); } export default function LibraryPage() { const [history, setHistory] = useState([]); const [subscriptions, setSubscriptions] = useState([]); const [savedVideos, setSavedVideos] = useState([]); const [loading, setLoading] = useState(true); useEffect(() => { async function fetchData() { try { const apiBase = process.env.NEXT_PUBLIC_API_URL || 'http://127.0.0.1:8080/api'; const [historyRes, subsRes] = await Promise.all([ fetch(`${apiBase}/history?limit=20`, { cache: 'no-store' }), fetch(`${apiBase}/subscriptions`, { cache: 'no-store' }) ]); const historyData = await historyRes.json(); const subsData = await subsRes.json(); const savedData = getSavedVideos(20); setHistory(Array.isArray(historyData) ? historyData : []); setSubscriptions(Array.isArray(subsData) ? subsData : []); setSavedVideos(savedData); } catch (err) { console.error('Failed to fetch library data:', err); } finally { setLoading(false); } } fetchData(); }, []); if (loading) { return (
); } return (
{subscriptions.length > 0 && (

Sub

{subscriptions.map((sub) => ( ))}
)} {savedVideos.length > 0 && (

Saved Videos

{savedVideos.map((video) => ( ))}
)}

Watch History

{history.length === 0 ? (

No videos watched yet

Videos you watch will appear here

) : (
{history.map((video) => ( ))}
)}
); }