Compare commits

...
18 Commits
Author SHA1 Message Date
Quentin Robert 3eb64b175d Merge pull request #137 from Masutayunikon/release-please--branches--main
chore(main): release 3.21.0
2026-06-28 12:25:54 +02:00
Quentin Robert 25a8adc91d chore(main): release 3.21.0 2026-06-28 12:11:12 +02:00
MasutaYunikon d533751ffb Merge remote-tracking branch 'origin/main' 2026-06-28 12:10:45 +02:00
MasutaYunikon 6f1e0dc844 feat(settings): ajout de englishDirectory qui permet d'avoir Season XX au lieu de Saison X 2026-06-28 12:10:34 +02:00
github-actions[bot] 894a7ed2b1 chore: update version.txt to v3.20.3 2026-06-26 13:25:13 +00:00
Quentin Robert c87b7a4c7f Merge pull request #136 from Masutayunikon/release-please--branches--main
chore(main): release 3.20.3
2026-06-26 15:24:47 +02:00
Quentin Robert a000e349c9 chore(main): release 3.20.3 2026-06-26 15:24:30 +02:00
MasutaYunikon f23e3c04ec Merge remote-tracking branch 'origin/main' 2026-06-26 15:23:45 +02:00
MasutaYunikon 63146fdc27 fix(serie): ajout du label de saisons avec les episodes 2026-06-26 15:21:55 +02:00
github-actions[bot] 7795598346 chore: update version.txt to v3.20.2 2026-06-26 12:52:07 +00:00
Quentin Robert 04252db0c4 Merge pull request #135 from Masutayunikon/release-please--branches--main
chore(main): release 3.20.2
2026-06-26 14:51:44 +02:00
Quentin Robert aeb2e938ef chore(main): release 3.20.2 2026-06-26 14:50:31 +02:00
MasutaYunikon 3799d33c62 fix(scraper): fallback on empty env for GITHUB_BASE
fix(client): add placeholder from config
fix(client): remove alldebrid
2026-06-26 14:49:53 +02:00
MasutaYunikon 98b317d493 fix(label): add season of episodes 2026-06-26 14:25:41 +02:00
Quentin Robert 4292f96645 Merge pull request #131 from Nackophilz/main
Replace favicon and add PNG logo
2026-06-09 21:44:35 +02:00
ElPouki 182c7cc807 add png logo 2026-06-09 21:42:14 +02:00
ElPouki 53be057425 replace favicon 2026-06-09 21:41:51 +02:00
github-actions[bot] ac70dba927 chore: update version.txt to v3.20.1 2026-06-08 00:23:47 +00:00
11 changed files with 77 additions and 30 deletions

No files matched your search

