Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d8e284b0de | ||
|
|
0b98269695 | ||
|
|
402946b462 | ||
|
|
c374ceb0a7 |
No files matched your search
@@ -1,5 +1,19 @@
|
||||
# Changelog
|
||||
|
||||
## [2.4.0](https://github.com/Masutayunikon/FanKarr/compare/v2.3.3...v2.4.0) (2026-03-24)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **settings:** option titres optimisés Plex (title_for_plex) ([54dfe6b](https://github.com/Masutayunikon/FanKarr/commit/54dfe6b595c65a77b50dc3b4853182b594df0d9b))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **catalogue:** activeTorrents filtre désormais uniquement state=downloading ([54dfe6b](https://github.com/Masutayunikon/FanKarr/commit/54dfe6b595c65a77b50dc3b4853182b594df0d9b))
|
||||
* **organize:** retry sur les erreurs réseau dans enrichSeriesDataWithOriginalFilenames ([54dfe6b](https://github.com/Masutayunikon/FanKarr/commit/54dfe6b595c65a77b50dc3b4853182b594df0d9b))
|
||||
* **organize:** sanitisation des caractères spéciaux dans les noms de dossiers ([54dfe6b](https://github.com/Masutayunikon/FanKarr/commit/54dfe6b595c65a77b50dc3b4853182b594df0d9b))
|
||||
|
||||
## [2.3.3](https://github.com/Masutayunikon/FanKarr/compare/v2.3.2...v2.3.3) (2026-03-24)
|
||||
|
||||
|
||||
|
||||
+10
-3
@@ -42,8 +42,8 @@ app.get('/api/settings', requireAuth, (_req, res) => {
|
||||
res.json(readSettings())
|
||||
})
|
||||
app.post('/api/settings', requireAuth, (req, res) => {
|
||||
const { mediaPath, completePath, organizeMode, category, nfoSupport, autoImport } = req.body
|
||||
res.json(writeSettings({ mediaPath, completePath, organizeMode, category, nfoSupport, autoImport }))
|
||||
const { mediaPath, completePath, organizeMode, category, nfoSupport, autoImport, usePlexTitles } = req.body
|
||||
res.json(writeSettings({ mediaPath, completePath, organizeMode, category, nfoSupport, autoImport, usePlexTitles }))
|
||||
})
|
||||
|
||||
// ── Torrent clients ────────────────────────────────────────────
|
||||
@@ -284,6 +284,13 @@ async function enrichSeriesDataWithOriginalFilenames(seriesData: any[]): Promise
|
||||
for (const sd of seriesData) {
|
||||
if (!sd) { results.push(sd); continue }
|
||||
|
||||
// Fetcher title_for_plex depuis l'API série
|
||||
let title_for_plex: string | null = null
|
||||
try {
|
||||
const serieApi = await fankaiGet(`/series/${sd.id}`)
|
||||
title_for_plex = serieApi.title_for_plex ?? null
|
||||
} catch {}
|
||||
|
||||
const enrichedSeasons: any[] = []
|
||||
for (const season of sd.seasons ?? []) {
|
||||
let epsData: any[] = []
|
||||
@@ -326,7 +333,7 @@ async function enrichSeriesDataWithOriginalFilenames(seriesData: any[]): Promise
|
||||
enrichedSeasons.push({ ...season, episodes: enrichedEpisodes })
|
||||
}
|
||||
|
||||
results.push({ ...sd, seasons: enrichedSeasons })
|
||||
results.push({ ...sd, title_for_plex, seasons: enrichedSeasons })
|
||||
}
|
||||
|
||||
return results
|
||||
|
||||
@@ -118,12 +118,18 @@ function buildFileMap(
|
||||
const filename = matchedPath.split('/').pop() ?? matchedPath
|
||||
|
||||
// nfoSupport → original_filename (correspond aux .nfo GitLab)
|
||||
// sinon → formatted_name de l'API (nommage Jellyfin/Plex standardisé)
|
||||
// sinon → formatted_name de l'API, ou fallback original_filename
|
||||
const fmtName = ep.formatted_name?.trim()
|
||||
? ep.formatted_name.replace(/[<>:"/\\|?*]/g, '') + '.mkv'
|
||||
: null
|
||||
|
||||
if (!nfoSupport && !fmtName) {
|
||||
warn(`[organize] formatted_name manquant pour ep ${ep.id} (ep ${ep.episode_number}) — fallback original_filename`)
|
||||
}
|
||||
|
||||
const destName = nfoSupport
|
||||
? (ep.original_filename ?? filename)
|
||||
: (ep.formatted_name
|
||||
? ep.formatted_name.replace(/[<>:"/\\|?*]/g, '') + '.mkv'
|
||||
: (ep.original_filename ?? filename))
|
||||
: (fmtName ?? ep.original_filename ?? filename)
|
||||
|
||||
map.set(filename, {
|
||||
season_number : season.season_number,
|
||||
@@ -243,6 +249,16 @@ function tryHardlink(src: string, dest: string): boolean {
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Sanitise un nom de dossier ──────────────────────────────
|
||||
// Remplace les caractères interdits sur les systèmes de fichiers communs
|
||||
function sanitizeDirName(name: string): string {
|
||||
return name
|
||||
.replace(/:/g, ' -') // "Kaguya-sama : Love" → "Kaguya-sama - Love"
|
||||
.replace(/[<>"/\\|?*]/g, '') // autres caractères interdits
|
||||
.replace(/\s+/g, ' ') // espaces multiples
|
||||
.trim()
|
||||
}
|
||||
|
||||
// ─── Organise un torrent ──────────────────────────────────────
|
||||
async function organizeTorrent(hash: string, name: string, savePath: string, seriesData: any[]) {
|
||||
const { mediaPath, completePath, organizeMode, nfoSupport } = readSettings()
|
||||
@@ -258,7 +274,13 @@ async function organizeTorrent(hash: string, name: string, savePath: string, ser
|
||||
}
|
||||
|
||||
const { torrent, serieData } = found
|
||||
const serieTitle = serieData.title ?? serieData.show_title ?? name
|
||||
const { usePlexTitles } = readSettings()
|
||||
const rawTitle = serieData.title ?? serieData.show_title ?? name
|
||||
const serieTitle = sanitizeDirName(
|
||||
usePlexTitles && serieData.title_for_plex
|
||||
? serieData.title_for_plex
|
||||
: rawTitle
|
||||
)
|
||||
const torrentTitle = torrent.title ?? name
|
||||
log(`[organize] ✓ Trouvé : ${torrentTitle} — série: ${serieTitle}`)
|
||||
|
||||
@@ -278,11 +300,12 @@ async function organizeTorrent(hash: string, name: string, savePath: string, ser
|
||||
else if (p?.infohash?.toLowerCase() === hash.toLowerCase()) { filePath = p.path.replace(/\\/g, '/'); break }
|
||||
}
|
||||
const filename = filePath?.split('/').pop() ?? name
|
||||
const fmtName = ep.formatted_name?.trim()
|
||||
? ep.formatted_name.replace(/[<>:"/\\|?*]/g, '') + '.mkv'
|
||||
: null
|
||||
const destName = nfoSupport
|
||||
? (ep.original_filename ?? filename)
|
||||
: (ep.formatted_name
|
||||
? ep.formatted_name.replace(/[<>:"/\\|?*]/g, '') + '.mkv'
|
||||
: (ep.original_filename ?? filename))
|
||||
: (fmtName ?? ep.original_filename ?? filename)
|
||||
const destDir = path.join(mediaPath, serieTitle, seasonFolder(season.season_number ?? 1))
|
||||
const dest = path.join(destDir, destName)
|
||||
|
||||
|
||||
+3
-1
@@ -10,7 +10,8 @@ export interface Settings {
|
||||
organizeMode : 'hardlink' | 'move'
|
||||
category : string
|
||||
nfoSupport : boolean
|
||||
autoImport : boolean // import automatique quand un torrent passe en seeding
|
||||
autoImport : boolean
|
||||
usePlexTitles : boolean // utilise title_for_plex pour les noms de dossiers
|
||||
}
|
||||
|
||||
const defaults: Settings = {
|
||||
@@ -20,6 +21,7 @@ const defaults: Settings = {
|
||||
category : 'fankai',
|
||||
nfoSupport : false,
|
||||
autoImport : true,
|
||||
usePlexTitles: false,
|
||||
}
|
||||
|
||||
export function readSettings(): Settings {
|
||||
|
||||
@@ -154,6 +154,25 @@
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Titres Plex -->
|
||||
<div>
|
||||
<label class="text-[10px] text-[#5a7a94] tracking-widest block mb-2">NOMS DE DOSSIERS</label>
|
||||
<div class="flex items-center gap-3">
|
||||
<button @click="form.usePlexTitles = !form.usePlexTitles"
|
||||
class="relative shrink-0 w-11 h-6 rounded-full transition-colors cursor-pointer"
|
||||
:class="form.usePlexTitles ? 'bg-[#e8513a]' : 'bg-[#1e2d3d]'">
|
||||
<span class="absolute top-1 left-1 w-4 h-4 rounded-full bg-white transition-transform duration-200"
|
||||
:class="form.usePlexTitles ? 'translate-x-5' : 'translate-x-0'" />
|
||||
</button>
|
||||
<span class="text-xs text-[#c8d8e8]">Utiliser les titres optimisés Plex</span>
|
||||
</div>
|
||||
<p class="text-[10px] text-[#5a7a94] mt-1.5">
|
||||
Utilise <span class="text-white">title_for_plex</span> pour les noms de dossiers —
|
||||
évite les caractères spéciaux (ex: <span class="text-white">Kaguya-sama - Love is War Henshū</span>
|
||||
au lieu de <span class="text-white">Kaguya-sama : Love is War Henshū</span>)
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Support NFO -->
|
||||
<div>
|
||||
<label class="text-[10px] text-[#5a7a94] tracking-widest block mb-2">NFO / MÉTADONNÉES</label>
|
||||
@@ -277,6 +296,7 @@ const form = ref({
|
||||
organizeMode: 'hardlink' as 'hardlink' | 'move',
|
||||
nfoSupport : false,
|
||||
autoImport : true,
|
||||
usePlexTitles: false,
|
||||
})
|
||||
|
||||
const saving = ref(false)
|
||||
|
||||
Reference in new issue
Block a user