fix: utilisation des noms de dossier du torrent pour l'affichage plutot que le nom sur nyaa (notament pour gto kai qui a plusieurs integral et qui est plus explicite qque le nom nyaa)

This commit is contained in:
MasutaYunikon committed 2026-04-22 13:13:15 +02:00
1 parent 806121f95c
commit 10a9b75685
3 files changed
+70 -24

No files matched your search

+7 -7
View File
@@ -66,12 +66,12 @@ router.get('/series/:id', requireAuth, async (req, res) => {
}
if (serieData) {
// Lookup rapide hash → torrent pack (intégrale ou saison)
const torrentByHash = new Map<string, { torrent_url: string; magnet: string; type: string; raw: string; fankai: boolean }>()
const torrentByHash = new Map<string, { torrent_url: string; magnet: string; type: string; raw: string; fankai: boolean; torrent_name: string | null }>()
for (const t of (serieData.torrents ?? []))
if (t.infohash) torrentByHash.set(t.infohash.toLowerCase(), { torrent_url: t.torrent_url, magnet: t.magnet, type: 'pack_integrale', raw: t.title ?? '', fankai: t.fankai ?? true })
if (t.infohash) torrentByHash.set(t.infohash.toLowerCase(), { torrent_url: t.torrent_url, magnet: t.magnet, type: 'pack_integrale', raw: t.title ?? '', fankai: t.fankai ?? true, torrent_name: t.torrent_name ?? null })
for (const sd_s of (serieData.seasons ?? []))
for (const t of (sd_s.torrents ?? []))
if (t.infohash) torrentByHash.set(t.infohash.toLowerCase(), { torrent_url: t.torrent_url, magnet: t.magnet, type: 'pack_saison', raw: t.title ?? '', fankai: t.fankai ?? true })
if (t.infohash) torrentByHash.set(t.infohash.toLowerCase(), { torrent_url: t.torrent_url, magnet: t.magnet, type: 'pack_saison', raw: t.title ?? '', fankai: t.fankai ?? true, torrent_name: t.torrent_name ?? null })
for (const t of (serieData.torrents ?? [])) {
integraleTorrents.push({ ...t, raw: t.title })
@@ -86,7 +86,7 @@ router.get('/series/:id', requireAuth, async (req, res) => {
for (const season of serieData.seasons ?? []) {
for (const t of (season.torrents ?? [])) {
if (!seasonTorrentMapBySn[season.season_number]) seasonTorrentMapBySn[season.season_number] = []
seasonTorrentMapBySn[season.season_number].push({ torrent_url: t.torrent_url, magnet: t.magnet, infohash: t.infohash?.toLowerCase() ?? null, type: 'pack_saison', raw: t.title, manual: t.manual ?? false })
seasonTorrentMapBySn[season.season_number].push({ torrent_url: t.torrent_url, magnet: t.magnet, infohash: t.infohash?.toLowerCase() ?? null, type: 'pack_saison', raw: t.title, torrent_name: t.torrent_name ?? null, manual: t.manual ?? false })
const resolved = buildResolvedEpisodes(serieData, t.infohash, season.season_number)
for (const ep of resolved) availableEpisodeIds.add(ep.episode_id)
const orgFiles = organized[t.infohash?.toLowerCase()] ?? {}
@@ -99,7 +99,7 @@ router.get('/series/:id', requireAuth, async (req, res) => {
for (const t of (ep.torrents ?? [])) {
if (!episodeTorrentMap[ep.id]) episodeTorrentMap[ep.id] = []
const pathEntry = (ep.paths ?? []).find((p: any) => typeof p === 'object' && p.infohash?.toLowerCase() === t.infohash?.toLowerCase())
episodeTorrentMap[ep.id].push({ torrent_url: t.torrent_url, magnet: t.magnet, infohash: t.infohash?.toLowerCase() ?? null, type: 'episode', raw: t.title, manual: t.manual ?? false, fankai: t.fankai ?? true, file_index: pathEntry?.file_index ?? null, file_path: pathEntry?.path ?? null })
episodeTorrentMap[ep.id].push({ torrent_url: t.torrent_url, magnet: t.magnet, infohash: t.infohash?.toLowerCase() ?? null, type: 'episode', raw: t.title, torrent_name: t.torrent_name ?? null, manual: t.manual ?? false, fankai: t.fankai ?? true, file_index: pathEntry?.file_index ?? null, file_path: pathEntry?.path ?? null })
availableEpisodeIds.add(ep.id)
const orgFiles = organized[t.infohash?.toLowerCase()] ?? {}
const isOrg = orgFiles[String(ep.id)] !== undefined
@@ -124,7 +124,7 @@ router.get('/series/:id', requireAuth, async (req, res) => {
if (!pack) continue
seenPackHashes.add(hashKey)
if (!episodeTorrentMap[ep.id]) episodeTorrentMap[ep.id] = []
episodeTorrentMap[ep.id].push({ torrent_url: pack.torrent_url, magnet: pack.magnet, infohash: hashKey, type: pack.type, raw: pack.raw, manual: false, fankai: pack.fankai, file_index: pathEntry.file_index ?? null, file_path: pathEntry.path ?? null })
episodeTorrentMap[ep.id].push({ torrent_url: pack.torrent_url, magnet: pack.magnet, infohash: hashKey, type: pack.type, raw: pack.raw, torrent_name: pack.torrent_name ?? null, manual: false, fankai: pack.fankai, file_index: pathEntry.file_index ?? null, file_path: pathEntry.path ?? null })
availableEpisodeIds.add(ep.id)
}
}
@@ -181,7 +181,7 @@ router.get('/series/:id', requireAuth, async (req, res) => {
}
})
res.json({ serie, seasons: enrichedSeasons, torrents_integrale: integraleTorrents.map(t => ({ label: 'Intégrale', torrent_url: t.torrent_url, magnet: t.magnet, infohash: t.infohash?.toLowerCase() ?? null, raw: t.title ?? t.raw })) })
res.json({ serie, seasons: enrichedSeasons, torrents_integrale: integraleTorrents.map(t => ({ label: 'Intégrale', torrent_url: t.torrent_url, magnet: t.magnet, infohash: t.infohash?.toLowerCase() ?? null, raw: t.title ?? t.raw, torrent_name: t.torrent_name ?? null })) })
} catch (err) {
logger.error('api', `GET /api/series/${id} échoué : ${err instanceof Error ? err.message : err}`)
res.status(500).json({ error: err instanceof Error ? err.message : 'Erreur inconnue' })
+54 -13
View File
@@ -31,16 +31,38 @@
<!-- Boutons saison -->
<div class="flex items-center gap-2">
<!-- Pas de pack saison : bouton "Saison" qui déclenche les épisodes individuels -->
<button
v-if="season.torrents.length === 0 && hasDownloadable"
@click="emit('downloadSeason', season)"
:disabled="downloadingSeason"
class="inline-flex items-center gap-1.5 px-3 py-1.5 text-xs rounded-lg border bg-accent-muted text-accent border-accent/20 hover:bg-accent/20 transition"
>
<component :is="downloadingSeason ? loaderIcon : downloadIcon" />
{{ downloadingSeason ? 'Envoi…' : 'Saison' }}
</button>
<!-- Pas de pack saison : bouton "Saison" (dropdown si plusieurs packs via intégrales) -->
<template v-if="season.torrents.length === 0 && hasDownloadable">
<div v-if="uniquePackOptions.length > 1" class="relative" @click.stop>
<button
class="inline-flex items-center gap-1.5 px-3 py-1.5 text-xs rounded-lg border transition bg-accent-muted text-accent border-accent/20 hover:bg-accent/20"
@click="seasonMenuOpen = !seasonMenuOpen"
>
<component :is="downloadIcon" />
Saison ▾
</button>
<div v-if="seasonMenuOpen" class="absolute right-0 bottom-full mb-1 bg-card border border-border rounded-xl p-1 z-20 w-56 shadow-xl flex flex-col gap-0.5">
<button
v-for="opt in uniquePackOptions"
:key="opt.infohash"
class="flex items-center gap-2 px-3 py-2 rounded-lg text-xs text-primary hover:bg-hover transition-colors text-left"
@click="emit('downloadSeason', season, opt.infohash); seasonMenuOpen = false"
>
<component :is="downloadIcon" />
{{ opt.label }}
</button>
</div>
</div>
<button
v-else
@click="emit('downloadSeason', season)"
:disabled="downloadingSeason"
class="inline-flex items-center gap-1.5 px-3 py-1.5 text-xs rounded-lg border bg-accent-muted text-accent border-accent/20 hover:bg-accent/20 transition"
>
<component :is="downloadingSeason ? loaderIcon : downloadIcon" />
{{ downloadingSeason ? 'Envoi…' : 'Saison' }}
</button>
</template>
<!-- Pack saison unique -->
<button
@@ -216,7 +238,7 @@ const props = defineProps<{
const emit = defineEmits<{
toggle : [id: number]
download : [key: string, url: string | null, magnet: string | null, file_index?: number | null, file_path?: string | null]
downloadSeason : [season: any]
downloadSeason : [season: any, packHash?: string]
renameEpisode : [ep: any, season: any]
unimportEpisode: [ep: any, season: any, deleteFile: boolean]
}>()
@@ -396,15 +418,34 @@ function epNeedsRename(ep: any): boolean {
}
function torrentLabel(torrent: any, index: number): string {
const raw = torrent.raw ?? ''
if (torrent.torrent_name) return torrent.torrent_name
const raw = torrent.raw ?? ''
const quality = raw.match(/\b(2160p|4K|1080p|720p|480p)\b/i)?.[1]?.toUpperCase()
const lang = raw.match(/\b(VOSTFR|MULTI|VF|VO|FR|EN)\b/i)?.[1]?.toUpperCase()
const lang = raw.match(/\b(VOSTFR|MULTI|VF|VO|FR|EN)\b/i)?.[1]?.toUpperCase()
if (quality && lang) return `${quality} · ${lang}`
if (quality) return quality
if (lang) return lang
return raw.length > 40 ? raw.slice(0, 40) + '…' : (raw || `Option ${index + 1}`)
}
// Packs parents distincts couvrant cette saison (quand pas de pack saison explicite)
// Permet le dropdown "Saison ▾" quand une saison est couverte par plusieurs intégrales
const uniquePackOptions = computed(() => {
if (props.season.torrents?.length) return []
const seen = new Set<string>()
const opts: { infohash: string; label: string }[] = []
for (const ep of props.season.episodes) {
if (!ep.available) continue
for (const t of (ep.torrents ?? [])) {
const hash = t.infohash ?? extractHash(t)
if (!hash || seen.has(hash)) continue
seen.add(hash)
opts.push({ infohash: hash, label: torrentLabel(t, opts.length) })
}
}
return opts.length > 1 ? opts : []
})
function formatDate(d: string): string {
if (!d) return ''
return new Date(d).toLocaleDateString('fr-FR', { year: 'numeric', month: 'short', day: 'numeric' })
+9 -4
View File
@@ -110,7 +110,7 @@
:nfo-support="nfoSupport"
@toggle="toggleSeason"
@download="(key, url, magnet, fi, fp) => download(key, url, magnet, fi, fp)"
@download-season="downloadSeason"
@download-season="(s, h) => downloadSeason(s, h)"
@rename-episode="(ep, s) => renameEpisode(ep, s)"
@unimport-episode="(ep, s, del) => unimportEpisode(ep, s, del)"
/>
@@ -172,6 +172,7 @@ const data = computed(() => store.currentSerie)
// ── Helpers intégrale ─────────────────────────────────────────
function integraleLabel(t: any, i: number): string {
if (t.torrent_name) return t.torrent_name
const raw = t.raw ?? ''
const quality = raw.match(/\b(2160p|4K|1080p|720p|480p)\b/i)?.[1]?.toUpperCase()
const lang = raw.match(/\b(VOSTFR|MULTI|VF|VO|FR|EN)\b/i)?.[1]?.toUpperCase()
@@ -396,7 +397,7 @@ async function downloadAll(integraleIndex?: number) {
else toast('Aucun nouveau torrent à télécharger', 'success')
}
async function downloadSeason(season: Season) {
async function downloadSeason(season: Season, packHash?: string) {
downloadingSeason.value[season.id] = true
const torrents: { key: string; torrent_url: string | null; magnet: string | null; file_index?: number | null; file_path?: string | null }[] = []
const seasonAny = season as any
@@ -409,8 +410,12 @@ async function downloadSeason(season: Season) {
})
} else {
for (const ep of seasonAny.episodes ?? []) {
if (!ep.torrent || !ep.available || ep.organized || isAlreadyQueued(ep.torrent) || isDownloaded(`ep-${ep.id}`)) continue
torrents.push({ key: `ep-${ep.id}`, torrent_url: ep.torrent.torrent_url, magnet: ep.torrent.magnet, file_index: ep.torrent.file_index ?? null, file_path: ep.torrent.file_path ?? null })
// Si un hash de pack est précisé (choix depuis dropdown), utiliser le torrent correspondant
const epTorrent = packHash
? (ep.torrents?.find((t: any) => (t.infohash ?? extractHash(t)) === packHash) ?? ep.torrent)
: ep.torrent
if (!epTorrent || !ep.available || ep.organized || isAlreadyQueued(epTorrent) || isDownloaded(`ep-${ep.id}`)) continue
torrents.push({ key: `ep-${ep.id}`, torrent_url: epTorrent.torrent_url, magnet: epTorrent.magnet, file_index: epTorrent.file_index ?? null, file_path: epTorrent.file_path ?? null })
}
}
let sent = 0