feat: ajout d'une hashmap pour matcher les titres sur synologie et ajouter les hash de torrent manuellement
This commit is contained in:
1 parent
bb6d094383
commit
9b4dc4f782
2 files changed
+38
-8
No files matched your search
+10
-3
@@ -170,6 +170,11 @@ async function readAvailable(): Promise<number[]> {
|
|||||||
catch { return [] }
|
catch { return [] }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function readInfohashMap(): Promise<Record<string, string>> {
|
||||||
|
try { return await githubGet('infohash_map.json') as Record<string, string> }
|
||||||
|
catch { return {} }
|
||||||
|
}
|
||||||
|
|
||||||
async function readSerieData(serieId: number): Promise<any | null> {
|
async function readSerieData(serieId: number): Promise<any | null> {
|
||||||
try { return await githubGet(`series/${serieId}.json`) }
|
try { return await githubGet(`series/${serieId}.json`) }
|
||||||
catch { return null }
|
catch { return null }
|
||||||
@@ -277,7 +282,7 @@ app.get('/api/series', requireAuth, async (_req, res) => {
|
|||||||
let organized: Record<string, Record<string, any>> = {}
|
let organized: Record<string, Record<string, any>> = {}
|
||||||
try { const p = path.join(DATA_DIR, 'organized.json'); if (fs.existsSync(p)) organized = JSON.parse(fs.readFileSync(p, 'utf-8')) } catch {}
|
try { const p = path.join(DATA_DIR, 'organized.json'); if (fs.existsSync(p)) organized = JSON.parse(fs.readFileSync(p, 'utf-8')) } catch {}
|
||||||
const activeTorrents = new Set<string>()
|
const activeTorrents = new Set<string>()
|
||||||
try { const { category } = readSettings(); const active = await dispatchList(category ?? 'fankai'); for (const t of active) { if (t.hash && t.state === 'downloading') activeTorrents.add(t.hash.toLowerCase()) } } catch {}
|
try { const { category } = readSettings(); const infohashMap = await readInfohashMap(); const active = await dispatchList(category ?? 'fankai', infohashMap); for (const t of active) { if (t.hash && t.state === 'downloading') activeTorrents.add(t.hash.toLowerCase()) } } catch {}
|
||||||
const seriesRaw = (Array.isArray(apiData) ? apiData : (apiData.series ?? [])).map(normalizeSerie)
|
const seriesRaw = (Array.isArray(apiData) ? apiData : (apiData.series ?? [])).map(normalizeSerie)
|
||||||
const serieDataMap = new Map<number, any>()
|
const serieDataMap = new Map<number, any>()
|
||||||
await Promise.all(seriesRaw.filter((s: any) => availableSet.has(s.id)).map(async (s: any) => { const sd = await readSerieData(s.id); if (sd) serieDataMap.set(s.id, sd) }))
|
await Promise.all(seriesRaw.filter((s: any) => availableSet.has(s.id)).map(async (s: any) => { const sd = await readSerieData(s.id); if (sd) serieDataMap.set(s.id, sd) }))
|
||||||
@@ -392,7 +397,8 @@ app.post('/api/download', requireAuth, async (req, res) => {
|
|||||||
app.get('/api/downloads', requireAuth, async (_req, res) => {
|
app.get('/api/downloads', requireAuth, async (_req, res) => {
|
||||||
try {
|
try {
|
||||||
const { category } = readSettings()
|
const { category } = readSettings()
|
||||||
const torrents = await dispatchList(category ?? 'fankai')
|
const infohashMap = await readInfohashMap()
|
||||||
|
const torrents = await dispatchList(category ?? 'fankai', infohashMap)
|
||||||
let organized: Record<string, Record<string, any>> = {}
|
let organized: Record<string, Record<string, any>> = {}
|
||||||
try { const p = path.join(DATA_DIR, 'organized.json'); if (fs.existsSync(p)) organized = JSON.parse(fs.readFileSync(p, 'utf-8')) } catch {}
|
try { const p = path.join(DATA_DIR, 'organized.json'); if (fs.existsSync(p)) organized = JSON.parse(fs.readFileSync(p, 'utf-8')) } catch {}
|
||||||
const enriched = await Promise.all(torrents.map(async (t: any) => {
|
const enriched = await Promise.all(torrents.map(async (t: any) => {
|
||||||
@@ -828,9 +834,10 @@ server.listen(PORT, async () => {
|
|||||||
const autoOrganize = async () => {
|
const autoOrganize = async () => {
|
||||||
try {
|
try {
|
||||||
const { category } = readSettings()
|
const { category } = readSettings()
|
||||||
|
const infohashMap = await readInfohashMap()
|
||||||
const seriesData = await loadEnrichedSeriesData()
|
const seriesData = await loadEnrichedSeriesData()
|
||||||
await autoOrganizeAll(
|
await autoOrganizeAll(
|
||||||
() => dispatchList(category ?? 'fankai'),
|
() => dispatchList(category ?? 'fankai', infohashMap),
|
||||||
seriesData,
|
seriesData,
|
||||||
(result) => {
|
(result) => {
|
||||||
if (result.done > 0 || result.errors > 0) {
|
if (result.done > 0 || result.errors > 0) {
|
||||||
|
|||||||
@@ -237,15 +237,27 @@ export async function dispatchDownload(url: string): Promise<{ uuid: string; nam
|
|||||||
|
|
||||||
// ─── Dispatch list ─────────────────────────────────────────────────────────────
|
// ─── Dispatch list ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
export async function dispatchList(category?: string): Promise<(TorrentInfo & { client_uuid: string; client_name: string })[]> {
|
// Map inversée : titre normalisé → infohash (pour matcher les torrents Synology)
|
||||||
const clients = loadClients()
|
function buildTitleIndex(infohashMap: Record<string, string>): Map<string, string> {
|
||||||
|
const index = new Map<string, string>()
|
||||||
|
for (const [hash, title] of Object.entries(infohashMap)) {
|
||||||
|
index.set(title.toLowerCase().trim(), hash.toLowerCase())
|
||||||
|
}
|
||||||
|
return index
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function dispatchList(
|
||||||
|
category? : string,
|
||||||
|
infohashMap? : Record<string, string>,
|
||||||
|
): Promise<(TorrentInfo & { client_uuid: string; client_name: string })[]> {
|
||||||
|
const clients = loadClients()
|
||||||
const results: (TorrentInfo & { client_uuid: string; client_name: string })[] = []
|
const results: (TorrentInfo & { client_uuid: string; client_name: string })[] = []
|
||||||
|
const titleIndex = infohashMap ? buildTitleIndex(infohashMap) : null
|
||||||
|
|
||||||
for (const client of clients) {
|
for (const client of clients) {
|
||||||
const driver = getDriver(client.type)
|
const driver = getDriver(client.type)
|
||||||
if (!driver) continue
|
if (!driver) continue
|
||||||
|
|
||||||
// Skip si le client est en cooldown suite à des échecs d'auth
|
|
||||||
if (isInCooldown(client.uuid)) {
|
if (isInCooldown(client.uuid)) {
|
||||||
const remaining = cooldownRemaining(client.uuid)
|
const remaining = cooldownRemaining(client.uuid)
|
||||||
logger.debug('torrent-clients', `Client "${client.name}" en cooldown — retry dans ${remaining}`)
|
logger.debug('torrent-clients', `Client "${client.name}" en cooldown — retry dans ${remaining}`)
|
||||||
@@ -266,12 +278,23 @@ export async function dispatchList(category?: string): Promise<(TorrentInfo & {
|
|||||||
const save_path = hasRemap
|
const save_path = hasRemap
|
||||||
? remapSavePath(t.save_path, remotePath, localPath)
|
? remapSavePath(t.save_path, remotePath, localPath)
|
||||||
: t.save_path
|
: t.save_path
|
||||||
results.push({ ...t, save_path, client_uuid: client.uuid, client_name: client.name })
|
|
||||||
|
// Pour Synology (hash = dbid_xxx), on essaie de résoudre le vrai infohash
|
||||||
|
// en matchant le titre du torrent dans la infohashMap
|
||||||
|
let hash = t.hash
|
||||||
|
if (titleIndex && t.hash.startsWith('dbid_')) {
|
||||||
|
const resolved = titleIndex.get(t.name.toLowerCase().trim())
|
||||||
|
if (resolved) {
|
||||||
|
logger.debug('torrent-clients', `Synology hash résolu : "${t.name}" → ${resolved.slice(0, 8)}…`)
|
||||||
|
hash = resolved
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
results.push({ ...t, hash, save_path, client_uuid: client.uuid, client_name: client.name })
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const msg = err instanceof Error ? err.message : 'Erreur inconnue'
|
const msg = err instanceof Error ? err.message : 'Erreur inconnue'
|
||||||
logger.error('torrent-clients', `Impossible de récupérer la liste depuis "${client.name}" : ${msg}`)
|
logger.error('torrent-clients', `Impossible de récupérer la liste depuis "${client.name}" : ${msg}`)
|
||||||
// Déclencher le backoff si c'est une erreur d'auth
|
|
||||||
const isAuthError = msg.toLowerCase().includes('login') ||
|
const isAuthError = msg.toLowerCase().includes('login') ||
|
||||||
msg.toLowerCase().includes('auth') ||
|
msg.toLowerCase().includes('auth') ||
|
||||||
msg.toLowerCase().includes('ban') ||
|
msg.toLowerCase().includes('ban') ||
|
||||||
|
|||||||
Reference in new issue
Block a user