import { useMemo } from 'react'
import { useLibraryGenreDistribution, useLibraryByTmdbId } from '../../hooks/use-jellyfin'
import { useTmdbDiscoverMovies } from '../../hooks/use-tmdb'
import { usePreferencesStore } from '../../stores/preferences-store'
import { mapTmdbToJf } from '../../lib/tmdb-mapping'
import { tmdbMovieGenreId } from '../../lib/tmdb-genres'
import { filterToMissing } from '../../pages/discover/helpers'
import ContentRow from '../ui/ContentRow'
/**
* Set of "interesting" genres we consider when looking for library
* gaps. Limited to the ones that have a clear TMDB equivalent and are
* reasonable to recommend in - "Music" or "TV Movie" are excluded
* because suggestions there are usually noise.
*/
const INTERESTING_GENRES = [
'Documentary',
'Animation',
'Horror',
'Romance',
'Thriller',
'Science Fiction',
'Mystery',
'Fantasy',
'Drama',
'Comedy',
'Action',
'Adventure',
'Crime',
'Family',
'War',
'Western',
'History',
]
/**
* "Your library is heavy on X, light on Y" finder. Computes the genre
* distribution across the user's Movie + Series catalogue, picks the
* underrepresented INTERESTING_GENRES (definition: < 30% of the
* top-genre count AND fewer than 12 absolute items), and surfaces a
* top-rated TMDB row for each one.
*
* Hides itself when:
* - The user has fewer than 30 items total (results would be noise)
* - No genre crosses the under-representation threshold
*/
export default function LibraryGapFinder() {
const distQuery = useLibraryGenreDistribution()
const lib = useLibraryByTmdbId()
const gaps = useMemo(() => {
const data = distQuery.data
if (!data || data.total < 30) return [] as Array<{ genre: string; count: number; top: number }>
const top = Math.max(...Array.from(data.counts.values()), 1)
return INTERESTING_GENRES
.map(g => {
const count = data.counts.get(g) || 0
return { genre: g, count, top }
})
.filter(g => g.count < 12 && g.count < top * 0.3)
// Most-glaring gaps first (the ones with the biggest delta from top).
.sort((a, b) => a.count - b.count)
.slice(0, 3)
}, [distQuery.data])
if (gaps.length === 0) return null
const data = distQuery.data!
const topEntry = [...data.counts.entries()].sort((a, b) => b[1] - a[1])[0]
const topGenre = topEntry ? topEntry[0] : null
return (
Library gaps
What your shelves are missing
You have plenty of {topGenre || '...'}{' '}
but barely anything in {gaps.map((g, i) => (
{i > 0 && (i === gaps.length - 1 ? ' or ' : ', ')}
{g.genre.toLowerCase()} ({g.count})
))}. A few top-rated picks worth adding: