import { useMemo } from 'react' import { useNavigate } from 'react-router-dom' import { motion } from 'framer-motion' import { Clock, Flame, Film, Tv, Award, Activity, CalendarStar } from '../lib/icons' import { useLibraryItems } from '../hooks/use-jellyfin' import { useDiary } from '../stores/diary-store' import { genreBreakdown, totalHoursWatched, longestBinge, watchStreak } from '../lib/watch-stats' import { hoursPerGenre, hoursPerStudio, completion, topByPersonRole, totalTimeSavedSeconds, } from '../lib/stats' import { formatTimeSaved } from '../lib/time-saved' const CURRENT_YEAR = new Date().getFullYear() /** * Detailed stats / year-in-watching. Profile page surfaces the at-a-glance * summary; this one digs deeper into hours-weighted breakdowns, top * directors / actors, completion ratios, and the time-saved-by-skipping * total across every series. */ export default function StatsPage() { const navigate = useNavigate() const { data, isLoading } = useLibraryItems(undefined, { includeItemTypes: ['Movie', 'Series', 'Episode'], sortBy: ['DatePlayed'], sortOrder: ['Descending'], filters: ['IsPlayed'], limit: 2000, includePeople: true, }) const items = useMemo(() => data?.Items || [], [data?.Items]) const yearItems = useMemo( () => items.filter(it => { const raw = it.UserData?.LastPlayedDate if (!raw) return false return new Date(raw).getFullYear() === CURRENT_YEAR }), [items], ) const totalHoursAll = useMemo(() => totalHoursWatched(items), [items]) const totalHoursYear = useMemo(() => totalHoursWatched(yearItems), [yearItems]) const streak = useMemo(() => watchStreak(items), [items]) const binge = useMemo(() => longestBinge(items), [items]) const genreShare = useMemo(() => genreBreakdown(items).slice(0, 10), [items]) const hoursGenre = useMemo(() => hoursPerGenre(items).slice(0, 10), [items]) const hoursStudio = useMemo(() => hoursPerStudio(items).slice(0, 8), [items]) const comp = useMemo(() => completion(items), [items]) const topDirectors = useMemo( () => topByPersonRole(items, (_role, type) => type === 'Director', 8), [items], ) const topActors = useMemo( () => topByPersonRole(items, (_role, type) => type === 'Actor', 10), [items], ) const timeSaved = useMemo(() => totalTimeSavedSeconds(), []) const diary = useDiary(s => s.entries) const diaryYearCount = useMemo( () => diary.filter(d => { const t = Date.parse(d.watchedAt) return Number.isFinite(t) && new Date(t).getFullYear() === CURRENT_YEAR }).length, [diary], ) const moviesCount = useMemo(() => items.filter(i => i.Type === 'Movie').length, [items]) const episodesCount = useMemo(() => items.filter(i => i.Type === 'Episode').length, [items]) return (
Stats
A deeper look at everything you've played - {items.length.toLocaleString()} {items.length === 1 ? 'item' : 'items'} tracked.
Crunching the numbers...
{(comp.completionRate * 100).toFixed(1)}% of tracked items reached the finish line.
{binge.count} items in one day
On {binge.day ? new Date(binge.day + 'T00:00:00').toLocaleDateString(undefined, { year: 'numeric', month: 'long', day: 'numeric' }) : 'unknown'}.
No watched items yet.
)}No auto-skips recorded yet. Turn intro / credits skipping on in Playback settings.
)}{hint}
}{empty}
: children}{label}
{value.toLocaleString()}
{pct}%
{icon} {label}
{value}