diff --git a/backend/package.json b/backend/package.json index 410f309..946aec8 100644 --- a/backend/package.json +++ b/backend/package.json @@ -7,7 +7,6 @@ "better-sqlite3": "^9.4.3", "express": "^4.18.2", "jsonwebtoken": "^9.0.2", - "multer": "^1.4.5-lts.1", - "xlsx": "^0.18.5" + "multer": "^1.4.5-lts.1" } } \ No newline at end of file diff --git a/backend/src/tools/media/routes.js b/backend/src/tools/media/routes.js index 5a7c9b7..db10161 100644 --- a/backend/src/tools/media/routes.js +++ b/backend/src/tools/media/routes.js @@ -2,13 +2,11 @@ const express = require('express'); const router = express.Router(); const db = require('../../db'); const { authenticate, requireAdmin } = require('../../middleware/auth'); -const XLSX = require('xlsx'); const TMDB_BASE = 'https://api.themoviedb.org/3'; const XREL_BASE = 'https://api.xrel.to/v2'; const THIS_YEAR = new Date().getFullYear(); const MIN_YEAR = THIS_YEAR - 1; -const NETFLIX_TOP10_URL = 'https://www.netflix.com/tudum/top10/data/all-weeks-countries.xlsx'; // Sprachen die wir NICHT wollen const BLOCKED_LANGS = new Set(['tr', 'hi', 'te', 'ta', 'ml', 'kn', 'bn', 'mr', 'pa']); @@ -491,69 +489,12 @@ router.get('/xrel-cached', authenticate, (req, res) => { // ── Streaming Top 10 pro Anbieter ────────────────────────────────────────────── // TMDb Watch-Provider-IDs für Deutschland (watch_region=DE) -// ── Netflix offizielle Top-10-Daten (keine Näherung – echte Zahlen von Netflix selbst) ── -// Quelle: https://www.netflix.com/tudum/top10/data/all-weeks-countries.xlsx -// Wird wöchentlich (dienstags) von Netflix aktualisiert → 20h Cache reicht völlig -async function getNetflixTop10(category /* 'Films' | 'TV' */) { - const cacheKey = `netflix-top10:${category}`; - const cached = cacheGet(cacheKey); - if (cached !== undefined) return cached; - - const resp = await fetch(NETFLIX_TOP10_URL, { - headers: { 'User-Agent': 'Mozilla/5.0 (compatible; DickenDock/1.0)' }, - }); - if (!resp.ok) throw new Error(`Netflix-Daten nicht erreichbar (HTTP ${resp.status})`); - const buf = await resp.arrayBuffer(); - const wb = XLSX.read(buf, { type: 'array' }); - const sheet = wb.Sheets[wb.SheetNames[0]]; - const rows = XLSX.utils.sheet_to_json(sheet); - - // Spalten (Stand der Netflix-Datei): country_name, country_iso2, category, week, weekly_rank, - // show_title, season_title, cumulative_weeks_in_top10, ... - const deRows = rows.filter(r => r.country_iso2 === 'DE' && r.category === category); - if (!deRows.length) { cacheSet(cacheKey, [], 60*60*1000); return []; } - - // Neueste Woche finden - const latestWeek = deRows.reduce((max, r) => (r.week > max ? r.week : max), deRows[0].week); - const top10 = deRows - .filter(r => r.week === latestWeek) - .sort((a, b) => a.weekly_rank - b.weekly_rank) - .slice(0, 10) - .map(r => ({ - rank: r.weekly_rank, - title: r.show_title, - seasonTitle: r.season_title || '', - weeksInTop10: r.cumulative_weeks_in_top10 || null, - week: r.week, - })); - - cacheSet(cacheKey, top10, 20 * 60 * 60 * 1000); // 20h Cache - return top10; -} - -// Netflix-Titel mit TMDb abgleichen (für Poster, Genres, Modal-Infos) -async function matchNetflixTitleToTmdb(title, mediaType, genreMap) { - try { - const searchPath = mediaType === 'tv' ? '/search/tv' : '/search/movie'; - const data = await tmdb(searchPath, { query: title }); - const hit = (data.results || [])[0]; - if (!hit) return null; - if (mediaType === 'tv') { - return { - id: hit.id, media_type: 'tv', - title: hit.name || hit.original_name, - original_title: hit.original_name, - release_date: hit.first_air_date || '', - poster_path: hit.poster_path || '', - genres: (hit.genre_ids||[]).map(gid => genreMap[gid]).filter(Boolean), - vote_average: hit.vote_average, - original_language: hit.original_language, - origin_country: hit.origin_country || [], - }; - } - return { ...enrichMovie(hit, genreMap, null, null), media_type: 'movie' }; - } catch { return null; } -} +// HINWEIS: Eine frühere Version hat hier versucht die offizielle Netflix-XLSX-Datei +// (komplette Historie seit 2021, alle ~90 Länder) direkt zu laden und zu parsen. +// Das hat den Container überlastet (CPU ~90%, RAM voll, Container kurzzeitig nicht +// erreichbar) – die Datei ist für ein synchrones Parsing im selben Prozess zu groß. +// Netflix läuft daher wie alle anderen Anbieter über die leichtgewichtige +// TMDb-Näherung weiter unten. const STREAMING_PROVIDERS = [ { id: 'netflix', label: 'Netflix', tmdbId: '8' }, @@ -593,37 +534,6 @@ router.get('/streaming/:providerId', authenticate, async (req, res) => { const provider = STREAMING_PROVIDERS.find(p => p.id === req.params.providerId); if (!provider) return res.status(404).json({ error: 'Unbekannter Anbieter' }); - // ── NETFLIX: echte offizielle Daten, KEIN Filter ───────────────────────── - if (provider.id === 'netflix') { - const genreMap = await getGenreMap(); - const [nfFilms, nfShows] = await Promise.all([ - getNetflixTop10('Films'), - getNetflixTop10('TV'), - ]); - - const movies = (await Promise.all( - nfFilms.map(async nf => { - const matched = await matchNetflixTitleToTmdb(nf.title, 'movie', genreMap); - return matched ? { ...matched, rank: nf.rank, weeksInTop10: nf.weeksInTop10, netflixTitle: nf.title } : null; - }) - )).filter(Boolean); - - const tvShows = (await Promise.all( - nfShows.map(async nf => { - const matched = await matchNetflixTitleToTmdb(nf.title, 'tv', genreMap); - return matched ? { ...matched, rank: nf.rank, weeksInTop10: nf.weeksInTop10, netflixTitle: nf.title } : null; - }) - )).filter(Boolean); - - return res.json({ - provider: { id: provider.id, label: provider.label }, - movies, tvShows, - isOfficialData: true, - filterInfo: null, // keine Filter – Original Netflix Top 10 - }); - } - // ── ENDE NETFLIX-Sonderfall ─────────────────────────────────────────────── - // Nur Titel der letzten 2 Jahre (aktuelles Jahr - 1) const minYear = new Date().getFullYear() - 1; const minDate = `${minYear}-01-01`; @@ -721,7 +631,6 @@ router.get('/streaming/:providerId', authenticate, async (req, res) => { res.json({ provider: { id: provider.id, label: provider.label }, movies, tvShows, - isOfficialData: false, filterInfo: { minYear, minVotes: MIN_VOTES, diff --git a/frontend/src/tools/media.jsx b/frontend/src/tools/media.jsx index 0fc412f..d974c8b 100644 --- a/frontend/src/tools/media.jsx +++ b/frontend/src/tools/media.jsx @@ -476,16 +476,10 @@ function StreamingGrid({ onOpenModal, xrelIds, onXrelLoaded, favIds: favIdsProp, return (