Portage complet PHP/Bash vers Node.js (Fastify + worker_threads)
This commit is contained in:
85
lib/queryParser.js
Normal file
85
lib/queryParser.js
Normal file
@@ -0,0 +1,85 @@
|
||||
// Replicates the query parsing logic shared by api.php and search.php:
|
||||
// - extract a year (last (19|20)\d{2} match, ignoring 1080/2160)
|
||||
// - extract an episode marker (SxxExxx, SxxExx, Sxx, partN, NxN, Exxx)
|
||||
// - choose movie vs tv accordingly
|
||||
// - extract titlein from the bytes before the year/episode
|
||||
|
||||
import { FILTER_RE } from './titleFilter.js';
|
||||
|
||||
const YEAR_RE = /(19|20)\d{2}/g;
|
||||
// Single-pass regex matching the PHP behaviour:
|
||||
// - S/s and E/e and "part" are case-insensitive ([Ss], [Ee], [Pp]art)
|
||||
// - the lowercase 'x' in NxN, and uppercase 'E' in standalone Exxx, are case-sensitive
|
||||
// Greedy left-to-right alternation means "S01E02" is consumed whole, so the
|
||||
// trailing "E02" alternative cannot match inside it.
|
||||
const EPISODE_RE = /[Ss][0-9]{1,2}.?[Ee][0-9]{1,3}|[Ss][0-9]{2}|[Pp]art\.?[0-9]{1,3}|[0-9]{1,2}x[0-9]{1,3}|E[0-9]{1,3}/g;
|
||||
|
||||
// PHP uses byte offsets (substr). To stay byte-faithful, work on the UTF-8 bytes.
|
||||
const utf8 = (s) => Buffer.from(s, 'utf8');
|
||||
const sliceBytes = (s, end) => utf8(s).slice(0, end).toString('utf8');
|
||||
|
||||
function findAll(re, str) {
|
||||
const out = [];
|
||||
re.lastIndex = 0;
|
||||
let m;
|
||||
while ((m = re.exec(str)) !== null) {
|
||||
out.push({ value: m[0], byteOffset: Buffer.byteLength(str.slice(0, m.index), 'utf8') });
|
||||
if (m.index === re.lastIndex) re.lastIndex++;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function stripFilter(s) {
|
||||
return s.replace(FILTER_RE, '');
|
||||
}
|
||||
|
||||
export function parseQuery(query) {
|
||||
if (!query) return null;
|
||||
|
||||
let yearin = 0;
|
||||
let yearpos = -1;
|
||||
let titlein = '';
|
||||
|
||||
const years = findAll(YEAR_RE, query).reverse();
|
||||
for (const m of years) {
|
||||
if (m.value === '1080' || m.value === '2160') continue;
|
||||
yearin = parseInt(m.value, 10);
|
||||
yearpos = m.byteOffset;
|
||||
titlein = sliceBytes(query, yearpos);
|
||||
break;
|
||||
}
|
||||
|
||||
let episodein = '';
|
||||
let episodepos = -1;
|
||||
const eps = findAll(EPISODE_RE, query).reverse();
|
||||
for (const m of eps) {
|
||||
episodein = m.value;
|
||||
episodepos = m.byteOffset;
|
||||
break;
|
||||
}
|
||||
|
||||
if (episodein) {
|
||||
if (!yearin) {
|
||||
titlein = sliceBytes(query, episodepos);
|
||||
} else if (episodepos > yearpos) {
|
||||
titlein = sliceBytes(query, yearpos);
|
||||
if (!stripFilter(titlein)) {
|
||||
titlein = sliceBytes(query, episodepos);
|
||||
yearin = 0;
|
||||
}
|
||||
} else {
|
||||
titlein = sliceBytes(query, episodepos);
|
||||
if (!stripFilter(titlein)) {
|
||||
titlein = sliceBytes(query, yearpos);
|
||||
episodein = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!yearin && !episodein) {
|
||||
return { error: 'Year or episode not found in query', titlein, yearin, episodein };
|
||||
}
|
||||
|
||||
const type = episodein ? 'tv' : 'movie';
|
||||
return { type, titlein, yearin, episodein };
|
||||
}
|
||||
Reference in New Issue
Block a user