From 0e6e13332f1a8ccc487af7d2bb4cc625615da496 Mon Sep 17 00:00:00 2001 From: MasutaYunikon Date: Wed, 11 Mar 2026 14:24:33 +0100 Subject: [PATCH] =?UTF-8?q?feat(serie):=20badges=20=C3=A9tat=20organisatio?= =?UTF-8?q?n=20sur=20=C3=A9pisodes=20et=20saisons?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - api: /api/series/:id enrichi avec organized + organized_state/count par saison - ui: badge ✓ organisé vert sur chaque épisode organisé - ui: compteur organisés dans le header de saison (partial/complete) - fix: suppression fill="none" restants dans SerieView.vue --- server/index.ts | 54 +++++++++++++++++++++++++++++++++++------ src/views/SerieView.vue | 25 +++++++++++++++---- 2 files changed, 67 insertions(+), 12 deletions(-) diff --git a/server/index.ts b/server/index.ts index c4b1a6f..4ffc1df 100644 --- a/server/index.ts +++ b/server/index.ts @@ -253,15 +253,55 @@ app.get('/api/series/:id', requireAuth, async (req, res) => { }).join(' · ') } - const enrichedSeasons = seasonsWithEpisodes.map((season: any) => ({ - ...season, - torrent: seasonTorrentMap[season.id] ?? null, - episodes: season.episodes.map((ep: any) => ({ + // Charger organized.json pour les badges d'état + let organized: Record> = {} + try { + const orgPath = path.join(process.cwd(), 'data', 'organized.json') + if (fs.existsSync(orgPath)) + organized = JSON.parse(fs.readFileSync(orgPath, 'utf-8')) + } catch {} + + // Construire un set des episode_id organisés + // On croise resolved_episodes de chaque torrent avec organized.json + const organizedEpisodeIds = new Set() + for (const t of torrents) { + const hash = t.infohash?.toLowerCase() + const files = t.torrent_files ?? [] + const orgFiles = organized[hash] ?? {} + if (Object.keys(orgFiles).length === 0) continue + // Au moins un fichier organisé pour ce torrent → on marque les épisodes résolus + for (const ep of t.resolved_episodes ?? []) { + // Vérifier si le fichier de cet épisode est organisé + const epFile = files.find((f: any) => + f.episode_ids?.includes(ep.episode_id) || f.episode_number === ep.episode_number + ) + if (epFile ? orgFiles[epFile.filename] : Object.keys(orgFiles).length > 0) { + organizedEpisodeIds.add(ep.episode_id) + } + } + } + + const enrichedSeasons = seasonsWithEpisodes.map((season: any) => { + const eps = season.episodes.map((ep: any) => ({ ...ep, - available: availableEpisodeIds.has(ep.id), - torrent : episodeTorrentMap[ep.id] ?? null + available : availableEpisodeIds.has(ep.id), + torrent : episodeTorrentMap[ep.id] ?? null, + organized : organizedEpisodeIds.has(ep.id), })) - })) + // State saison : none / partial / complete + const total = eps.filter((e: any) => e.available).length + const orgCount = eps.filter((e: any) => e.organized).length + const seasonOrganizedState = + orgCount === 0 ? 'none' : + orgCount >= total && total > 0 ? 'complete' : 'partial' + return { + ...season, + torrent : seasonTorrentMap[season.id] ?? null, + organized_state : seasonOrganizedState, + organized_count : orgCount, + episodes : eps, + } + }) const heroBundles: any[] = [ ...integraleTorrents.map(t => ({ label: 'Intégrale', torrent_url: t.torrent_url, magnet: t.magnet, raw: t.raw })), diff --git a/src/views/SerieView.vue b/src/views/SerieView.vue index 1408495..3850149 100644 --- a/src/views/SerieView.vue +++ b/src/views/SerieView.vue @@ -27,14 +27,14 @@
- fanart image +
@@ -135,7 +144,7 @@ > -
+
+ + + ✓ organisé + +