Harden auth, add AI smart-search and post-download organization #11
+16
-1
@@ -100,9 +100,16 @@ async function tpbSearch(title){
|
|||||||
return parseTPBHtml(await res.text());
|
return parseTPBHtml(await res.text());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Low-quality source tags we never want to recommend( token match to avoid false hits
|
||||||
|
// like "GHOSTS" matching "TS").
|
||||||
|
const JUNK = new Set(['cam', 'hdcam', 'camrip', 'ts', 'hdts', 'telesync', 'tc', 'telecine', 'scr', 'screener', 'dvdscr', 'workprint']);
|
||||||
|
function isJunk(name){
|
||||||
|
return String(name).toLowerCase().split(/[.\s_\-\[\]()]+/).some(tok => JUNK.has(tok));
|
||||||
|
}
|
||||||
|
|
||||||
// Deterministic pre-filter before the torrents ever reach the LLM.
|
// Deterministic pre-filter before the torrents ever reach the LLM.
|
||||||
function prefilter(torrents, imdbId){
|
function prefilter(torrents, imdbId){
|
||||||
let out = torrents.filter(t => videoKind(t.category) && Number(t.seeders) > 0);
|
let out = torrents.filter(t => videoKind(t.category) && Number(t.seeders) > 0 && !isJunk(t.name));
|
||||||
|
|
||||||
// If we can positively identify the title by IMDB id, trust it and drop the rest.
|
// If we can positively identify the title by IMDB id, trust it and drop the rest.
|
||||||
if(imdbId){
|
if(imdbId){
|
||||||
@@ -244,6 +251,13 @@ async function findReleases(tmdbId, mediaType){
|
|||||||
let torrents = await tpbSearch(meta.title);
|
let torrents = await tpbSearch(meta.title);
|
||||||
let candidates = prefilter(torrents, meta.imdbId);
|
let candidates = prefilter(torrents, meta.imdbId);
|
||||||
|
|
||||||
|
// For movies, require the confirmed release year in the name. TPB search is fuzzy
|
||||||
|
// (a "Toy Story 5" query returns Toy Story 4/3/…), and without this the fallback can
|
||||||
|
// cross to the wrong movie. If nothing matches, we genuinely have no good copy yet.
|
||||||
|
if(mediaType !== 'tv' && meta.year){
|
||||||
|
candidates = candidates.filter(t => t.name.includes(meta.year));
|
||||||
|
}
|
||||||
|
|
||||||
if(!candidates.length){
|
if(!candidates.length){
|
||||||
return { title: meta.title, year: meta.year, options: [] };
|
return { title: meta.title, year: meta.year, options: [] };
|
||||||
}
|
}
|
||||||
@@ -345,4 +359,5 @@ module.exports = {
|
|||||||
tpbSearch,
|
tpbSearch,
|
||||||
parseTPBHtml,
|
parseTPBHtml,
|
||||||
sizeToBytes,
|
sizeToBytes,
|
||||||
|
isJunk,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -396,7 +396,7 @@
|
|||||||
|
|
||||||
// Step 3: render the release cards; a tap feeds the existing add dialog.
|
// Step 3: render the release cards; a tap feeds the existing add dialog.
|
||||||
function tbpRenderReleases(data){
|
function tbpRenderReleases(data){
|
||||||
if(!data.options.length){ tbpSearchBody('<p>No good torrents found for '+ tbpEsc(data.title) +'.</p>'); return; }
|
if(!data.options.length){ tbpSearchBody('<p>No good-quality copy of <b>'+ tbpEsc(data.title) +'</b> found — it may not be released yet, or only low-quality (CAM) copies exist.</p>'); return; }
|
||||||
var header = '<h3>'+ tbpEsc(data.title +' ('+ (data.year || '?') +')') +'</h3>';
|
var header = '<h3>'+ tbpEsc(data.title +' ('+ (data.year || '?') +')') +'</h3>';
|
||||||
if(data.library && data.library.owned) header += '<p style="color:#2a2">✓ Already in your library ('+ tbpEsc(data.library.quality) +') — only upgrades are offered.</p>';
|
if(data.library && data.library.owned) header += '<p style="color:#2a2">✓ Already in your library ('+ tbpEsc(data.library.quality) +') — only upgrades are offered.</p>';
|
||||||
tbpSearchBody(header);
|
tbpSearchBody(header);
|
||||||
|
|||||||
Reference in New Issue
Block a user