fix: media – FSK alle Einträge, Duplikate, Jahres-Filter, xREL Response-Struktur, xREL-Cover-Badge

This commit is contained in:
2026-06-06 22:05:44 +02:00
parent e7c9c0f52f
commit 2f277c43d9
2 changed files with 193 additions and 74 deletions

View File

@@ -136,10 +136,11 @@ export default function Media({ toast, mobile }) {
}
// ── Film-Karte ────────────────────────────────────────────────────────────────
function MovieCard({ movie, favIds, onToggleFav, onOpenModal }) {
const isFav = favIds.has(movie.id);
const isTop = movie.rank != null && movie.rank <= 10;
function MovieCard({ movie, favIds, xrelIds, onToggleFav, onOpenModal }) {
const isFav = favIds.has(movie.id);
const isTop = movie.rank != null && movie.rank <= 10;
const fskStyle = FSK_COLOR(movie.fsk);
const hasXrel = xrelIds?.has(movie.id);
return (
<div style={{
@@ -177,6 +178,14 @@ function MovieCard({ movie, favIds, onToggleFav, onOpenModal }) {
{movie.fsk}
</div>
)}
{/* xREL-Badge */}
{hasXrel && (
<div style={{ position:'absolute', bottom:6, right:6, background:'rgba(78,205,196,0.9)',
borderRadius:4, padding:'1px 5px', fontSize:9, color:'#000',
fontWeight:900, fontFamily:"'Space Mono',monospace", lineHeight:1.5 }}>
xREL
</div>
)}
{/* Fav */}
<button onClick={e => { e.stopPropagation(); onToggleFav(movie, isFav); }} style={{
position:'absolute', top:5, right:5, background:'rgba(0,0,0,0.65)',
@@ -241,14 +250,27 @@ function useFavIds() {
// ── Kino Grid alle laufenden Filme ─────────────────────────────────────────
function KinoGrid({ onOpenModal }) {
const [movies, setMovies] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState('');
const [favIds, toggleFav] = useFavIds();
const [movies, setMovies] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState('');
const [xrelIds, setXrelIds] = useState(new Set());
const [favIds, toggleFav] = useFavIds();
useEffect(() => {
setLoading(true); setError('');
api('/tools/media/now-playing').then(setMovies).catch(e => setError(e.message)).finally(() => setLoading(false));
api('/tools/media/now-playing')
.then(list => {
setMovies(list);
// xREL-Batch-Check nach Laden
api('/tools/media/xrel-check', {
method: 'POST',
body: { movies: list.map(m => ({ id: m.id, title: m.title, original_title: m.original_title, release_date: m.release_date })) }
}).then(result => {
setXrelIds(new Set(Object.entries(result).filter(([,v]) => v).map(([k]) => Number(k))));
}).catch(() => {});
})
.catch(e => setError(e.message))
.finally(() => setLoading(false));
}, []);
const onToggle = async (movie, isFav) => {
@@ -268,7 +290,7 @@ function KinoGrid({ onOpenModal }) {
<>
<div style={{ ...S.head, marginBottom:12 }}> TOP 10 MEISTGESEHEN</div>
<div style={{ display:'grid', gridTemplateColumns:'repeat(auto-fill, minmax(130px,1fr))', gap:12, marginBottom:24 }}>
{top10.map(m => <MovieCard key={m.id} movie={m} favIds={favIds} onToggleFav={onToggle} onOpenModal={onOpenModal} />)}
{top10.map(m => <MovieCard key={m.id} movie={m} favIds={favIds} xrelIds={xrelIds} onToggleFav={onToggle} onOpenModal={onOpenModal} />)}
</div>
</>
)}
@@ -276,7 +298,7 @@ function KinoGrid({ onOpenModal }) {
<>
<div style={{ ...S.head, marginBottom:12 }}>WEITERE FILME IM KINO</div>
<div style={{ display:'grid', gridTemplateColumns:'repeat(auto-fill, minmax(130px,1fr))', gap:12 }}>
{rest.map(m => <MovieCard key={m.id} movie={m} favIds={favIds} onToggleFav={onToggle} onOpenModal={onOpenModal} />)}
{rest.map(m => <MovieCard key={m.id} movie={m} favIds={favIds} xrelIds={xrelIds} onToggleFav={onToggle} onOpenModal={onOpenModal} />)}
</div>
</>
)}
@@ -289,11 +311,24 @@ function DemnächstGrid({ onOpenModal }) {
const [groups, setGroups] = useState({});
const [loading, setLoading] = useState(true);
const [error, setError] = useState('');
const [xrelIds, setXrelIds] = useState(new Set());
const [favIds, toggleFav] = useFavIds();
useEffect(() => {
setLoading(true); setError('');
api('/tools/media/upcoming').then(setGroups).catch(e => setError(e.message)).finally(() => setLoading(false));
api('/tools/media/upcoming')
.then(grps => {
setGroups(grps);
const allMovies = Object.values(grps).flat();
api('/tools/media/xrel-check', {
method: 'POST',
body: { movies: allMovies.map(m => ({ id: m.id, title: m.title, original_title: m.original_title, release_date: m.release_date })) }
}).then(result => {
setXrelIds(new Set(Object.entries(result).filter(([,v]) => v).map(([k]) => Number(k))));
}).catch(() => {});
})
.catch(e => setError(e.message))
.finally(() => setLoading(false));
}, []);
const onToggle = async (movie, isFav) => {
@@ -316,7 +351,7 @@ function DemnächstGrid({ onOpenModal }) {
<span style={{ fontSize:9 }}>{movies.length} Film{movies.length!==1?'e':''}</span>
</div>
<div style={{ display:'grid', gridTemplateColumns:'repeat(auto-fill, minmax(130px,1fr))', gap:12 }}>
{movies.map(m => <MovieCard key={m.id} movie={m} favIds={favIds} onToggleFav={onToggle} onOpenModal={onOpenModal} />)}
{movies.map(m => <MovieCard key={m.id} movie={m} favIds={favIds} xrelIds={xrelIds} onToggleFav={onToggle} onOpenModal={onOpenModal} />)}
</div>
</div>
))}