Compare commits

...
8 Commits
2 changed files with 31 additions and 25 deletions

No files matched your search

+14
View File
@@ -1,5 +1,19 @@
# Changelog
## [1.12.1](https://github.com/Masutayunikon/FanKarr/compare/v1.12.0...v1.12.1) (2026-03-21)
### Bug Fixes
* path ([7ddd9ca](https://github.com/Masutayunikon/FanKarr/commit/7ddd9cad9271d79a258f45245bf405f13b9d764f))
## [1.12.0](https://github.com/Masutayunikon/FanKarr/compare/v1.11.10...v1.12.0) (2026-03-21)
### Features
* now rename episodes with original_filename from api ([9bdb21a](https://github.com/Masutayunikon/FanKarr/commit/9bdb21a673ebb9871548aea6691bafbd049554ce))
## [1.11.10](https://github.com/Masutayunikon/FanKarr/compare/v1.11.9...v1.11.10) (2026-03-21)
+17 -25
View File
@@ -21,15 +21,12 @@ const TORRENTS_PATH = path.join(DATA_DIR, 'torrent_final.json')
const ORGANIZED_PATH = path.join(DATA_DIR, 'organized.json')
// ─── Utils log ────────────────────────────────────────────────
// Le worker tourne dans un thread séparé — il passe les logs au thread principal
// qui les écrit dans le fichier via logger.ts
function log(msg: string) { parentPort?.postMessage({ type: 'log', level: 'info', msg }) }
function warn(msg: string) { parentPort?.postMessage({ type: 'log', level: 'warn', msg }) }
function error(msg: string) { parentPort?.postMessage({ type: 'log', level: 'error', msg }) }
function debug(msg: string) { parentPort?.postMessage({ type: 'log', level: 'debug', msg }) }
// ─── Settings (lu directement, pas d'import circulaire) ──────
// ─── Settings ─────────────────────────────────────────────────
function readSettings(): { mediaPath: string; completePath: string; organizeMode: string; nfoSupport: boolean } {
try {
@@ -106,8 +103,6 @@ function getGitlabSerieTitle(serieTitle: string): string {
return SERIE_TITLE_GITLAB_MAP[serieTitle] ?? serieTitle
}
// ─── GitLab NFO downloader ────────────────────────────────────
const GITLAB_API = 'https://gitlab.com/api/v4/projects/ElPouki%2Ffankai_pack/repository'
@@ -133,7 +128,6 @@ async function downloadGitlabFolder(serieTitle: string, destRoot: string): Promi
log(`[nfo] Récupération dossier GitLab: ${gitlabTitle}`)
// Lister récursivement tous les fichiers du dossier (pagination GitLab)
let files: any[] = []
try {
let page = 1
@@ -160,13 +154,10 @@ async function downloadGitlabFolder(serieTitle: string, destRoot: string): Promi
log(`[nfo] ${fileEntries.length} fichiers à télécharger`)
for (const entry of fileEntries) {
// entry.path = "pack/Black Lagoon Henshū/Saison 1/fichier.nfo"
// On retire le préfixe "pack/{gitlabTitle}/" pour obtenir le chemin relatif
const relativePath = entry.path.replace(`pack/${gitlabTitle}/`, '')
const destPath = path.join(destRoot, relativePath)
const destDir = path.dirname(destPath)
// Skip si déjà présent
if (fs.existsSync(destPath)) continue
try {
@@ -219,15 +210,9 @@ async function organizeTorrent(hash: string, name: string, savePath: string) {
// ── Téléchargement NFO/images depuis GitLab ───────────────
if (nfoSupport) {
// Collecter tous les titres de séries distincts depuis resolved_episodes
// (un pack_integrale peut mélanger plusieurs séries, ex: One Piece Yabai + Kaï)
const serieTitlesMap = new Map<number, string>()
// Titre principal du torrent
serieTitlesMap.set(torrent.serie_id ?? 0, serieTitle)
// Titres alternatifs depuis resolved_episodes
// resolved_episodes n'a pas toujours serie_title → on cherche dans torrent_final.json
const allTorrents: any[] = (() => {
try { return JSON.parse(fs.readFileSync(TORRENTS_PATH, 'utf-8')) } catch { return [] }
})()
@@ -243,7 +228,6 @@ async function organizeTorrent(hash: string, name: string, savePath: string) {
}
}
// Télécharger NFO pour chaque série distincte
for (const title of serieTitlesMap.values()) {
const serieDestRoot = path.join(mediaPath, title)
await downloadGitlabFolder(title, serieDestRoot)
@@ -284,11 +268,9 @@ async function organizeTorrent(hash: string, name: string, savePath: string) {
}
// ── Map filename → season ─────────────────────────────────
// Priorité : season_number dans torrent_files (packs multi-saisons)
// Fallback : resolved_episodes matchés par episode_number (torrents individuels)
const filenameSeasonMap = new Map<string, number>()
// Index resolved_episodes par episode_number pour lookup rapide
// Index par filename depuis resolved_episodes (évite les collisions episode_number)
const filenameToSeason = new Map<string, number>()
for (const ep of torrent.resolved_episodes ?? []) {
if (ep.filename && ep.season_number !== undefined)
@@ -309,6 +291,15 @@ async function organizeTorrent(hash: string, name: string, savePath: string) {
}
}
// ── Map filename → original_filename (depuis l'API) ────────
// Permet de renommer le fichier selon le nom standardisé Fankai
const filenameToOriginal = new Map<string, string>()
for (const ep of torrent.resolved_episodes ?? []) {
if (ep.filename && ep.original_filename && ep.filename !== ep.original_filename) {
filenameToOriginal.set(ep.filename, ep.original_filename)
}
}
result.total = torrentFiles.length
for (const tf of torrentFiles) {
@@ -316,6 +307,7 @@ async function organizeTorrent(hash: string, name: string, savePath: string) {
const filePath: string[] = tf.path
// Skip les fichiers dans des dossiers bonus/musique/images
// Exception : fichiers avec season_number explicite (saison 0)
const tfMeta = torrentFiles.find((tf: any) => tf.filename === filename)
if (isInExcludedFolder(filePath) && tfMeta?.season_number == null) {
log(`[organize] skip bonus: ${filename}`)
@@ -347,9 +339,10 @@ async function organizeTorrent(hash: string, name: string, savePath: string) {
continue
}
const seasonNum = filenameSeasonMap.get(filename) ?? 1
const destDir = path.join(mediaPath, serieTitle, seasonFolder(seasonNum))
const dest = path.join(destDir, filename)
const seasonNum = filenameSeasonMap.get(filename) ?? 1
const destFilename = filenameToOriginal.get(filename) ?? filename
const destDir = path.join(mediaPath, serieTitle, seasonFolder(seasonNum))
const dest = path.join(destDir, destFilename)
if (fs.existsSync(dest)) {
markOrganized(hash, filename)
@@ -367,7 +360,7 @@ async function organizeTorrent(hash: string, name: string, savePath: string) {
}
markOrganized(hash, filename)
result.done++
log(`[organize] ✓ ${filename} → Saison ${String(seasonNum).padStart(3,'0')}`)
log(`[organize] ✓ ${destFilename} → Saison ${String(seasonNum).padStart(3,'0')}`)
} catch (err) {
const msg = err instanceof Error ? err.message : 'Erreur inconnue'
error(`[organize] ✗ ${filename}: ${msg}`)
@@ -397,7 +390,6 @@ parentPort?.on('message', async (msg: any) => {
}
const files: any[] = torrent.torrent_files ?? []
// Pour fichier unique : vérifier via le name directement
const allDone = files.length > 0
? files.every((f: any) => organized[t.hash]?.[f.filename])
: !!organized[t.hash]?.[t.name]