+24
View File
@@ -1,5 +1,29 @@
# Changelog # Changelog
## [3.21.0](https://github.com/Masutayunikon/FanKarr/compare/v3.20.3...v3.21.0) (2026-06-28)
### Features
* **settings:** ajout de englishDirectory qui permet d'avoir Season XX au lieu de Saison X ([6f1e0dc](https://github.com/Masutayunikon/FanKarr/commit/6f1e0dc844e8f97b0966b223d558c09ab498ddc9))
## [3.20.3](https://github.com/Masutayunikon/FanKarr/compare/v3.20.2...v3.20.3) (2026-06-26)
### Bug Fixes
* **serie:** ajout du label de saisons avec les episodes ([63146fd](https://github.com/Masutayunikon/FanKarr/commit/63146fdc27d98acb70cf86c7890ea1236388d717))
## [3.20.2](https://github.com/Masutayunikon/FanKarr/compare/v3.20.1...v3.20.2) (2026-06-26)
### Bug Fixes
* **client:** add placeholder from config ([3799d33](https://github.com/Masutayunikon/FanKarr/commit/3799d33c625fe28eab4b46917ff32b1266cda1e3))
* **client:** remove alldebrid ([3799d33](https://github.com/Masutayunikon/FanKarr/commit/3799d33c625fe28eab4b46917ff32b1266cda1e3))
* **label:** add season of episodes ([98b317d](https://github.com/Masutayunikon/FanKarr/commit/98b317d493b7742c330d49ca816c0fecd983e82a))
* **scraper:** fallback on empty env for GITHUB_BASE ([3799d33](https://github.com/Masutayunikon/FanKarr/commit/3799d33c625fe28eab4b46917ff32b1266cda1e3))
## [3.20.1](https://github.com/Masutayunikon/FanKarr/compare/v3.20.0...v3.20.1) (2026-06-08) ## [3.20.1](https://github.com/Masutayunikon/FanKarr/compare/v3.20.0...v3.20.1) (2026-06-08)
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 231 KiB

+3 -2
View File
@@ -2,8 +2,9 @@ import { DATA_DIR } from '../config.js'
import path from 'path' import path from 'path'
import fs from 'fs' import fs from 'fs'
const GITHUB_BASE = process.env.GITHUB_BASE const GITHUB_BASE =
?? 'https://raw.githubusercontent.com/masutayunikon/fankarr-scraper/main' process.env.GITHUB_BASE?.trim() ||
'https://raw.githubusercontent.com/masutayunikon/fankarr-scraper/main'
const CACHE_TTL_MS = 1 * 60 * 60 * 1000 const CACHE_TTL_MS = 1 * 60 * 60 * 1000
+5 -4
View File
@@ -35,12 +35,12 @@ export interface OrgEntry {
type Organized = Record<string, Record<string, OrgEntry>> type Organized = Record<string, Record<string, OrgEntry>>
// ─── Settings ───────────────────────────────────────────────── // ─── Settings ─────────────────────────────────────────────────
function readSettings(): { mediaPath: string; completePath: string; organizeMode: string; nfoSupport: boolean } { function readSettings(): { mediaPath: string; completePath: string; organizeMode: string; nfoSupport: boolean, englishDirectory: boolean } {
try { try {
const p = path.join(DATA_DIR, 'settings.json') const p = path.join(DATA_DIR, 'settings.json')
if (!fs.existsSync(p)) return { mediaPath: '', completePath: '', organizeMode: 'hardlink', nfoSupport: false } if (!fs.existsSync(p)) return { mediaPath: '', completePath: '', organizeMode: 'hardlink', nfoSupport: false, englishDirectory: false }
return JSON.parse(fs.readFileSync(p, 'utf-8')) return JSON.parse(fs.readFileSync(p, 'utf-8'))
} catch { return { mediaPath: '', completePath: '', organizeMode: 'hardlink', nfoSupport: false } } } catch { return { mediaPath: '', completePath: '', organizeMode: 'hardlink', nfoSupport: false , englishDirectory: false } }
} }
// ─── Organized log ──────────────────────────────────────────── // ─── Organized log ────────────────────────────────────────────
@@ -302,7 +302,8 @@ function swapExtension(filename: string, ext: string): string {
} }
function seasonFolder(n: number): string { function seasonFolder(n: number): string {
return n === 0 ? 'Specials' : `Saison ${n}` const { englishDirectory } = readSettings()
return n === 0 ? 'Specials' : (englishDirectory ? `Season ${String(n).padStart(2, '0')}` : `Saison ${n}`)
} }
/** /**
+2 -1
View File
@@ -10,7 +10,7 @@ router.get('/settings', requireAuth, (_req, res) => {
}) })
router.post('/settings', requireAuth, (req, res) => { router.post('/settings', requireAuth, (req, res) => {
const { mediaPath, completePath, organizeMode, category, nfoSupport, autoImport, devMode, deleteTorrentOnMove, autoUnimportMissing, requestAutoDownloadUsers } = req.body const { mediaPath, completePath, organizeMode, category, nfoSupport, autoImport, devMode, deleteTorrentOnMove, autoUnimportMissing, requestAutoDownloadUsers, englishDirectory } = req.body
const patch: Record<string, any> = {} const patch: Record<string, any> = {}
if (mediaPath !== undefined) patch.mediaPath = mediaPath if (mediaPath !== undefined) patch.mediaPath = mediaPath
if (completePath !== undefined) patch.completePath = completePath if (completePath !== undefined) patch.completePath = completePath
@@ -22,6 +22,7 @@ router.post('/settings', requireAuth, (req, res) => {
if (deleteTorrentOnMove !== undefined) patch.deleteTorrentOnMove = deleteTorrentOnMove if (deleteTorrentOnMove !== undefined) patch.deleteTorrentOnMove = deleteTorrentOnMove
if (autoUnimportMissing !== undefined) patch.autoUnimportMissing = autoUnimportMissing if (autoUnimportMissing !== undefined) patch.autoUnimportMissing = autoUnimportMissing
if (requestAutoDownloadUsers !== undefined) patch.requestAutoDownloadUsers = requestAutoDownloadUsers if (requestAutoDownloadUsers !== undefined) patch.requestAutoDownloadUsers = requestAutoDownloadUsers
if (englishDirectory !== undefined) patch.englishDirectory = englishDirectory
const updated = writeSettings(patch) const updated = writeSettings(patch)
logger.info('api', 'Paramètres mis à jour') logger.info('api', 'Paramètres mis à jour')
res.json(updated) res.json(updated)
+2
View File
@@ -16,6 +16,7 @@ export interface Settings {
requestAutoDownloadUsers: 'all' | string[] // IDs users autorisés au dl auto sur approbation ; [] = désactivé requestAutoDownloadUsers: 'all' | string[] // IDs users autorisés au dl auto sur approbation ; [] = désactivé
jellyfinUrl : string // URL du serveur Jellyfin (ex: http://jellyfin:8096) jellyfinUrl : string // URL du serveur Jellyfin (ex: http://jellyfin:8096)
jellyfinAdminToken : string // Token admin Jellyfin pour la sync jellyfinAdminToken : string // Token admin Jellyfin pour la sync
englishDirectory : boolean // Si true, les dossiers de séries sont en anglais (ex: "Season 1" au lieu de "Saison 1")
} }
const defaults: Settings = { const defaults: Settings = {
@@ -30,6 +31,7 @@ const defaults: Settings = {
requestAutoDownloadUsers: [], requestAutoDownloadUsers: [],
jellyfinUrl : '', jellyfinUrl : '',
jellyfinAdminToken : '', jellyfinAdminToken : '',
englishDirectory : false,
} }
export function readSettings(): Settings { export function readSettings(): Settings {
+32 -16
View File
@@ -606,33 +606,49 @@ const _epChapCache = new Map<string | null | undefined, ReturnType<typeof _parse
*/ */
function _parseEpChap(plot: string | null | undefined): { function _parseEpChap(plot: string | null | undefined): {
episodes: string | null episodes: string | null
chapters: string | null season: number | null
chapters: string |null
cleanPlot: string cleanPlot: string
} { } {
const text = plot ?? '' const text = plot ?? ''
const epMatch = text.match(/[éeÉE]pisodes?\s+(\d+)(?:\s+[àa]\s+(\d+))?/i) const epMatch = text.match(
const chapMatch = text.match(/chapitres?\s+(\d+)(?:\s+[àa]\s+(\d+))?/i) /[éeÉE]pisodes?\s+(\d+)(?:\s+[àa]\s+(\d+))?(?:\s+de\s+la\s+saison\s+(\d+))?/i
)
const seasonMatch = text.match(/saison\s+(\d+)/i)
const season = epMatch?.[3] ?? seasonMatch?.[1] ?? null
const chapMatch = text.match(
/chapitres?\s+(\d+)(?:\s+[àa]\s+(\d+))?/i
)
const episodes = epMatch const episodes = epMatch
? (epMatch[2] ? `Ép. ${epMatch[1]}–${epMatch[2]}` : `Ép. ${epMatch[1]}`) ? `${season ? `S${season} · ` : ''}${
: null epMatch[2]
const chapters = chapMatch ? `Ép. ${epMatch[1]}–${epMatch[2]}`
? (chapMatch[2] ? `Ch. ${chapMatch[1]}–${chapMatch[2]}` : `Ch. ${chapMatch[1]}`) : `Ép. ${epMatch[1]}`
: null }`
: null
const chapters = chapMatch
? (chapMatch[2] ? `Ch. ${chapMatch[1]}–${chapMatch[2]}` : `Ch. ${chapMatch[1]}`)
: null
// Retirer les lignes contenant les références d'épisodes ou chapitres
let cleanPlot = text let cleanPlot = text
if (episodes || chapters) { if (episodes || chapters || season) {
cleanPlot = text cleanPlot = text
.split(/\r?\n/) .split(/\r?\n/)
.filter(line => !/[éeÉE]pisodes?\s+\d+|chapitres?\s+\d+/i.test(line)) .filter(line => !/(?:[éeÉE]pisodes?\s+\d+|chapitres?\s+\d+|saison\s+\d+)/i.test(line))
.join('\n') .join('\n')
.replace(/\n{3,}/g, '\n\n') .replace(/\n{3,}/g, '\n\n')
.trim() .trim()
} }
return { episodes, chapters, cleanPlot } return { episodes, season, chapters, cleanPlot }
} }
function parseEpChap(plot: string | null | undefined) { function parseEpChap(plot: string | null | undefined) {
if (_epChapCache.has(plot)) return _epChapCache.get(plot)! if (_epChapCache.has(plot)) return _epChapCache.get(plot)!
+2 -6
View File
@@ -182,7 +182,7 @@
<input <input
v-model="modal.config[field.key]" v-model="modal.config[field.key]"
:type="field.type === 'password' ? 'password' : 'text'" :type="field.type === 'password' ? 'password' : 'text'"
placeholder="" :placeholder="field.placeholder ?? ''"
class="settings-input" class="settings-input"
/> />
</div> </div>
@@ -254,11 +254,7 @@ const clientMatrix = [
{ {
id: 'real-debrid', label: 'Real-Debrid', version: 'API v1', id: 'real-debrid', label: 'Real-Debrid', version: 'API v1',
auth: 'Clé API', fileSelect: true, fileProgress: true, torrentUpload: 'proxy', categories: false, deleteFiles: true, auth: 'Clé API', fileSelect: true, fileProgress: true, torrentUpload: 'proxy', categories: false, deleteFiles: true,
}, }
{
id: 'alldebrid', label: 'AllDebrid', version: 'API v4',
auth: 'Clé API', fileSelect: false, fileProgress: true, torrentUpload: 'proxy', categories: false, deleteFiles: true,
},
] ]
const modal = ref({ const modal = ref({
@@ -103,6 +103,11 @@
label="Désimporter si fichier manquant" label="Désimporter si fichier manquant"
description="Si un fichier importé n'est plus trouvé sur le disque lors du scan, il est automatiquement retiré de la bibliothèque." description="Si un fichier importé n'est plus trouvé sur le disque lors du scan, il est automatiquement retiré de la bibliothèque."
/> />
<SettingsToggle
v-model="form.englishDirectory"
label="Utilise 'Season' au lieu de 'Saison' dans les dossiers"
description="Si activé, les dossiers de saisons seront nommés 'Season 01' au lieu de 'Saison 01'."
/>
</div> </div>
<!-- Plex --> <!-- Plex -->
@@ -215,6 +220,7 @@ const form = ref({
autoImport : true, autoImport : true,
deleteTorrentOnMove : false, deleteTorrentOnMove : false,
autoUnimportMissing : false, autoUnimportMissing : false,
englishDirectory : false,
}) })
const picker = ref<{ open: boolean; field: 'completePath' | 'mediaPath'; currentPath: string }>({ const picker = ref<{ open: boolean; field: 'completePath' | 'mediaPath'; currentPath: string }>({
+1 -1
View File
@@ -1 +1 @@
3.20.1 3.21.0