node-csfd-api 5.7.0 → 5.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli.js +6 -6
- package/dto/movie.d.cts +1 -1
- package/dto/movie.d.ts +1 -1
- package/fetchers/index.cjs +1 -1
- package/fetchers/index.cjs.map +1 -1
- package/fetchers/index.js +1 -1
- package/fetchers/index.js.map +1 -1
- package/helpers/movie.helper.cjs +32 -18
- package/helpers/movie.helper.cjs.map +1 -1
- package/helpers/movie.helper.js +32 -18
- package/helpers/movie.helper.js.map +1 -1
- package/package.js +1 -1
- package/package.json +2 -2
- package/services/movie.service.cjs +7 -7
- package/services/movie.service.cjs.map +1 -1
- package/services/movie.service.js +7 -7
- package/services/movie.service.js.map +1 -1
package/cli.js
CHANGED
|
@@ -141,7 +141,7 @@ async function main() {
|
|
|
141
141
|
}
|
|
142
142
|
case "--version":
|
|
143
143
|
case "-v":
|
|
144
|
-
console.log(c.bold("5.
|
|
144
|
+
console.log(c.bold("5.8.0"));
|
|
145
145
|
break;
|
|
146
146
|
case "update":
|
|
147
147
|
await runUpdate();
|
|
@@ -234,15 +234,15 @@ async function checkForUpdateInBackground() {
|
|
|
234
234
|
} catch {}
|
|
235
235
|
}
|
|
236
236
|
} catch {}
|
|
237
|
-
if (!latestVersion || compareSemver("5.
|
|
237
|
+
if (!latestVersion || compareSemver("5.8.0", latestVersion) >= 0) return;
|
|
238
238
|
console.log("");
|
|
239
239
|
console.log(c.dim(" " + "─".repeat(44)));
|
|
240
|
-
console.log(` ${c.yellow(c.bold("↑ Update available:"))} ${c.dim("5.
|
|
240
|
+
console.log(` ${c.yellow(c.bold("↑ Update available:"))} ${c.dim("5.8.0")} → ${c.bold(c.green(latestVersion))}`);
|
|
241
241
|
console.log(` ${c.dim("Run")} ${c.cyan(getCommandName() + " update")} ${c.dim("for upgrade instructions.")}`);
|
|
242
242
|
} catch {}
|
|
243
243
|
}
|
|
244
244
|
async function runUpdate() {
|
|
245
|
-
console.log(c.dim("Current version: ") + c.bold("5.
|
|
245
|
+
console.log(c.dim("Current version: ") + c.bold("5.8.0"));
|
|
246
246
|
console.log(c.dim("Checking for updates..."));
|
|
247
247
|
let latest;
|
|
248
248
|
try {
|
|
@@ -255,7 +255,7 @@ async function runUpdate() {
|
|
|
255
255
|
console.error(err("Could not determine latest version."));
|
|
256
256
|
process.exit(1);
|
|
257
257
|
}
|
|
258
|
-
const cmp = compareSemver("5.
|
|
258
|
+
const cmp = compareSemver("5.8.0", latest);
|
|
259
259
|
if (cmp === 0) {
|
|
260
260
|
console.log(c.green("✔ Already up to date."));
|
|
261
261
|
return;
|
|
@@ -268,7 +268,7 @@ async function runUpdate() {
|
|
|
268
268
|
}
|
|
269
269
|
function printUsage() {
|
|
270
270
|
const cmd = getCommandName();
|
|
271
|
-
const header = c.bold(c.cyan("csfd")) + " " + c.dim(`v5.
|
|
271
|
+
const header = c.bold(c.cyan("csfd")) + " " + c.dim(`v5.8.0`);
|
|
272
272
|
const usage = c.bold("Usage:") + ` ${c.cyan(cmd)} ${c.dim("<command> [options]")}`;
|
|
273
273
|
const section = (title) => c.bold(title);
|
|
274
274
|
const cmd_ = (name) => " " + c.cyan(name);
|
package/dto/movie.d.cts
CHANGED
|
@@ -39,7 +39,7 @@ interface MovieJsonLd {
|
|
|
39
39
|
duration?: string;
|
|
40
40
|
[key: string]: any;
|
|
41
41
|
}
|
|
42
|
-
type CSFDVodService = 'Netflix' | 'hbogo' | 'Prime Video' | 'Apple TV+' | 'iTunes' | 'KVIFF.TV' | 'Edisonline' | 'o2tv' | 'SledovaniTV' | 'Starmax' | 'DAFilms' | 'FILMY ČESKY A ZADARMO' | 'Youtube Česká filmová klasika' | 'VAPET' | 'VOREL FILM' | 'ivysilani' | 'Google Play' | 'Voyo' | 'YouTube Movies' | 'prima+' | 'Lepší.TV' | 'Blu-ray' | 'DVD';
|
|
42
|
+
type CSFDVodService = 'Netflix' | 'hbogo' | 'Prime Video' | 'Apple TV+' | 'Apple TV' | 'iTunes' | 'KVIFF.TV' | 'Edisonline' | 'o2tv' | 'SledovaniTV' | 'Starmax' | 'DAFilms' | 'FILMY ČESKY A ZADARMO' | 'Youtube Česká filmová klasika' | 'VAPET' | 'VOREL FILM' | 'ivysilani' | 'Google Play' | 'Voyo' | 'YouTube Movies' | 'prima+' | 'Lepší.TV' | 'Blu-ray' | 'DVD';
|
|
43
43
|
interface CSFDVod {
|
|
44
44
|
title: CSFDVodService;
|
|
45
45
|
url: string;
|
package/dto/movie.d.ts
CHANGED
|
@@ -39,7 +39,7 @@ interface MovieJsonLd {
|
|
|
39
39
|
duration?: string;
|
|
40
40
|
[key: string]: any;
|
|
41
41
|
}
|
|
42
|
-
type CSFDVodService = 'Netflix' | 'hbogo' | 'Prime Video' | 'Apple TV+' | 'iTunes' | 'KVIFF.TV' | 'Edisonline' | 'o2tv' | 'SledovaniTV' | 'Starmax' | 'DAFilms' | 'FILMY ČESKY A ZADARMO' | 'Youtube Česká filmová klasika' | 'VAPET' | 'VOREL FILM' | 'ivysilani' | 'Google Play' | 'Voyo' | 'YouTube Movies' | 'prima+' | 'Lepší.TV' | 'Blu-ray' | 'DVD';
|
|
42
|
+
type CSFDVodService = 'Netflix' | 'hbogo' | 'Prime Video' | 'Apple TV+' | 'Apple TV' | 'iTunes' | 'KVIFF.TV' | 'Edisonline' | 'o2tv' | 'SledovaniTV' | 'Starmax' | 'DAFilms' | 'FILMY ČESKY A ZADARMO' | 'Youtube Česká filmová klasika' | 'VAPET' | 'VOREL FILM' | 'ivysilani' | 'Google Play' | 'Voyo' | 'YouTube Movies' | 'prima+' | 'Lepší.TV' | 'Blu-ray' | 'DVD';
|
|
43
43
|
interface CSFDVod {
|
|
44
44
|
title: CSFDVodService;
|
|
45
45
|
url: string;
|
package/fetchers/index.cjs
CHANGED
package/fetchers/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["fetchSafe","LIB_PREFIX"],"sources":["../../src/fetchers/index.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["fetchSafe","LIB_PREFIX"],"sources":["../../src/fetchers/index.ts"],"sourcesContent":["import { LIB_PREFIX } from '../vars';\nimport { fetchSafe } from './fetch.polyfill';\n\ninterface BrowserProfile {\n 'User-Agent': string;\n 'Sec-Ch-Ua': string;\n 'Sec-Ch-Ua-Platform': string;\n}\n\nconst browserProfiles: BrowserProfile[] = [\n // Chrome 131 / Windows\n {\n 'User-Agent':\n 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',\n 'Sec-Ch-Ua': '\"Google Chrome\";v=\"131\", \"Chromium\";v=\"131\", \"Not_A Brand\";v=\"24\"',\n 'Sec-Ch-Ua-Platform': '\"Windows\"'\n },\n // Chrome 130 / Windows\n {\n 'User-Agent':\n 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36',\n 'Sec-Ch-Ua': '\"Google Chrome\";v=\"130\", \"Chromium\";v=\"130\", \"Not_A Brand\";v=\"24\"',\n 'Sec-Ch-Ua-Platform': '\"Windows\"'\n },\n // Chrome 131 / macOS\n {\n 'User-Agent':\n 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',\n 'Sec-Ch-Ua': '\"Google Chrome\";v=\"131\", \"Chromium\";v=\"131\", \"Not_A Brand\";v=\"24\"',\n 'Sec-Ch-Ua-Platform': '\"macOS\"'\n },\n // Chrome 130 / macOS\n {\n 'User-Agent':\n 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36',\n 'Sec-Ch-Ua': '\"Google Chrome\";v=\"130\", \"Chromium\";v=\"130\", \"Not_A Brand\";v=\"24\"',\n 'Sec-Ch-Ua-Platform': '\"macOS\"'\n },\n // Edge 131 / Windows\n {\n 'User-Agent':\n 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.0.0',\n 'Sec-Ch-Ua': '\"Microsoft Edge\";v=\"131\", \"Chromium\";v=\"131\", \"Not_A Brand\";v=\"24\"',\n 'Sec-Ch-Ua-Platform': '\"Windows\"'\n },\n // Edge 130 / Windows\n {\n 'User-Agent':\n 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 Edg/130.0.0.0',\n 'Sec-Ch-Ua': '\"Microsoft Edge\";v=\"130\", \"Chromium\";v=\"130\", \"Not_A Brand\";v=\"24\"',\n 'Sec-Ch-Ua-Platform': '\"Windows\"'\n }\n];\n\nconst randomProfile = (): BrowserProfile =>\n browserProfiles[Math.floor(Math.random() * browserProfiles.length)];\n\nconst baseHeaders = {\n Accept:\n 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',\n 'Accept-Language': 'cs-CZ,cs;q=0.9,en-US;q=0.8,en;q=0.7',\n 'Accept-Encoding': 'gzip, deflate, br',\n 'Cache-Control': 'max-age=0',\n Connection: 'keep-alive',\n 'Sec-Ch-Ua-Mobile': '?0',\n 'Sec-Fetch-Dest': 'document',\n 'Sec-Fetch-Mode': 'navigate',\n 'Sec-Fetch-Site': 'none',\n 'Sec-Fetch-User': '?1',\n 'Upgrade-Insecure-Requests': '1'\n};\n\nexport const fetchPage = async (url: string, optionsRequest?: RequestInit): Promise<string> => {\n try {\n const mergedHeaders = new Headers({ ...baseHeaders, ...randomProfile() });\n\n // Merge any custom headers provided in the function arguments\n if (optionsRequest?.headers) {\n const reqHeaders = new Headers(optionsRequest.headers);\n reqHeaders.forEach((value, key) => mergedHeaders.set(key, value));\n }\n\n const { headers: _, ...restOptions } = optionsRequest || {};\n\n const response = await fetchSafe(url, {\n credentials: 'omit',\n ...restOptions,\n headers: mergedHeaders\n });\n\n if (!response.ok) {\n throw new Error(`node-csfd-api: Bad response ${response.status} for url: ${url}`);\n }\n\n const html = await response.text();\n\n // Quickly check if we hit the trap\n if (html.includes(\"Making sure you're not a bot!\")) {\n console.warn('[node-csfd-api] Trap detected. You may be rate-limited or blocked by ČSFD.');\n }\n\n return html;\n } catch (e: unknown) {\n if (e instanceof Error) {\n console.error(LIB_PREFIX, e.message);\n } else {\n console.error(LIB_PREFIX, String(e));\n }\n return 'Error';\n }\n};\n"],"mappings":";;;AASA,MAAM,kBAAoC;CAExC;EACE,cACE;EACF,aAAa;EACb,sBAAsB;EACvB;CAED;EACE,cACE;EACF,aAAa;EACb,sBAAsB;EACvB;CAED;EACE,cACE;EACF,aAAa;EACb,sBAAsB;EACvB;CAED;EACE,cACE;EACF,aAAa;EACb,sBAAsB;EACvB;CAED;EACE,cACE;EACF,aAAa;EACb,sBAAsB;EACvB;CAED;EACE,cACE;EACF,aAAa;EACb,sBAAsB;EACvB;CACF;AAED,MAAM,sBACJ,gBAAgB,KAAK,MAAM,KAAK,QAAQ,GAAG,gBAAgB,OAAO;AAEpE,MAAM,cAAc;CAClB,QACE;CACF,mBAAmB;CACnB,mBAAmB;CACnB,iBAAiB;CACjB,YAAY;CACZ,oBAAoB;CACpB,kBAAkB;CAClB,kBAAkB;CAClB,kBAAkB;CAClB,kBAAkB;CAClB,6BAA6B;CAC9B;AAED,MAAa,YAAY,OAAO,KAAa,mBAAkD;AAC7F,KAAI;EACF,MAAM,gBAAgB,IAAI,QAAQ;GAAE,GAAG;GAAa,GAAG,eAAe;GAAE,CAAC;AAGzE,MAAI,gBAAgB,QACC,KAAI,QAAQ,eAAe,QAAQ,CAC3C,SAAS,OAAO,QAAQ,cAAc,IAAI,KAAK,MAAM,CAAC;EAGnE,MAAM,EAAE,SAAS,GAAG,GAAG,gBAAgB,kBAAkB,EAAE;EAE3D,MAAM,WAAW,MAAMA,uBAAAA,UAAU,KAAK;GACpC,aAAa;GACb,GAAG;GACH,SAAS;GACV,CAAC;AAEF,MAAI,CAAC,SAAS,GACZ,OAAM,IAAI,MAAM,+BAA+B,SAAS,OAAO,YAAY,MAAM;EAGnF,MAAM,OAAO,MAAM,SAAS,MAAM;AAGlC,MAAI,KAAK,SAAS,gCAAgC,CAChD,SAAQ,KAAK,6EAA6E;AAG5F,SAAO;UACA,GAAY;AACnB,MAAI,aAAa,MACf,SAAQ,MAAMC,aAAAA,YAAY,EAAE,QAAQ;MAEpC,SAAQ,MAAMA,aAAAA,YAAY,OAAO,EAAE,CAAC;AAEtC,SAAO"}
|
package/fetchers/index.js
CHANGED
package/fetchers/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/fetchers/index.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/fetchers/index.ts"],"sourcesContent":["import { LIB_PREFIX } from '../vars';\nimport { fetchSafe } from './fetch.polyfill';\n\ninterface BrowserProfile {\n 'User-Agent': string;\n 'Sec-Ch-Ua': string;\n 'Sec-Ch-Ua-Platform': string;\n}\n\nconst browserProfiles: BrowserProfile[] = [\n // Chrome 131 / Windows\n {\n 'User-Agent':\n 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',\n 'Sec-Ch-Ua': '\"Google Chrome\";v=\"131\", \"Chromium\";v=\"131\", \"Not_A Brand\";v=\"24\"',\n 'Sec-Ch-Ua-Platform': '\"Windows\"'\n },\n // Chrome 130 / Windows\n {\n 'User-Agent':\n 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36',\n 'Sec-Ch-Ua': '\"Google Chrome\";v=\"130\", \"Chromium\";v=\"130\", \"Not_A Brand\";v=\"24\"',\n 'Sec-Ch-Ua-Platform': '\"Windows\"'\n },\n // Chrome 131 / macOS\n {\n 'User-Agent':\n 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',\n 'Sec-Ch-Ua': '\"Google Chrome\";v=\"131\", \"Chromium\";v=\"131\", \"Not_A Brand\";v=\"24\"',\n 'Sec-Ch-Ua-Platform': '\"macOS\"'\n },\n // Chrome 130 / macOS\n {\n 'User-Agent':\n 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36',\n 'Sec-Ch-Ua': '\"Google Chrome\";v=\"130\", \"Chromium\";v=\"130\", \"Not_A Brand\";v=\"24\"',\n 'Sec-Ch-Ua-Platform': '\"macOS\"'\n },\n // Edge 131 / Windows\n {\n 'User-Agent':\n 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.0.0',\n 'Sec-Ch-Ua': '\"Microsoft Edge\";v=\"131\", \"Chromium\";v=\"131\", \"Not_A Brand\";v=\"24\"',\n 'Sec-Ch-Ua-Platform': '\"Windows\"'\n },\n // Edge 130 / Windows\n {\n 'User-Agent':\n 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 Edg/130.0.0.0',\n 'Sec-Ch-Ua': '\"Microsoft Edge\";v=\"130\", \"Chromium\";v=\"130\", \"Not_A Brand\";v=\"24\"',\n 'Sec-Ch-Ua-Platform': '\"Windows\"'\n }\n];\n\nconst randomProfile = (): BrowserProfile =>\n browserProfiles[Math.floor(Math.random() * browserProfiles.length)];\n\nconst baseHeaders = {\n Accept:\n 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',\n 'Accept-Language': 'cs-CZ,cs;q=0.9,en-US;q=0.8,en;q=0.7',\n 'Accept-Encoding': 'gzip, deflate, br',\n 'Cache-Control': 'max-age=0',\n Connection: 'keep-alive',\n 'Sec-Ch-Ua-Mobile': '?0',\n 'Sec-Fetch-Dest': 'document',\n 'Sec-Fetch-Mode': 'navigate',\n 'Sec-Fetch-Site': 'none',\n 'Sec-Fetch-User': '?1',\n 'Upgrade-Insecure-Requests': '1'\n};\n\nexport const fetchPage = async (url: string, optionsRequest?: RequestInit): Promise<string> => {\n try {\n const mergedHeaders = new Headers({ ...baseHeaders, ...randomProfile() });\n\n // Merge any custom headers provided in the function arguments\n if (optionsRequest?.headers) {\n const reqHeaders = new Headers(optionsRequest.headers);\n reqHeaders.forEach((value, key) => mergedHeaders.set(key, value));\n }\n\n const { headers: _, ...restOptions } = optionsRequest || {};\n\n const response = await fetchSafe(url, {\n credentials: 'omit',\n ...restOptions,\n headers: mergedHeaders\n });\n\n if (!response.ok) {\n throw new Error(`node-csfd-api: Bad response ${response.status} for url: ${url}`);\n }\n\n const html = await response.text();\n\n // Quickly check if we hit the trap\n if (html.includes(\"Making sure you're not a bot!\")) {\n console.warn('[node-csfd-api] Trap detected. You may be rate-limited or blocked by ČSFD.');\n }\n\n return html;\n } catch (e: unknown) {\n if (e instanceof Error) {\n console.error(LIB_PREFIX, e.message);\n } else {\n console.error(LIB_PREFIX, String(e));\n }\n return 'Error';\n }\n};\n"],"mappings":";;;AASA,MAAM,kBAAoC;CAExC;EACE,cACE;EACF,aAAa;EACb,sBAAsB;EACvB;CAED;EACE,cACE;EACF,aAAa;EACb,sBAAsB;EACvB;CAED;EACE,cACE;EACF,aAAa;EACb,sBAAsB;EACvB;CAED;EACE,cACE;EACF,aAAa;EACb,sBAAsB;EACvB;CAED;EACE,cACE;EACF,aAAa;EACb,sBAAsB;EACvB;CAED;EACE,cACE;EACF,aAAa;EACb,sBAAsB;EACvB;CACF;AAED,MAAM,sBACJ,gBAAgB,KAAK,MAAM,KAAK,QAAQ,GAAG,gBAAgB,OAAO;AAEpE,MAAM,cAAc;CAClB,QACE;CACF,mBAAmB;CACnB,mBAAmB;CACnB,iBAAiB;CACjB,YAAY;CACZ,oBAAoB;CACpB,kBAAkB;CAClB,kBAAkB;CAClB,kBAAkB;CAClB,kBAAkB;CAClB,6BAA6B;CAC9B;AAED,MAAa,YAAY,OAAO,KAAa,mBAAkD;AAC7F,KAAI;EACF,MAAM,gBAAgB,IAAI,QAAQ;GAAE,GAAG;GAAa,GAAG,eAAe;GAAE,CAAC;AAGzE,MAAI,gBAAgB,QACC,KAAI,QAAQ,eAAe,QAAQ,CAC3C,SAAS,OAAO,QAAQ,cAAc,IAAI,KAAK,MAAM,CAAC;EAGnE,MAAM,EAAE,SAAS,GAAG,GAAG,gBAAgB,kBAAkB,EAAE;EAE3D,MAAM,WAAW,MAAM,UAAU,KAAK;GACpC,aAAa;GACb,GAAG;GACH,SAAS;GACV,CAAC;AAEF,MAAI,CAAC,SAAS,GACZ,OAAM,IAAI,MAAM,+BAA+B,SAAS,OAAO,YAAY,MAAM;EAGnF,MAAM,OAAO,MAAM,SAAS,MAAM;AAGlC,MAAI,KAAK,SAAS,gCAAgC,CAChD,SAAQ,KAAK,6EAA6E;AAG5F,SAAO;UACA,GAAY;AACnB,MAAI,aAAa,MACf,SAAQ,MAAM,YAAY,EAAE,QAAQ;MAEpC,SAAQ,MAAM,YAAY,OAAO,EAAE,CAAC;AAEtC,SAAO"}
|
package/helpers/movie.helper.cjs
CHANGED
|
@@ -79,22 +79,29 @@ const getMovieTitle = (el) => {
|
|
|
79
79
|
return el.querySelector("h1").innerText.split(`(`)[0].trim();
|
|
80
80
|
};
|
|
81
81
|
const getMovieGenres = (el) => {
|
|
82
|
-
|
|
82
|
+
const genresNode = el.querySelector(".genres");
|
|
83
|
+
if (!genresNode) return [];
|
|
84
|
+
return genresNode.childNodes.map((n) => n.textContent.trim()).filter((x) => x.length > 0);
|
|
83
85
|
};
|
|
84
86
|
const getMovieOrigins = (el) => {
|
|
85
|
-
|
|
87
|
+
const originNode = el.querySelector(".origin");
|
|
88
|
+
if (!originNode) return [];
|
|
89
|
+
return (originNode.childNodes[0]?.text || "").split("/").map((x) => x.trim()).filter((x) => x);
|
|
86
90
|
};
|
|
87
91
|
const getMovieColorRating = (bodyClasses) => {
|
|
88
92
|
return require_global_helper.getColor(bodyClasses[1]);
|
|
89
93
|
};
|
|
90
94
|
const getMovieRating = (el) => {
|
|
91
|
-
const rating = el.querySelector(".film-rating-average")
|
|
95
|
+
const rating = (el.querySelector(".film-rating-average")?.textContent)?.replace(/%/g, "")?.trim();
|
|
92
96
|
const ratingInt = parseInt(rating);
|
|
93
97
|
if (Number.isInteger(ratingInt)) return ratingInt;
|
|
94
98
|
else return null;
|
|
95
99
|
};
|
|
96
100
|
const getMovieRatingCount = (el) => {
|
|
97
|
-
const
|
|
101
|
+
const ratingCountRaw = el.querySelector(".ratings-list .counter")?.textContent;
|
|
102
|
+
if (!ratingCountRaw) return null;
|
|
103
|
+
const countString = ratingCountRaw.replace(/[^\d]/g, "");
|
|
104
|
+
const ratingCount = parseInt(countString, 10);
|
|
98
105
|
if (Number.isInteger(ratingCount)) return ratingCount;
|
|
99
106
|
else return null;
|
|
100
107
|
};
|
|
@@ -107,14 +114,19 @@ const getMovieDuration = (jsonLd, el) => {
|
|
|
107
114
|
return require_global_helper.parseISO8601Duration(jsonLd.duration);
|
|
108
115
|
} catch (e) {}
|
|
109
116
|
try {
|
|
110
|
-
const
|
|
111
|
-
if (
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
|
|
117
|
+
const originText = el.querySelector(".origin")?.textContent;
|
|
118
|
+
if (originText) {
|
|
119
|
+
const match = originText.match(/(?:(\d+)\s*h)?\s*(\d+)\s*min/);
|
|
120
|
+
if (match) {
|
|
121
|
+
const hours = parseInt(match[1] || "0", 10);
|
|
122
|
+
const minutes = parseInt(match[2] || "0", 10);
|
|
123
|
+
return hours * 60 + minutes;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
115
126
|
} catch (error) {
|
|
116
127
|
return null;
|
|
117
128
|
}
|
|
129
|
+
return null;
|
|
118
130
|
};
|
|
119
131
|
const getMovieTitlesOther = (el) => {
|
|
120
132
|
const namesNode = el.querySelectorAll(".film-names li");
|
|
@@ -200,7 +212,7 @@ const getMovieCreators = (el, options) => {
|
|
|
200
212
|
const getSeasonsOrEpisodes = (el) => {
|
|
201
213
|
const childrenList = el.querySelector(".film-episodes-list");
|
|
202
214
|
if (!childrenList) return null;
|
|
203
|
-
const childrenNodes = childrenList.querySelectorAll(".film-title");
|
|
215
|
+
const childrenNodes = childrenList.querySelectorAll(".film-title-inline");
|
|
204
216
|
if (!childrenNodes?.length) return [];
|
|
205
217
|
return childrenNodes.map((season) => {
|
|
206
218
|
const nameContainer = season.querySelector(".film-title-name");
|
|
@@ -222,14 +234,16 @@ const getEpisodeCode = (el) => {
|
|
|
222
234
|
return match ? match[1] : null;
|
|
223
235
|
};
|
|
224
236
|
const detectSeasonOrEpisodeListType = (el) => {
|
|
225
|
-
const
|
|
237
|
+
const episodesList = el.querySelector(".film-episodes-list");
|
|
238
|
+
if (!episodesList) return null;
|
|
239
|
+
const headerText = (episodesList.closest(".updated-box") || episodesList.closest("section") || el).querySelector(".updated-box-header h3")?.textContent?.trim() ?? "";
|
|
226
240
|
if (headerText.includes("Série")) return "seasons";
|
|
227
|
-
if (headerText.
|
|
241
|
+
if (headerText.includes("Epizody")) return "episodes";
|
|
228
242
|
return null;
|
|
229
243
|
};
|
|
230
244
|
const getSeasonOrEpisodeParent = (el) => {
|
|
231
|
-
let parents = el.querySelectorAll(".film-
|
|
232
|
-
if (parents.length === 0) parents = el.querySelectorAll(".film-header h1 a");
|
|
245
|
+
let parents = el.querySelectorAll(".film-series-content h2 a");
|
|
246
|
+
if (parents.length === 0) parents = el.querySelectorAll(".film-header-name h1 a");
|
|
233
247
|
if (parents.length === 0) return null;
|
|
234
248
|
const [parentSeries, parentSeason] = parents;
|
|
235
249
|
const seriesId = require_global_helper.parseIdFromUrl(parentSeries?.getAttribute("href"));
|
|
@@ -251,11 +265,11 @@ const getSeasonOrEpisodeParent = (el) => {
|
|
|
251
265
|
};
|
|
252
266
|
};
|
|
253
267
|
const getMovieType = (el) => {
|
|
254
|
-
return require_global_helper.parseFilmType(el.querySelector(".film-header-name .type")?.innerText?.replace(/[{()}]/g, "") || "film");
|
|
268
|
+
return require_global_helper.parseFilmType(el.querySelector(".film-header-name .type")?.innerText?.replace(/[{()}]/g, "").split("\n")[0].trim() || "film");
|
|
255
269
|
};
|
|
256
270
|
const getMovieVods = (el) => {
|
|
257
271
|
let vods = [];
|
|
258
|
-
if (el) vods = el.querySelectorAll(".box-
|
|
272
|
+
if (el) vods = el.querySelectorAll(".box-film-vod .vod-badge-link").map((btn) => {
|
|
259
273
|
return {
|
|
260
274
|
title: btn.textContent.trim(),
|
|
261
275
|
url: btn.attributes.href
|
|
@@ -264,7 +278,7 @@ const getMovieVods = (el) => {
|
|
|
264
278
|
return vods.length ? vods : [];
|
|
265
279
|
};
|
|
266
280
|
const getBoxContent = (el, box) => {
|
|
267
|
-
return el.querySelectorAll("section
|
|
281
|
+
return el.querySelectorAll("section .updated-box-header").find((header) => header.querySelector("h3")?.textContent.trim() === box || header.querySelector("h2")?.textContent.trim() === box)?.parentNode;
|
|
268
282
|
};
|
|
269
283
|
const getMovieBoxMovies = (el, boxName) => {
|
|
270
284
|
const movieListItem = [];
|
|
@@ -295,7 +309,7 @@ const getMoviePremieres = (el) => {
|
|
|
295
309
|
return premiere;
|
|
296
310
|
};
|
|
297
311
|
const getMovieTags = (el) => {
|
|
298
|
-
return el.querySelectorAll(".box-content a
|
|
312
|
+
return el.querySelectorAll(".updated-box-content-tags a").map((tag) => tag.textContent.trim());
|
|
299
313
|
};
|
|
300
314
|
//#endregion
|
|
301
315
|
exports.detectSeasonOrEpisodeListType = detectSeasonOrEpisodeListType;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"movie.helper.cjs","names":["getColor","parseISO8601Duration","addProtocol","parseIdFromUrl","parseLastIdFromUrl","parseFilmType","parseDate"],"sources":["../../src/helpers/movie.helper.ts"],"sourcesContent":["import { HTMLElement } from 'node-html-parser';\nimport type { CSFDColorRating, CSFDFilmTypes } from '../dto/global';\nimport type {\n CSFDBoxContent,\n CSFDCreatorGroups,\n CSFDCreatorGroupsEnglish,\n CSFDCreatorGroupsSlovak,\n CSFDCreators,\n CSFDGenres,\n CSFDMovieCreator,\n CSFDMovieListItem,\n CSFDParent,\n CSFDPremiere,\n CSFDSeriesChild,\n CSFDTitlesOther,\n CSFDVod,\n CSFDVodService,\n MovieJsonLd\n} from '../dto/movie';\nimport type { CSFDOptions } from '../types';\nimport {\n addProtocol,\n getColor,\n parseDate,\n parseFilmType,\n parseISO8601Duration,\n parseIdFromUrl,\n parseLastIdFromUrl\n} from './global.helper';\n\nconst CREATOR_LABELS: Record<\n string,\n Record<string, CSFDCreatorGroups | CSFDCreatorGroupsEnglish | CSFDCreatorGroupsSlovak>\n> = {\n en: {\n directors: 'Directed by',\n writers: 'Screenplay',\n cinematography: 'Cinematography',\n music: 'Composer',\n actors: 'Cast',\n basedOn: 'Based on',\n producers: 'Produced by',\n filmEditing: 'Editing',\n costumeDesign: 'Costumes',\n productionDesign: 'Production design',\n casting: 'Casting',\n sound: 'Sound',\n makeup: 'Make-up'\n },\n cs: {\n directors: 'Režie',\n writers: 'Scénář',\n cinematography: 'Kamera',\n music: 'Hudba',\n actors: 'Hrají',\n basedOn: 'Předloha',\n producers: 'Produkce',\n filmEditing: 'Střih',\n costumeDesign: 'Kostýmy',\n productionDesign: 'Scénografie',\n casting: 'Casting',\n sound: 'Zvuk',\n makeup: 'Masky'\n },\n sk: {\n directors: 'Réžia',\n writers: 'Scenár',\n cinematography: 'Kamera',\n music: 'Hudba',\n actors: 'Hrajú',\n basedOn: 'Predloha',\n producers: 'Produkcia',\n filmEditing: 'Strih',\n costumeDesign: 'Kostýmy',\n productionDesign: 'Scénografia',\n casting: 'Casting',\n sound: 'Zvuk',\n makeup: 'Masky'\n }\n};\n\n/**\n * Maps language-specific movie creator group labels.\n * @param language - The language code (e.g., 'en', 'cs')\n * @param key - The key of the creator group (e.g., 'directors', 'writers')\n * @returns The localized label for the creator group\n */\nexport const getLocalizedCreatorLabel = (\n language: string | undefined,\n key:\n | 'directors'\n | 'writers'\n | 'cinematography'\n | 'music'\n | 'actors'\n | 'basedOn'\n | 'producers'\n | 'filmEditing'\n | 'costumeDesign'\n | 'productionDesign'\n | 'casting'\n | 'sound'\n | 'makeup'\n): CSFDCreatorGroups | CSFDCreatorGroupsEnglish | CSFDCreatorGroupsSlovak => {\n const lang = language || 'cs'; // Default to Czech\n return (CREATOR_LABELS[lang] || CREATOR_LABELS['cs'])[key];\n};\n\nexport const getMovieId = (el: HTMLElement): number => {\n const url = el.querySelector('.tabs .tab-nav-list a').attributes.href;\n return parseIdFromUrl(url);\n};\n\nexport const getSeriesAndSeasonTitle = (\n el: HTMLElement\n): { seriesName: string | null; seasonName: string | null } => {\n const titleElement = el.querySelector('h1');\n if (!titleElement) {\n return { seriesName: null, seasonName: null };\n }\n\n const fullText = titleElement.innerText.trim();\n\n // Check if there's a series part indicated by ' - '\n if (fullText.includes(' - ')) {\n const [seriesName, seasonName] = fullText.split(' - ').map((part) => part.trim());\n return { seriesName, seasonName };\n }\n\n // If no series part found, return just the name\n return { seriesName: fullText, seasonName: null };\n};\n\nexport const getMovieTitle = (el: HTMLElement): string => {\n return el.querySelector('h1').innerText.split(`(`)[0].trim();\n};\n\nexport const getMovieGenres = (el: HTMLElement): CSFDGenres[] => {\n const genresRaw = el.querySelector('.genres').textContent;\n return genresRaw.split(' / ') as CSFDGenres[];\n};\n\nexport const getMovieOrigins = (el: HTMLElement): string[] => {\n const originsRaw = el.querySelector('.origin').textContent;\n const origins = originsRaw.split(',')[0];\n return origins.split(' / ');\n};\n\nexport const getMovieColorRating = (bodyClasses: string[]): CSFDColorRating => {\n return getColor(bodyClasses[1]);\n};\n\nexport const getMovieRating = (el: HTMLElement): number => {\n const ratingRaw = el.querySelector('.film-rating-average').textContent;\n const rating = ratingRaw?.replace(/%/g, '').trim();\n const ratingInt = parseInt(rating);\n\n if (Number.isInteger(ratingInt)) {\n return ratingInt;\n } else {\n return null;\n }\n};\n\nexport const getMovieRatingCount = (el: HTMLElement): number => {\n const ratingCountRaw = el.querySelector('.box-rating-container .counter')?.textContent;\n const ratingCount = +ratingCountRaw?.replace(/[(\\s)]/g, '');\n if (Number.isInteger(ratingCount)) {\n return ratingCount;\n } else {\n return null;\n }\n};\n\nexport const getMovieYear = (jsonLd: MovieJsonLd | null): number => {\n if (jsonLd && jsonLd.dateCreated) {\n return +jsonLd.dateCreated;\n }\n return null;\n};\n\nexport const getMovieDuration = (jsonLd: MovieJsonLd | null, el: HTMLElement): number => {\n if (jsonLd && jsonLd.duration) {\n try {\n return parseISO8601Duration(jsonLd.duration);\n } catch (e) {\n // ignore\n }\n }\n\n try {\n const origin = el.querySelector('.origin').innerText;\n const timeString = origin.split(',');\n if (timeString.length > 2) {\n // Get last time elelment\n const timeString2 = timeString.pop().trim();\n // Clean it\n const timeRaw = timeString2.split('(')[0].trim();\n // Split by minutes and hours\n const hoursMinsRaw = timeRaw.split('min')[0];\n const hoursMins = hoursMinsRaw.split('h');\n // Resolve hours + minutes format\n const duration = hoursMins.length > 1 ? +hoursMins[0] * 60 + +hoursMins[1] : +hoursMins[0];\n return duration;\n } else {\n return null;\n }\n } catch (error) {\n return null;\n }\n};\n\nexport const getMovieTitlesOther = (el: HTMLElement): CSFDTitlesOther[] => {\n const namesNode = el.querySelectorAll('.film-names li');\n\n if (!namesNode.length) {\n return [];\n }\n\n const titlesOther = namesNode.map((el) => {\n const country = el.querySelector('img.flag').attributes.alt;\n const title = el.textContent.trim().split('\\n')[0];\n\n if (country && title) {\n return {\n country,\n title\n };\n } else {\n return null;\n }\n });\n\n return titlesOther.filter((x) => x);\n};\n\nexport const getMoviePoster = (el: HTMLElement | null): string => {\n const poster = el.querySelector('.film-posters img');\n // Resolve empty image\n if (poster) {\n if (poster.classNames?.includes('empty-image')) {\n return null;\n } else {\n // Full sized image (not thumb)\n const imageThumb = poster.attributes.src.split('?')[0];\n const image = imageThumb.replace(/\\/w140\\//, '/w1080/');\n return addProtocol(image);\n }\n } else {\n return null;\n }\n};\n\nexport const getMovieRandomPhoto = (el: HTMLElement | null): string => {\n const imageNode = el.querySelector('.gallery-item picture img');\n const image = imageNode?.attributes?.src;\n if (image) {\n return image.replace(/\\/w663\\//, '/w1326/');\n } else {\n return null;\n }\n};\n\nexport const getMovieTrivia = (el: HTMLElement | null): string[] => {\n const triviaNodes = el.querySelectorAll('.article-trivia ul li');\n if (triviaNodes?.length) {\n return triviaNodes.map((node) => node.textContent.trim().replace(/(\\r\\n|\\n|\\r|\\t)/gm, ''));\n } else {\n return null;\n }\n};\n\nexport const getMovieDescriptions = (el: HTMLElement): string[] => {\n return el\n .querySelectorAll('.body--plots .plot-full p, .body--plots .plots .plots-item p')\n .map((movie) => movie.textContent?.trim().replace(/(\\r\\n|\\n|\\r|\\t)/gm, ''));\n};\n\nconst parseMoviePeople = (el: HTMLElement): CSFDMovieCreator[] => {\n const people = el.querySelectorAll('a');\n return (\n people\n // Filter out \"more\" links\n .filter((x) => x.classNames.length === 0)\n .map((person) => {\n return {\n id: parseIdFromUrl(person.attributes.href),\n name: person.innerText.trim(),\n url: `https://www.csfd.cz${person.attributes.href}`\n };\n })\n );\n};\n\n// export const getMovieGroup = (el: HTMLElement, group: CSFDCreatorGroups | CSFDCreatorGroupsEnglish | CSFDCreatorGroupsSlovak): CSFDMovieCreator[] => {\n// const creators = el.querySelectorAll('.creators h4');\n// const element = creators.filter((elem) => elem.textContent.trim().includes(group))[0];\n// if (element?.parentNode) {\n// return parseMoviePeople(element.parentNode as HTMLElement);\n// } else {\n// return [];\n// }\n// };\n\nexport const getMovieCreators = (el: HTMLElement, options?: CSFDOptions): CSFDCreators => {\n const creators: CSFDCreators = {\n directors: [],\n writers: [],\n cinematography: [],\n music: [],\n actors: [],\n basedOn: [],\n producers: [],\n filmEditing: [],\n costumeDesign: [],\n productionDesign: [],\n sound: []\n };\n\n const groups = el.querySelectorAll('.creators h4');\n\n const keys = [\n 'directors',\n 'writers',\n 'cinematography',\n 'music',\n 'actors',\n 'basedOn',\n 'producers',\n 'filmEditing',\n 'costumeDesign',\n 'productionDesign',\n 'sound'\n ] as const;\n\n const localizedLabels = keys.map((key) => ({\n key,\n label: getLocalizedCreatorLabel(options?.language, key) as string\n }));\n\n for (const group of groups) {\n const text = group.textContent.trim();\n for (const { key, label } of localizedLabels) {\n if (text.includes(label)) {\n if (group.parentNode) {\n creators[key] = parseMoviePeople(group.parentNode as HTMLElement);\n }\n break;\n }\n }\n }\n\n return creators;\n};\n\nexport const getSeasonsOrEpisodes = (el: HTMLElement): CSFDSeriesChild[] | null => {\n const childrenList = el.querySelector('.film-episodes-list');\n if (!childrenList) return null;\n\n const childrenNodes = childrenList.querySelectorAll('.film-title');\n if (!childrenNodes?.length) return [];\n\n return childrenNodes.map((season) => {\n const nameContainer = season.querySelector('.film-title-name');\n const infoContainer = season.querySelector('.info');\n\n const href = nameContainer?.getAttribute('href');\n const url = href ? (href.startsWith('/') ? `https://www.csfd.cz${href}` : href) : null;\n\n return {\n id: parseLastIdFromUrl(href || ''),\n title: nameContainer?.textContent?.trim() || null,\n url,\n info: infoContainer?.textContent?.replace(/[{()}]/g, '').trim() || null\n };\n });\n};\n\nexport const getEpisodeCode = (el: HTMLElement): string | null => {\n const filmHeaderName = el.querySelector('.film-header-name h1');\n if (!filmHeaderName) return null;\n\n const text = filmHeaderName.textContent?.trim() || '';\n const match = text.match(/\\(([^)]+)\\)/);\n const code = match ? match[1] : null;\n\n return code;\n};\n\nexport const detectSeasonOrEpisodeListType = (el: HTMLElement) => {\n const headerText = el.querySelector('.box-header h3')?.innerText.trim() ?? '';\n\n if (headerText.includes('Série')) return 'seasons';\n if (headerText.startsWith('Epizody')) return 'episodes';\n return null;\n};\n\nexport const getSeasonOrEpisodeParent = (el: HTMLElement): CSFDParent | null => {\n // Try h2 first (for episodes), then h1 (for seasons)\n let parents = el.querySelectorAll('.film-header h2 a');\n if (parents.length === 0) {\n parents = el.querySelectorAll('.film-header h1 a');\n }\n\n if (parents.length === 0) return null;\n\n const [parentSeries, parentSeason] = parents;\n\n const seriesId = parseIdFromUrl(parentSeries?.getAttribute('href'));\n const seasonId = parseLastIdFromUrl(parentSeason?.getAttribute('href') || '');\n const seriesTitle = parentSeries?.textContent?.trim() || null;\n const seasonTitle = parentSeason?.textContent?.trim() || null;\n\n const series = seriesId && seriesTitle ? { id: seriesId, title: seriesTitle } : null;\n const season = seasonId && seasonTitle ? { id: seasonId, title: seasonTitle } : null;\n\n if (!series && !season) return null;\n\n return { series, season };\n};\n\nexport const getMovieGroup = (\n el: HTMLElement,\n group: CSFDCreatorGroups | CSFDCreatorGroupsEnglish | CSFDCreatorGroupsSlovak\n): CSFDMovieCreator[] => {\n const creators = el.querySelectorAll('.creators h4');\n const element = creators.find((elem) => elem.textContent.trim().includes(group as string));\n if (element?.parentNode) {\n return parseMoviePeople(element.parentNode as HTMLElement);\n } else {\n return [];\n }\n};\n\nexport const getMovieType = (el: HTMLElement): CSFDFilmTypes => {\n const type = el.querySelector('.film-header-name .type');\n return parseFilmType(type?.innerText?.replace(/[{()}]/g, '') || 'film');\n};\n\nexport const getMovieVods = (el: HTMLElement | null): CSFDVod[] => {\n let vods: CSFDVod[] = [];\n if (el) {\n const buttonsVod = el.querySelectorAll('.box-buttons-vod .vod-badge a');\n vods = buttonsVod.map((btn) => {\n return {\n title: btn.textContent.trim() as CSFDVodService,\n url: btn.attributes.href\n };\n });\n }\n return vods.length ? vods : [];\n};\n\n// Get box content\nconst getBoxContent = (el: HTMLElement, box: string): HTMLElement => {\n const headers = el.querySelectorAll('section.box .box-header');\n return headers.find((header) => header.querySelector('h3')?.textContent.trim().includes(box))\n ?.parentNode;\n};\n\nexport const getMovieBoxMovies = (\n el: HTMLElement,\n boxName: CSFDBoxContent\n): CSFDMovieListItem[] => {\n const movieListItem: CSFDMovieListItem[] = [];\n const box = getBoxContent(el, boxName);\n const movieTitleNodes = box?.querySelectorAll('.article-header .film-title-name');\n if (movieTitleNodes?.length) {\n for (const item of movieTitleNodes) {\n movieListItem.push({\n id: parseIdFromUrl(item.attributes.href),\n title: item.textContent.trim(),\n url: `https://www.csfd.cz${item.attributes.href}`\n });\n }\n }\n return movieListItem;\n};\n\nexport const getMoviePremieres = (el: HTMLElement): CSFDPremiere[] => {\n const premiereNodes = el.querySelectorAll('.box-premieres li');\n const premiere: CSFDPremiere[] = [];\n for (const premiereNode of premiereNodes) {\n const title = premiereNode.querySelector('p + span').attributes.title;\n\n if (title) {\n const [dateRaw, ...company] = title?.split(' ');\n const date = parseDate(dateRaw);\n\n if (date) {\n premiere.push({\n country: premiereNode.querySelector('.flag')?.attributes.title || null,\n format: premiereNode.querySelector('p').textContent.trim()?.split(' od')[0],\n date,\n company: company.join(' ')\n });\n }\n }\n }\n return premiere;\n};\n\nexport const getMovieTags = (el: HTMLElement): string[] => {\n const tagsRaw = el.querySelectorAll('.box-content a[href*=\"/tag/\"]');\n return tagsRaw.map((tag) => tag.textContent);\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"movie.helper.cjs","names":["getColor","parseISO8601Duration","addProtocol","parseIdFromUrl","parseLastIdFromUrl","parseFilmType","parseDate"],"sources":["../../src/helpers/movie.helper.ts"],"sourcesContent":["import { HTMLElement } from 'node-html-parser';\nimport type { CSFDColorRating, CSFDFilmTypes } from '../dto/global';\nimport type {\n CSFDBoxContent,\n CSFDCreatorGroups,\n CSFDCreatorGroupsEnglish,\n CSFDCreatorGroupsSlovak,\n CSFDCreators,\n CSFDGenres,\n CSFDMovieCreator,\n CSFDMovieListItem,\n CSFDParent,\n CSFDPremiere,\n CSFDSeriesChild,\n CSFDTitlesOther,\n CSFDVod,\n CSFDVodService,\n MovieJsonLd\n} from '../dto/movie';\nimport type { CSFDOptions } from '../types';\nimport {\n addProtocol,\n getColor,\n parseDate,\n parseFilmType,\n parseISO8601Duration,\n parseIdFromUrl,\n parseLastIdFromUrl\n} from './global.helper';\n\nconst CREATOR_LABELS: Record<\n string,\n Record<string, CSFDCreatorGroups | CSFDCreatorGroupsEnglish | CSFDCreatorGroupsSlovak>\n> = {\n en: {\n directors: 'Directed by',\n writers: 'Screenplay',\n cinematography: 'Cinematography',\n music: 'Composer',\n actors: 'Cast',\n basedOn: 'Based on',\n producers: 'Produced by',\n filmEditing: 'Editing',\n costumeDesign: 'Costumes',\n productionDesign: 'Production design',\n casting: 'Casting',\n sound: 'Sound',\n makeup: 'Make-up'\n },\n cs: {\n directors: 'Režie',\n writers: 'Scénář',\n cinematography: 'Kamera',\n music: 'Hudba',\n actors: 'Hrají',\n basedOn: 'Předloha',\n producers: 'Produkce',\n filmEditing: 'Střih',\n costumeDesign: 'Kostýmy',\n productionDesign: 'Scénografie',\n casting: 'Casting',\n sound: 'Zvuk',\n makeup: 'Masky'\n },\n sk: {\n directors: 'Réžia',\n writers: 'Scenár',\n cinematography: 'Kamera',\n music: 'Hudba',\n actors: 'Hrajú',\n basedOn: 'Predloha',\n producers: 'Produkcia',\n filmEditing: 'Strih',\n costumeDesign: 'Kostýmy',\n productionDesign: 'Scénografia',\n casting: 'Casting',\n sound: 'Zvuk',\n makeup: 'Masky'\n }\n};\n\n/**\n * Maps language-specific movie creator group labels.\n * @param language - The language code (e.g., 'en', 'cs')\n * @param key - The key of the creator group (e.g., 'directors', 'writers')\n * @returns The localized label for the creator group\n */\nexport const getLocalizedCreatorLabel = (\n language: string | undefined,\n key:\n | 'directors'\n | 'writers'\n | 'cinematography'\n | 'music'\n | 'actors'\n | 'basedOn'\n | 'producers'\n | 'filmEditing'\n | 'costumeDesign'\n | 'productionDesign'\n | 'casting'\n | 'sound'\n | 'makeup'\n): CSFDCreatorGroups | CSFDCreatorGroupsEnglish | CSFDCreatorGroupsSlovak => {\n const lang = language || 'cs'; // Default to Czech\n return (CREATOR_LABELS[lang] || CREATOR_LABELS['cs'])[key];\n};\n\nexport const getMovieId = (el: HTMLElement): number => {\n const url = el.querySelector('.tabs .tab-nav-list a').attributes.href;\n return parseIdFromUrl(url);\n};\n\nexport const getSeriesAndSeasonTitle = (\n el: HTMLElement\n): { seriesName: string | null; seasonName: string | null } => {\n const titleElement = el.querySelector('h1');\n if (!titleElement) {\n return { seriesName: null, seasonName: null };\n }\n\n const fullText = titleElement.innerText.trim();\n\n // Check if there's a series part indicated by ' - '\n if (fullText.includes(' - ')) {\n const [seriesName, seasonName] = fullText.split(' - ').map((part) => part.trim());\n return { seriesName, seasonName };\n }\n\n // If no series part found, return just the name\n return { seriesName: fullText, seasonName: null };\n};\n\nexport const getMovieTitle = (el: HTMLElement): string => {\n return el.querySelector('h1').innerText.split(`(`)[0].trim();\n};\n\nexport const getMovieGenres = (el: HTMLElement): CSFDGenres[] => {\n const genresNode = el.querySelector('.genres');\n if (!genresNode) return [];\n return genresNode.childNodes\n .map((n) => n.textContent.trim())\n .filter((x) => x.length > 0) as CSFDGenres[];\n};\n\nexport const getMovieOrigins = (el: HTMLElement): string[] => {\n const originNode = el.querySelector('.origin');\n if (!originNode) return [];\n const text = originNode.childNodes[0]?.text || '';\n return text.split('/').map(x => x.trim()).filter(x => x);\n};\n\nexport const getMovieColorRating = (bodyClasses: string[]): CSFDColorRating => {\n return getColor(bodyClasses[1]);\n};\n\nexport const getMovieRating = (el: HTMLElement): number => {\n const ratingRaw = el.querySelector('.film-rating-average')?.textContent;\n const rating = ratingRaw?.replace(/%/g, '')?.trim();\n const ratingInt = parseInt(rating);\n\n if (Number.isInteger(ratingInt)) {\n return ratingInt;\n } else {\n return null;\n }\n};\n\nexport const getMovieRatingCount = (el: HTMLElement): number => {\n const ratingCountRaw = el.querySelector('.ratings-list .counter')?.textContent;\n if (!ratingCountRaw) return null;\n const countString = ratingCountRaw.replace(/[^\\d]/g, '');\n const ratingCount = parseInt(countString, 10);\n if (Number.isInteger(ratingCount)) {\n return ratingCount;\n } else {\n return null;\n }\n};\n\nexport const getMovieYear = (jsonLd: MovieJsonLd | null): number => {\n if (jsonLd && jsonLd.dateCreated) {\n return +jsonLd.dateCreated;\n }\n return null;\n};\n\nexport const getMovieDuration = (jsonLd: MovieJsonLd | null, el: HTMLElement): number => {\n if (jsonLd && jsonLd.duration) {\n try {\n return parseISO8601Duration(jsonLd.duration);\n } catch (e) {\n // ignore\n }\n }\n\n try {\n const originText = el.querySelector('.origin')?.textContent;\n if (originText) {\n const match = originText.match(/(?:(\\d+)\\s*h)?\\s*(\\d+)\\s*min/);\n if (match) {\n const hours = parseInt(match[1] || '0', 10);\n const minutes = parseInt(match[2] || '0', 10);\n return hours * 60 + minutes;\n }\n }\n } catch (error) {\n return null;\n }\n return null;\n};\n\nexport const getMovieTitlesOther = (el: HTMLElement): CSFDTitlesOther[] => {\n const namesNode = el.querySelectorAll('.film-names li');\n\n if (!namesNode.length) {\n return [];\n }\n\n const titlesOther = namesNode.map((el) => {\n const country = el.querySelector('img.flag').attributes.alt;\n const title = el.textContent.trim().split('\\n')[0];\n\n if (country && title) {\n return {\n country,\n title\n };\n } else {\n return null;\n }\n });\n\n return titlesOther.filter((x) => x);\n};\n\nexport const getMoviePoster = (el: HTMLElement | null): string => {\n const poster = el.querySelector('.film-posters img');\n // Resolve empty image\n if (poster) {\n if (poster.classNames?.includes('empty-image')) {\n return null;\n } else {\n // Full sized image (not thumb)\n const imageThumb = poster.attributes.src.split('?')[0];\n const image = imageThumb.replace(/\\/w140\\//, '/w1080/');\n return addProtocol(image);\n }\n } else {\n return null;\n }\n};\n\nexport const getMovieRandomPhoto = (el: HTMLElement | null): string => {\n const imageNode = el.querySelector('.gallery-item picture img');\n const image = imageNode?.attributes?.src;\n if (image) {\n return image.replace(/\\/w663\\//, '/w1326/');\n } else {\n return null;\n }\n};\n\nexport const getMovieTrivia = (el: HTMLElement | null): string[] => {\n const triviaNodes = el.querySelectorAll('.article-trivia ul li');\n if (triviaNodes?.length) {\n return triviaNodes.map((node) => node.textContent.trim().replace(/(\\r\\n|\\n|\\r|\\t)/gm, ''));\n } else {\n return null;\n }\n};\n\nexport const getMovieDescriptions = (el: HTMLElement): string[] => {\n return el\n .querySelectorAll('.body--plots .plot-full p, .body--plots .plots .plots-item p')\n .map((movie) => movie.textContent?.trim().replace(/(\\r\\n|\\n|\\r|\\t)/gm, ''));\n};\n\nconst parseMoviePeople = (el: HTMLElement): CSFDMovieCreator[] => {\n const people = el.querySelectorAll('a');\n return (\n people\n // Filter out \"more\" links\n .filter((x) => x.classNames.length === 0)\n .map((person) => {\n return {\n id: parseIdFromUrl(person.attributes.href),\n name: person.innerText.trim(),\n url: `https://www.csfd.cz${person.attributes.href}`\n };\n })\n );\n};\n\n// export const getMovieGroup = (el: HTMLElement, group: CSFDCreatorGroups | CSFDCreatorGroupsEnglish | CSFDCreatorGroupsSlovak): CSFDMovieCreator[] => {\n// const creators = el.querySelectorAll('.creators h4');\n// const element = creators.filter((elem) => elem.textContent.trim().includes(group))[0];\n// if (element?.parentNode) {\n// return parseMoviePeople(element.parentNode as HTMLElement);\n// } else {\n// return [];\n// }\n// };\n\nexport const getMovieCreators = (el: HTMLElement, options?: CSFDOptions): CSFDCreators => {\n const creators: CSFDCreators = {\n directors: [],\n writers: [],\n cinematography: [],\n music: [],\n actors: [],\n basedOn: [],\n producers: [],\n filmEditing: [],\n costumeDesign: [],\n productionDesign: [],\n sound: []\n };\n\n const groups = el.querySelectorAll('.creators h4');\n\n const keys = [\n 'directors',\n 'writers',\n 'cinematography',\n 'music',\n 'actors',\n 'basedOn',\n 'producers',\n 'filmEditing',\n 'costumeDesign',\n 'productionDesign',\n 'sound'\n ] as const;\n\n const localizedLabels = keys.map((key) => ({\n key,\n label: getLocalizedCreatorLabel(options?.language, key) as string\n }));\n\n for (const group of groups) {\n const text = group.textContent.trim();\n for (const { key, label } of localizedLabels) {\n if (text.includes(label)) {\n if (group.parentNode) {\n creators[key] = parseMoviePeople(group.parentNode as HTMLElement);\n }\n break;\n }\n }\n }\n\n return creators;\n};\n\nexport const getSeasonsOrEpisodes = (el: HTMLElement): CSFDSeriesChild[] | null => {\n const childrenList = el.querySelector('.film-episodes-list');\n if (!childrenList) return null;\n\n const childrenNodes = childrenList.querySelectorAll('.film-title-inline');\n if (!childrenNodes?.length) return [];\n\n return childrenNodes.map((season) => {\n const nameContainer = season.querySelector('.film-title-name');\n const infoContainer = season.querySelector('.info');\n\n const href = nameContainer?.getAttribute('href');\n const url = href ? (href.startsWith('/') ? `https://www.csfd.cz${href}` : href) : null;\n\n return {\n id: parseLastIdFromUrl(href || ''),\n title: nameContainer?.textContent?.trim() || null,\n url,\n info: infoContainer?.textContent?.replace(/[{()}]/g, '').trim() || null\n };\n });\n};\n\nexport const getEpisodeCode = (el: HTMLElement): string | null => {\n const filmHeaderName = el.querySelector('.film-header-name h1');\n if (!filmHeaderName) return null;\n\n const text = filmHeaderName.textContent?.trim() || '';\n const match = text.match(/\\(([^)]+)\\)/);\n const code = match ? match[1] : null;\n\n return code;\n};\n\nexport const detectSeasonOrEpisodeListType = (el: HTMLElement) => {\n const episodesList = el.querySelector('.film-episodes-list');\n if (!episodesList) return null;\n\n const section = episodesList.closest('.updated-box') || episodesList.closest('section') || el;\n const headerText = section.querySelector('.updated-box-header h3')?.textContent?.trim() ?? '';\n\n if (headerText.includes('Série')) return 'seasons';\n if (headerText.includes('Epizody')) return 'episodes';\n return null;\n};\n\nexport const getSeasonOrEpisodeParent = (el: HTMLElement): CSFDParent | null => {\n let parents = el.querySelectorAll('.film-series-content h2 a');\n if (parents.length === 0) parents = el.querySelectorAll('.film-header-name h1 a');\n if (parents.length === 0) return null;\n\n const [parentSeries, parentSeason] = parents;\n\n const seriesId = parseIdFromUrl(parentSeries?.getAttribute('href'));\n const seasonId = parseLastIdFromUrl(parentSeason?.getAttribute('href') || '');\n const seriesTitle = parentSeries?.textContent?.trim() || null;\n const seasonTitle = parentSeason?.textContent?.trim() || null;\n\n const series = seriesId && seriesTitle ? { id: seriesId, title: seriesTitle } : null;\n const season = seasonId && seasonTitle ? { id: seasonId, title: seasonTitle } : null;\n\n if (!series && !season) return null;\n\n return { series, season };\n};\n\nexport const getMovieGroup = (\n el: HTMLElement,\n group: CSFDCreatorGroups | CSFDCreatorGroupsEnglish | CSFDCreatorGroupsSlovak\n): CSFDMovieCreator[] => {\n const creators = el.querySelectorAll('.creators h4');\n const element = creators.find((elem) => elem.textContent.trim().includes(group as string));\n if (element?.parentNode) {\n return parseMoviePeople(element.parentNode as HTMLElement);\n } else {\n return [];\n }\n};\n\nexport const getMovieType = (el: HTMLElement): CSFDFilmTypes => {\n const type = el.querySelector('.film-header-name .type');\n const text = type?.innerText?.replace(/[{()}]/g, '').split('\\n')[0].trim() || 'film';\n return parseFilmType(text);\n};\n\nexport const getMovieVods = (el: HTMLElement | null): CSFDVod[] => {\n let vods: CSFDVod[] = [];\n if (el) {\n const buttonsVod = el.querySelectorAll('.box-film-vod .vod-badge-link');\n vods = buttonsVod.map((btn) => {\n return {\n title: btn.textContent.trim() as CSFDVodService,\n url: btn.attributes.href\n };\n });\n }\n return vods.length ? vods : [];\n};\n\n// Get box content\nconst getBoxContent = (el: HTMLElement, box: string): HTMLElement => {\n const headers = el.querySelectorAll('section .updated-box-header');\n return headers.find(\n (header) =>\n header.querySelector('h3')?.textContent.trim() === box ||\n header.querySelector('h2')?.textContent.trim() === box\n )?.parentNode;\n};\n\nexport const getMovieBoxMovies = (\n el: HTMLElement,\n boxName: CSFDBoxContent\n): CSFDMovieListItem[] => {\n const movieListItem: CSFDMovieListItem[] = [];\n const box = getBoxContent(el, boxName);\n const movieTitleNodes = box?.querySelectorAll('.article-header .film-title-name');\n if (movieTitleNodes?.length) {\n for (const item of movieTitleNodes) {\n movieListItem.push({\n id: parseIdFromUrl(item.attributes.href),\n title: item.textContent.trim(),\n url: `https://www.csfd.cz${item.attributes.href}`\n });\n }\n }\n return movieListItem;\n};\n\nexport const getMoviePremieres = (el: HTMLElement): CSFDPremiere[] => {\n const premiereNodes = el.querySelectorAll('.box-premieres li');\n const premiere: CSFDPremiere[] = [];\n for (const premiereNode of premiereNodes) {\n const title = premiereNode.querySelector('p + span').attributes.title;\n\n if (title) {\n const [dateRaw, ...company] = title?.split(' ');\n const date = parseDate(dateRaw);\n\n if (date) {\n premiere.push({\n country: premiereNode.querySelector('.flag')?.attributes.title || null,\n format: premiereNode.querySelector('p').textContent.trim()?.split(' od')[0],\n date,\n company: company.join(' ')\n });\n }\n }\n }\n return premiere;\n};\n\nexport const getMovieTags = (el: HTMLElement): string[] => {\n const tagsNodes = el.querySelectorAll('.updated-box-content-tags a');\n return tagsNodes.map((tag) => tag.textContent.trim());\n};\n"],"mappings":"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"}
|
package/helpers/movie.helper.js
CHANGED
|
@@ -79,22 +79,29 @@ const getMovieTitle = (el) => {
|
|
|
79
79
|
return el.querySelector("h1").innerText.split(`(`)[0].trim();
|
|
80
80
|
};
|
|
81
81
|
const getMovieGenres = (el) => {
|
|
82
|
-
|
|
82
|
+
const genresNode = el.querySelector(".genres");
|
|
83
|
+
if (!genresNode) return [];
|
|
84
|
+
return genresNode.childNodes.map((n) => n.textContent.trim()).filter((x) => x.length > 0);
|
|
83
85
|
};
|
|
84
86
|
const getMovieOrigins = (el) => {
|
|
85
|
-
|
|
87
|
+
const originNode = el.querySelector(".origin");
|
|
88
|
+
if (!originNode) return [];
|
|
89
|
+
return (originNode.childNodes[0]?.text || "").split("/").map((x) => x.trim()).filter((x) => x);
|
|
86
90
|
};
|
|
87
91
|
const getMovieColorRating = (bodyClasses) => {
|
|
88
92
|
return getColor(bodyClasses[1]);
|
|
89
93
|
};
|
|
90
94
|
const getMovieRating = (el) => {
|
|
91
|
-
const rating = el.querySelector(".film-rating-average")
|
|
95
|
+
const rating = (el.querySelector(".film-rating-average")?.textContent)?.replace(/%/g, "")?.trim();
|
|
92
96
|
const ratingInt = parseInt(rating);
|
|
93
97
|
if (Number.isInteger(ratingInt)) return ratingInt;
|
|
94
98
|
else return null;
|
|
95
99
|
};
|
|
96
100
|
const getMovieRatingCount = (el) => {
|
|
97
|
-
const
|
|
101
|
+
const ratingCountRaw = el.querySelector(".ratings-list .counter")?.textContent;
|
|
102
|
+
if (!ratingCountRaw) return null;
|
|
103
|
+
const countString = ratingCountRaw.replace(/[^\d]/g, "");
|
|
104
|
+
const ratingCount = parseInt(countString, 10);
|
|
98
105
|
if (Number.isInteger(ratingCount)) return ratingCount;
|
|
99
106
|
else return null;
|
|
100
107
|
};
|
|
@@ -107,14 +114,19 @@ const getMovieDuration = (jsonLd, el) => {
|
|
|
107
114
|
return parseISO8601Duration(jsonLd.duration);
|
|
108
115
|
} catch (e) {}
|
|
109
116
|
try {
|
|
110
|
-
const
|
|
111
|
-
if (
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
|
|
117
|
+
const originText = el.querySelector(".origin")?.textContent;
|
|
118
|
+
if (originText) {
|
|
119
|
+
const match = originText.match(/(?:(\d+)\s*h)?\s*(\d+)\s*min/);
|
|
120
|
+
if (match) {
|
|
121
|
+
const hours = parseInt(match[1] || "0", 10);
|
|
122
|
+
const minutes = parseInt(match[2] || "0", 10);
|
|
123
|
+
return hours * 60 + minutes;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
115
126
|
} catch (error) {
|
|
116
127
|
return null;
|
|
117
128
|
}
|
|
129
|
+
return null;
|
|
118
130
|
};
|
|
119
131
|
const getMovieTitlesOther = (el) => {
|
|
120
132
|
const namesNode = el.querySelectorAll(".film-names li");
|
|
@@ -200,7 +212,7 @@ const getMovieCreators = (el, options) => {
|
|
|
200
212
|
const getSeasonsOrEpisodes = (el) => {
|
|
201
213
|
const childrenList = el.querySelector(".film-episodes-list");
|
|
202
214
|
if (!childrenList) return null;
|
|
203
|
-
const childrenNodes = childrenList.querySelectorAll(".film-title");
|
|
215
|
+
const childrenNodes = childrenList.querySelectorAll(".film-title-inline");
|
|
204
216
|
if (!childrenNodes?.length) return [];
|
|
205
217
|
return childrenNodes.map((season) => {
|
|
206
218
|
const nameContainer = season.querySelector(".film-title-name");
|
|
@@ -222,14 +234,16 @@ const getEpisodeCode = (el) => {
|
|
|
222
234
|
return match ? match[1] : null;
|
|
223
235
|
};
|
|
224
236
|
const detectSeasonOrEpisodeListType = (el) => {
|
|
225
|
-
const
|
|
237
|
+
const episodesList = el.querySelector(".film-episodes-list");
|
|
238
|
+
if (!episodesList) return null;
|
|
239
|
+
const headerText = (episodesList.closest(".updated-box") || episodesList.closest("section") || el).querySelector(".updated-box-header h3")?.textContent?.trim() ?? "";
|
|
226
240
|
if (headerText.includes("Série")) return "seasons";
|
|
227
|
-
if (headerText.
|
|
241
|
+
if (headerText.includes("Epizody")) return "episodes";
|
|
228
242
|
return null;
|
|
229
243
|
};
|
|
230
244
|
const getSeasonOrEpisodeParent = (el) => {
|
|
231
|
-
let parents = el.querySelectorAll(".film-
|
|
232
|
-
if (parents.length === 0) parents = el.querySelectorAll(".film-header h1 a");
|
|
245
|
+
let parents = el.querySelectorAll(".film-series-content h2 a");
|
|
246
|
+
if (parents.length === 0) parents = el.querySelectorAll(".film-header-name h1 a");
|
|
233
247
|
if (parents.length === 0) return null;
|
|
234
248
|
const [parentSeries, parentSeason] = parents;
|
|
235
249
|
const seriesId = parseIdFromUrl(parentSeries?.getAttribute("href"));
|
|
@@ -251,11 +265,11 @@ const getSeasonOrEpisodeParent = (el) => {
|
|
|
251
265
|
};
|
|
252
266
|
};
|
|
253
267
|
const getMovieType = (el) => {
|
|
254
|
-
return parseFilmType(el.querySelector(".film-header-name .type")?.innerText?.replace(/[{()}]/g, "") || "film");
|
|
268
|
+
return parseFilmType(el.querySelector(".film-header-name .type")?.innerText?.replace(/[{()}]/g, "").split("\n")[0].trim() || "film");
|
|
255
269
|
};
|
|
256
270
|
const getMovieVods = (el) => {
|
|
257
271
|
let vods = [];
|
|
258
|
-
if (el) vods = el.querySelectorAll(".box-
|
|
272
|
+
if (el) vods = el.querySelectorAll(".box-film-vod .vod-badge-link").map((btn) => {
|
|
259
273
|
return {
|
|
260
274
|
title: btn.textContent.trim(),
|
|
261
275
|
url: btn.attributes.href
|
|
@@ -264,7 +278,7 @@ const getMovieVods = (el) => {
|
|
|
264
278
|
return vods.length ? vods : [];
|
|
265
279
|
};
|
|
266
280
|
const getBoxContent = (el, box) => {
|
|
267
|
-
return el.querySelectorAll("section
|
|
281
|
+
return el.querySelectorAll("section .updated-box-header").find((header) => header.querySelector("h3")?.textContent.trim() === box || header.querySelector("h2")?.textContent.trim() === box)?.parentNode;
|
|
268
282
|
};
|
|
269
283
|
const getMovieBoxMovies = (el, boxName) => {
|
|
270
284
|
const movieListItem = [];
|
|
@@ -295,7 +309,7 @@ const getMoviePremieres = (el) => {
|
|
|
295
309
|
return premiere;
|
|
296
310
|
};
|
|
297
311
|
const getMovieTags = (el) => {
|
|
298
|
-
return el.querySelectorAll(".box-content a
|
|
312
|
+
return el.querySelectorAll(".updated-box-content-tags a").map((tag) => tag.textContent.trim());
|
|
299
313
|
};
|
|
300
314
|
//#endregion
|
|
301
315
|
export { detectSeasonOrEpisodeListType, getEpisodeCode, getMovieBoxMovies, getMovieColorRating, getMovieCreators, getMovieDescriptions, getMovieDuration, getMovieGenres, getMovieOrigins, getMoviePoster, getMoviePremieres, getMovieRandomPhoto, getMovieRating, getMovieRatingCount, getMovieTags, getMovieTitle, getMovieTitlesOther, getMovieTrivia, getMovieType, getMovieVods, getMovieYear, getSeasonOrEpisodeParent, getSeasonsOrEpisodes, getSeriesAndSeasonTitle };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"movie.helper.js","names":[],"sources":["../../src/helpers/movie.helper.ts"],"sourcesContent":["import { HTMLElement } from 'node-html-parser';\nimport type { CSFDColorRating, CSFDFilmTypes } from '../dto/global';\nimport type {\n CSFDBoxContent,\n CSFDCreatorGroups,\n CSFDCreatorGroupsEnglish,\n CSFDCreatorGroupsSlovak,\n CSFDCreators,\n CSFDGenres,\n CSFDMovieCreator,\n CSFDMovieListItem,\n CSFDParent,\n CSFDPremiere,\n CSFDSeriesChild,\n CSFDTitlesOther,\n CSFDVod,\n CSFDVodService,\n MovieJsonLd\n} from '../dto/movie';\nimport type { CSFDOptions } from '../types';\nimport {\n addProtocol,\n getColor,\n parseDate,\n parseFilmType,\n parseISO8601Duration,\n parseIdFromUrl,\n parseLastIdFromUrl\n} from './global.helper';\n\nconst CREATOR_LABELS: Record<\n string,\n Record<string, CSFDCreatorGroups | CSFDCreatorGroupsEnglish | CSFDCreatorGroupsSlovak>\n> = {\n en: {\n directors: 'Directed by',\n writers: 'Screenplay',\n cinematography: 'Cinematography',\n music: 'Composer',\n actors: 'Cast',\n basedOn: 'Based on',\n producers: 'Produced by',\n filmEditing: 'Editing',\n costumeDesign: 'Costumes',\n productionDesign: 'Production design',\n casting: 'Casting',\n sound: 'Sound',\n makeup: 'Make-up'\n },\n cs: {\n directors: 'Režie',\n writers: 'Scénář',\n cinematography: 'Kamera',\n music: 'Hudba',\n actors: 'Hrají',\n basedOn: 'Předloha',\n producers: 'Produkce',\n filmEditing: 'Střih',\n costumeDesign: 'Kostýmy',\n productionDesign: 'Scénografie',\n casting: 'Casting',\n sound: 'Zvuk',\n makeup: 'Masky'\n },\n sk: {\n directors: 'Réžia',\n writers: 'Scenár',\n cinematography: 'Kamera',\n music: 'Hudba',\n actors: 'Hrajú',\n basedOn: 'Predloha',\n producers: 'Produkcia',\n filmEditing: 'Strih',\n costumeDesign: 'Kostýmy',\n productionDesign: 'Scénografia',\n casting: 'Casting',\n sound: 'Zvuk',\n makeup: 'Masky'\n }\n};\n\n/**\n * Maps language-specific movie creator group labels.\n * @param language - The language code (e.g., 'en', 'cs')\n * @param key - The key of the creator group (e.g., 'directors', 'writers')\n * @returns The localized label for the creator group\n */\nexport const getLocalizedCreatorLabel = (\n language: string | undefined,\n key:\n | 'directors'\n | 'writers'\n | 'cinematography'\n | 'music'\n | 'actors'\n | 'basedOn'\n | 'producers'\n | 'filmEditing'\n | 'costumeDesign'\n | 'productionDesign'\n | 'casting'\n | 'sound'\n | 'makeup'\n): CSFDCreatorGroups | CSFDCreatorGroupsEnglish | CSFDCreatorGroupsSlovak => {\n const lang = language || 'cs'; // Default to Czech\n return (CREATOR_LABELS[lang] || CREATOR_LABELS['cs'])[key];\n};\n\nexport const getMovieId = (el: HTMLElement): number => {\n const url = el.querySelector('.tabs .tab-nav-list a').attributes.href;\n return parseIdFromUrl(url);\n};\n\nexport const getSeriesAndSeasonTitle = (\n el: HTMLElement\n): { seriesName: string | null; seasonName: string | null } => {\n const titleElement = el.querySelector('h1');\n if (!titleElement) {\n return { seriesName: null, seasonName: null };\n }\n\n const fullText = titleElement.innerText.trim();\n\n // Check if there's a series part indicated by ' - '\n if (fullText.includes(' - ')) {\n const [seriesName, seasonName] = fullText.split(' - ').map((part) => part.trim());\n return { seriesName, seasonName };\n }\n\n // If no series part found, return just the name\n return { seriesName: fullText, seasonName: null };\n};\n\nexport const getMovieTitle = (el: HTMLElement): string => {\n return el.querySelector('h1').innerText.split(`(`)[0].trim();\n};\n\nexport const getMovieGenres = (el: HTMLElement): CSFDGenres[] => {\n const genresRaw = el.querySelector('.genres').textContent;\n return genresRaw.split(' / ') as CSFDGenres[];\n};\n\nexport const getMovieOrigins = (el: HTMLElement): string[] => {\n const originsRaw = el.querySelector('.origin').textContent;\n const origins = originsRaw.split(',')[0];\n return origins.split(' / ');\n};\n\nexport const getMovieColorRating = (bodyClasses: string[]): CSFDColorRating => {\n return getColor(bodyClasses[1]);\n};\n\nexport const getMovieRating = (el: HTMLElement): number => {\n const ratingRaw = el.querySelector('.film-rating-average').textContent;\n const rating = ratingRaw?.replace(/%/g, '').trim();\n const ratingInt = parseInt(rating);\n\n if (Number.isInteger(ratingInt)) {\n return ratingInt;\n } else {\n return null;\n }\n};\n\nexport const getMovieRatingCount = (el: HTMLElement): number => {\n const ratingCountRaw = el.querySelector('.box-rating-container .counter')?.textContent;\n const ratingCount = +ratingCountRaw?.replace(/[(\\s)]/g, '');\n if (Number.isInteger(ratingCount)) {\n return ratingCount;\n } else {\n return null;\n }\n};\n\nexport const getMovieYear = (jsonLd: MovieJsonLd | null): number => {\n if (jsonLd && jsonLd.dateCreated) {\n return +jsonLd.dateCreated;\n }\n return null;\n};\n\nexport const getMovieDuration = (jsonLd: MovieJsonLd | null, el: HTMLElement): number => {\n if (jsonLd && jsonLd.duration) {\n try {\n return parseISO8601Duration(jsonLd.duration);\n } catch (e) {\n // ignore\n }\n }\n\n try {\n const origin = el.querySelector('.origin').innerText;\n const timeString = origin.split(',');\n if (timeString.length > 2) {\n // Get last time elelment\n const timeString2 = timeString.pop().trim();\n // Clean it\n const timeRaw = timeString2.split('(')[0].trim();\n // Split by minutes and hours\n const hoursMinsRaw = timeRaw.split('min')[0];\n const hoursMins = hoursMinsRaw.split('h');\n // Resolve hours + minutes format\n const duration = hoursMins.length > 1 ? +hoursMins[0] * 60 + +hoursMins[1] : +hoursMins[0];\n return duration;\n } else {\n return null;\n }\n } catch (error) {\n return null;\n }\n};\n\nexport const getMovieTitlesOther = (el: HTMLElement): CSFDTitlesOther[] => {\n const namesNode = el.querySelectorAll('.film-names li');\n\n if (!namesNode.length) {\n return [];\n }\n\n const titlesOther = namesNode.map((el) => {\n const country = el.querySelector('img.flag').attributes.alt;\n const title = el.textContent.trim().split('\\n')[0];\n\n if (country && title) {\n return {\n country,\n title\n };\n } else {\n return null;\n }\n });\n\n return titlesOther.filter((x) => x);\n};\n\nexport const getMoviePoster = (el: HTMLElement | null): string => {\n const poster = el.querySelector('.film-posters img');\n // Resolve empty image\n if (poster) {\n if (poster.classNames?.includes('empty-image')) {\n return null;\n } else {\n // Full sized image (not thumb)\n const imageThumb = poster.attributes.src.split('?')[0];\n const image = imageThumb.replace(/\\/w140\\//, '/w1080/');\n return addProtocol(image);\n }\n } else {\n return null;\n }\n};\n\nexport const getMovieRandomPhoto = (el: HTMLElement | null): string => {\n const imageNode = el.querySelector('.gallery-item picture img');\n const image = imageNode?.attributes?.src;\n if (image) {\n return image.replace(/\\/w663\\//, '/w1326/');\n } else {\n return null;\n }\n};\n\nexport const getMovieTrivia = (el: HTMLElement | null): string[] => {\n const triviaNodes = el.querySelectorAll('.article-trivia ul li');\n if (triviaNodes?.length) {\n return triviaNodes.map((node) => node.textContent.trim().replace(/(\\r\\n|\\n|\\r|\\t)/gm, ''));\n } else {\n return null;\n }\n};\n\nexport const getMovieDescriptions = (el: HTMLElement): string[] => {\n return el\n .querySelectorAll('.body--plots .plot-full p, .body--plots .plots .plots-item p')\n .map((movie) => movie.textContent?.trim().replace(/(\\r\\n|\\n|\\r|\\t)/gm, ''));\n};\n\nconst parseMoviePeople = (el: HTMLElement): CSFDMovieCreator[] => {\n const people = el.querySelectorAll('a');\n return (\n people\n // Filter out \"more\" links\n .filter((x) => x.classNames.length === 0)\n .map((person) => {\n return {\n id: parseIdFromUrl(person.attributes.href),\n name: person.innerText.trim(),\n url: `https://www.csfd.cz${person.attributes.href}`\n };\n })\n );\n};\n\n// export const getMovieGroup = (el: HTMLElement, group: CSFDCreatorGroups | CSFDCreatorGroupsEnglish | CSFDCreatorGroupsSlovak): CSFDMovieCreator[] => {\n// const creators = el.querySelectorAll('.creators h4');\n// const element = creators.filter((elem) => elem.textContent.trim().includes(group))[0];\n// if (element?.parentNode) {\n// return parseMoviePeople(element.parentNode as HTMLElement);\n// } else {\n// return [];\n// }\n// };\n\nexport const getMovieCreators = (el: HTMLElement, options?: CSFDOptions): CSFDCreators => {\n const creators: CSFDCreators = {\n directors: [],\n writers: [],\n cinematography: [],\n music: [],\n actors: [],\n basedOn: [],\n producers: [],\n filmEditing: [],\n costumeDesign: [],\n productionDesign: [],\n sound: []\n };\n\n const groups = el.querySelectorAll('.creators h4');\n\n const keys = [\n 'directors',\n 'writers',\n 'cinematography',\n 'music',\n 'actors',\n 'basedOn',\n 'producers',\n 'filmEditing',\n 'costumeDesign',\n 'productionDesign',\n 'sound'\n ] as const;\n\n const localizedLabels = keys.map((key) => ({\n key,\n label: getLocalizedCreatorLabel(options?.language, key) as string\n }));\n\n for (const group of groups) {\n const text = group.textContent.trim();\n for (const { key, label } of localizedLabels) {\n if (text.includes(label)) {\n if (group.parentNode) {\n creators[key] = parseMoviePeople(group.parentNode as HTMLElement);\n }\n break;\n }\n }\n }\n\n return creators;\n};\n\nexport const getSeasonsOrEpisodes = (el: HTMLElement): CSFDSeriesChild[] | null => {\n const childrenList = el.querySelector('.film-episodes-list');\n if (!childrenList) return null;\n\n const childrenNodes = childrenList.querySelectorAll('.film-title');\n if (!childrenNodes?.length) return [];\n\n return childrenNodes.map((season) => {\n const nameContainer = season.querySelector('.film-title-name');\n const infoContainer = season.querySelector('.info');\n\n const href = nameContainer?.getAttribute('href');\n const url = href ? (href.startsWith('/') ? `https://www.csfd.cz${href}` : href) : null;\n\n return {\n id: parseLastIdFromUrl(href || ''),\n title: nameContainer?.textContent?.trim() || null,\n url,\n info: infoContainer?.textContent?.replace(/[{()}]/g, '').trim() || null\n };\n });\n};\n\nexport const getEpisodeCode = (el: HTMLElement): string | null => {\n const filmHeaderName = el.querySelector('.film-header-name h1');\n if (!filmHeaderName) return null;\n\n const text = filmHeaderName.textContent?.trim() || '';\n const match = text.match(/\\(([^)]+)\\)/);\n const code = match ? match[1] : null;\n\n return code;\n};\n\nexport const detectSeasonOrEpisodeListType = (el: HTMLElement) => {\n const headerText = el.querySelector('.box-header h3')?.innerText.trim() ?? '';\n\n if (headerText.includes('Série')) return 'seasons';\n if (headerText.startsWith('Epizody')) return 'episodes';\n return null;\n};\n\nexport const getSeasonOrEpisodeParent = (el: HTMLElement): CSFDParent | null => {\n // Try h2 first (for episodes), then h1 (for seasons)\n let parents = el.querySelectorAll('.film-header h2 a');\n if (parents.length === 0) {\n parents = el.querySelectorAll('.film-header h1 a');\n }\n\n if (parents.length === 0) return null;\n\n const [parentSeries, parentSeason] = parents;\n\n const seriesId = parseIdFromUrl(parentSeries?.getAttribute('href'));\n const seasonId = parseLastIdFromUrl(parentSeason?.getAttribute('href') || '');\n const seriesTitle = parentSeries?.textContent?.trim() || null;\n const seasonTitle = parentSeason?.textContent?.trim() || null;\n\n const series = seriesId && seriesTitle ? { id: seriesId, title: seriesTitle } : null;\n const season = seasonId && seasonTitle ? { id: seasonId, title: seasonTitle } : null;\n\n if (!series && !season) return null;\n\n return { series, season };\n};\n\nexport const getMovieGroup = (\n el: HTMLElement,\n group: CSFDCreatorGroups | CSFDCreatorGroupsEnglish | CSFDCreatorGroupsSlovak\n): CSFDMovieCreator[] => {\n const creators = el.querySelectorAll('.creators h4');\n const element = creators.find((elem) => elem.textContent.trim().includes(group as string));\n if (element?.parentNode) {\n return parseMoviePeople(element.parentNode as HTMLElement);\n } else {\n return [];\n }\n};\n\nexport const getMovieType = (el: HTMLElement): CSFDFilmTypes => {\n const type = el.querySelector('.film-header-name .type');\n return parseFilmType(type?.innerText?.replace(/[{()}]/g, '') || 'film');\n};\n\nexport const getMovieVods = (el: HTMLElement | null): CSFDVod[] => {\n let vods: CSFDVod[] = [];\n if (el) {\n const buttonsVod = el.querySelectorAll('.box-buttons-vod .vod-badge a');\n vods = buttonsVod.map((btn) => {\n return {\n title: btn.textContent.trim() as CSFDVodService,\n url: btn.attributes.href\n };\n });\n }\n return vods.length ? vods : [];\n};\n\n// Get box content\nconst getBoxContent = (el: HTMLElement, box: string): HTMLElement => {\n const headers = el.querySelectorAll('section.box .box-header');\n return headers.find((header) => header.querySelector('h3')?.textContent.trim().includes(box))\n ?.parentNode;\n};\n\nexport const getMovieBoxMovies = (\n el: HTMLElement,\n boxName: CSFDBoxContent\n): CSFDMovieListItem[] => {\n const movieListItem: CSFDMovieListItem[] = [];\n const box = getBoxContent(el, boxName);\n const movieTitleNodes = box?.querySelectorAll('.article-header .film-title-name');\n if (movieTitleNodes?.length) {\n for (const item of movieTitleNodes) {\n movieListItem.push({\n id: parseIdFromUrl(item.attributes.href),\n title: item.textContent.trim(),\n url: `https://www.csfd.cz${item.attributes.href}`\n });\n }\n }\n return movieListItem;\n};\n\nexport const getMoviePremieres = (el: HTMLElement): CSFDPremiere[] => {\n const premiereNodes = el.querySelectorAll('.box-premieres li');\n const premiere: CSFDPremiere[] = [];\n for (const premiereNode of premiereNodes) {\n const title = premiereNode.querySelector('p + span').attributes.title;\n\n if (title) {\n const [dateRaw, ...company] = title?.split(' ');\n const date = parseDate(dateRaw);\n\n if (date) {\n premiere.push({\n country: premiereNode.querySelector('.flag')?.attributes.title || null,\n format: premiereNode.querySelector('p').textContent.trim()?.split(' od')[0],\n date,\n company: company.join(' ')\n });\n }\n }\n }\n return premiere;\n};\n\nexport const getMovieTags = (el: HTMLElement): string[] => {\n const tagsRaw = el.querySelectorAll('.box-content a[href*=\"/tag/\"]');\n return tagsRaw.map((tag) => tag.textContent);\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"movie.helper.js","names":[],"sources":["../../src/helpers/movie.helper.ts"],"sourcesContent":["import { HTMLElement } from 'node-html-parser';\nimport type { CSFDColorRating, CSFDFilmTypes } from '../dto/global';\nimport type {\n CSFDBoxContent,\n CSFDCreatorGroups,\n CSFDCreatorGroupsEnglish,\n CSFDCreatorGroupsSlovak,\n CSFDCreators,\n CSFDGenres,\n CSFDMovieCreator,\n CSFDMovieListItem,\n CSFDParent,\n CSFDPremiere,\n CSFDSeriesChild,\n CSFDTitlesOther,\n CSFDVod,\n CSFDVodService,\n MovieJsonLd\n} from '../dto/movie';\nimport type { CSFDOptions } from '../types';\nimport {\n addProtocol,\n getColor,\n parseDate,\n parseFilmType,\n parseISO8601Duration,\n parseIdFromUrl,\n parseLastIdFromUrl\n} from './global.helper';\n\nconst CREATOR_LABELS: Record<\n string,\n Record<string, CSFDCreatorGroups | CSFDCreatorGroupsEnglish | CSFDCreatorGroupsSlovak>\n> = {\n en: {\n directors: 'Directed by',\n writers: 'Screenplay',\n cinematography: 'Cinematography',\n music: 'Composer',\n actors: 'Cast',\n basedOn: 'Based on',\n producers: 'Produced by',\n filmEditing: 'Editing',\n costumeDesign: 'Costumes',\n productionDesign: 'Production design',\n casting: 'Casting',\n sound: 'Sound',\n makeup: 'Make-up'\n },\n cs: {\n directors: 'Režie',\n writers: 'Scénář',\n cinematography: 'Kamera',\n music: 'Hudba',\n actors: 'Hrají',\n basedOn: 'Předloha',\n producers: 'Produkce',\n filmEditing: 'Střih',\n costumeDesign: 'Kostýmy',\n productionDesign: 'Scénografie',\n casting: 'Casting',\n sound: 'Zvuk',\n makeup: 'Masky'\n },\n sk: {\n directors: 'Réžia',\n writers: 'Scenár',\n cinematography: 'Kamera',\n music: 'Hudba',\n actors: 'Hrajú',\n basedOn: 'Predloha',\n producers: 'Produkcia',\n filmEditing: 'Strih',\n costumeDesign: 'Kostýmy',\n productionDesign: 'Scénografia',\n casting: 'Casting',\n sound: 'Zvuk',\n makeup: 'Masky'\n }\n};\n\n/**\n * Maps language-specific movie creator group labels.\n * @param language - The language code (e.g., 'en', 'cs')\n * @param key - The key of the creator group (e.g., 'directors', 'writers')\n * @returns The localized label for the creator group\n */\nexport const getLocalizedCreatorLabel = (\n language: string | undefined,\n key:\n | 'directors'\n | 'writers'\n | 'cinematography'\n | 'music'\n | 'actors'\n | 'basedOn'\n | 'producers'\n | 'filmEditing'\n | 'costumeDesign'\n | 'productionDesign'\n | 'casting'\n | 'sound'\n | 'makeup'\n): CSFDCreatorGroups | CSFDCreatorGroupsEnglish | CSFDCreatorGroupsSlovak => {\n const lang = language || 'cs'; // Default to Czech\n return (CREATOR_LABELS[lang] || CREATOR_LABELS['cs'])[key];\n};\n\nexport const getMovieId = (el: HTMLElement): number => {\n const url = el.querySelector('.tabs .tab-nav-list a').attributes.href;\n return parseIdFromUrl(url);\n};\n\nexport const getSeriesAndSeasonTitle = (\n el: HTMLElement\n): { seriesName: string | null; seasonName: string | null } => {\n const titleElement = el.querySelector('h1');\n if (!titleElement) {\n return { seriesName: null, seasonName: null };\n }\n\n const fullText = titleElement.innerText.trim();\n\n // Check if there's a series part indicated by ' - '\n if (fullText.includes(' - ')) {\n const [seriesName, seasonName] = fullText.split(' - ').map((part) => part.trim());\n return { seriesName, seasonName };\n }\n\n // If no series part found, return just the name\n return { seriesName: fullText, seasonName: null };\n};\n\nexport const getMovieTitle = (el: HTMLElement): string => {\n return el.querySelector('h1').innerText.split(`(`)[0].trim();\n};\n\nexport const getMovieGenres = (el: HTMLElement): CSFDGenres[] => {\n const genresNode = el.querySelector('.genres');\n if (!genresNode) return [];\n return genresNode.childNodes\n .map((n) => n.textContent.trim())\n .filter((x) => x.length > 0) as CSFDGenres[];\n};\n\nexport const getMovieOrigins = (el: HTMLElement): string[] => {\n const originNode = el.querySelector('.origin');\n if (!originNode) return [];\n const text = originNode.childNodes[0]?.text || '';\n return text.split('/').map(x => x.trim()).filter(x => x);\n};\n\nexport const getMovieColorRating = (bodyClasses: string[]): CSFDColorRating => {\n return getColor(bodyClasses[1]);\n};\n\nexport const getMovieRating = (el: HTMLElement): number => {\n const ratingRaw = el.querySelector('.film-rating-average')?.textContent;\n const rating = ratingRaw?.replace(/%/g, '')?.trim();\n const ratingInt = parseInt(rating);\n\n if (Number.isInteger(ratingInt)) {\n return ratingInt;\n } else {\n return null;\n }\n};\n\nexport const getMovieRatingCount = (el: HTMLElement): number => {\n const ratingCountRaw = el.querySelector('.ratings-list .counter')?.textContent;\n if (!ratingCountRaw) return null;\n const countString = ratingCountRaw.replace(/[^\\d]/g, '');\n const ratingCount = parseInt(countString, 10);\n if (Number.isInteger(ratingCount)) {\n return ratingCount;\n } else {\n return null;\n }\n};\n\nexport const getMovieYear = (jsonLd: MovieJsonLd | null): number => {\n if (jsonLd && jsonLd.dateCreated) {\n return +jsonLd.dateCreated;\n }\n return null;\n};\n\nexport const getMovieDuration = (jsonLd: MovieJsonLd | null, el: HTMLElement): number => {\n if (jsonLd && jsonLd.duration) {\n try {\n return parseISO8601Duration(jsonLd.duration);\n } catch (e) {\n // ignore\n }\n }\n\n try {\n const originText = el.querySelector('.origin')?.textContent;\n if (originText) {\n const match = originText.match(/(?:(\\d+)\\s*h)?\\s*(\\d+)\\s*min/);\n if (match) {\n const hours = parseInt(match[1] || '0', 10);\n const minutes = parseInt(match[2] || '0', 10);\n return hours * 60 + minutes;\n }\n }\n } catch (error) {\n return null;\n }\n return null;\n};\n\nexport const getMovieTitlesOther = (el: HTMLElement): CSFDTitlesOther[] => {\n const namesNode = el.querySelectorAll('.film-names li');\n\n if (!namesNode.length) {\n return [];\n }\n\n const titlesOther = namesNode.map((el) => {\n const country = el.querySelector('img.flag').attributes.alt;\n const title = el.textContent.trim().split('\\n')[0];\n\n if (country && title) {\n return {\n country,\n title\n };\n } else {\n return null;\n }\n });\n\n return titlesOther.filter((x) => x);\n};\n\nexport const getMoviePoster = (el: HTMLElement | null): string => {\n const poster = el.querySelector('.film-posters img');\n // Resolve empty image\n if (poster) {\n if (poster.classNames?.includes('empty-image')) {\n return null;\n } else {\n // Full sized image (not thumb)\n const imageThumb = poster.attributes.src.split('?')[0];\n const image = imageThumb.replace(/\\/w140\\//, '/w1080/');\n return addProtocol(image);\n }\n } else {\n return null;\n }\n};\n\nexport const getMovieRandomPhoto = (el: HTMLElement | null): string => {\n const imageNode = el.querySelector('.gallery-item picture img');\n const image = imageNode?.attributes?.src;\n if (image) {\n return image.replace(/\\/w663\\//, '/w1326/');\n } else {\n return null;\n }\n};\n\nexport const getMovieTrivia = (el: HTMLElement | null): string[] => {\n const triviaNodes = el.querySelectorAll('.article-trivia ul li');\n if (triviaNodes?.length) {\n return triviaNodes.map((node) => node.textContent.trim().replace(/(\\r\\n|\\n|\\r|\\t)/gm, ''));\n } else {\n return null;\n }\n};\n\nexport const getMovieDescriptions = (el: HTMLElement): string[] => {\n return el\n .querySelectorAll('.body--plots .plot-full p, .body--plots .plots .plots-item p')\n .map((movie) => movie.textContent?.trim().replace(/(\\r\\n|\\n|\\r|\\t)/gm, ''));\n};\n\nconst parseMoviePeople = (el: HTMLElement): CSFDMovieCreator[] => {\n const people = el.querySelectorAll('a');\n return (\n people\n // Filter out \"more\" links\n .filter((x) => x.classNames.length === 0)\n .map((person) => {\n return {\n id: parseIdFromUrl(person.attributes.href),\n name: person.innerText.trim(),\n url: `https://www.csfd.cz${person.attributes.href}`\n };\n })\n );\n};\n\n// export const getMovieGroup = (el: HTMLElement, group: CSFDCreatorGroups | CSFDCreatorGroupsEnglish | CSFDCreatorGroupsSlovak): CSFDMovieCreator[] => {\n// const creators = el.querySelectorAll('.creators h4');\n// const element = creators.filter((elem) => elem.textContent.trim().includes(group))[0];\n// if (element?.parentNode) {\n// return parseMoviePeople(element.parentNode as HTMLElement);\n// } else {\n// return [];\n// }\n// };\n\nexport const getMovieCreators = (el: HTMLElement, options?: CSFDOptions): CSFDCreators => {\n const creators: CSFDCreators = {\n directors: [],\n writers: [],\n cinematography: [],\n music: [],\n actors: [],\n basedOn: [],\n producers: [],\n filmEditing: [],\n costumeDesign: [],\n productionDesign: [],\n sound: []\n };\n\n const groups = el.querySelectorAll('.creators h4');\n\n const keys = [\n 'directors',\n 'writers',\n 'cinematography',\n 'music',\n 'actors',\n 'basedOn',\n 'producers',\n 'filmEditing',\n 'costumeDesign',\n 'productionDesign',\n 'sound'\n ] as const;\n\n const localizedLabels = keys.map((key) => ({\n key,\n label: getLocalizedCreatorLabel(options?.language, key) as string\n }));\n\n for (const group of groups) {\n const text = group.textContent.trim();\n for (const { key, label } of localizedLabels) {\n if (text.includes(label)) {\n if (group.parentNode) {\n creators[key] = parseMoviePeople(group.parentNode as HTMLElement);\n }\n break;\n }\n }\n }\n\n return creators;\n};\n\nexport const getSeasonsOrEpisodes = (el: HTMLElement): CSFDSeriesChild[] | null => {\n const childrenList = el.querySelector('.film-episodes-list');\n if (!childrenList) return null;\n\n const childrenNodes = childrenList.querySelectorAll('.film-title-inline');\n if (!childrenNodes?.length) return [];\n\n return childrenNodes.map((season) => {\n const nameContainer = season.querySelector('.film-title-name');\n const infoContainer = season.querySelector('.info');\n\n const href = nameContainer?.getAttribute('href');\n const url = href ? (href.startsWith('/') ? `https://www.csfd.cz${href}` : href) : null;\n\n return {\n id: parseLastIdFromUrl(href || ''),\n title: nameContainer?.textContent?.trim() || null,\n url,\n info: infoContainer?.textContent?.replace(/[{()}]/g, '').trim() || null\n };\n });\n};\n\nexport const getEpisodeCode = (el: HTMLElement): string | null => {\n const filmHeaderName = el.querySelector('.film-header-name h1');\n if (!filmHeaderName) return null;\n\n const text = filmHeaderName.textContent?.trim() || '';\n const match = text.match(/\\(([^)]+)\\)/);\n const code = match ? match[1] : null;\n\n return code;\n};\n\nexport const detectSeasonOrEpisodeListType = (el: HTMLElement) => {\n const episodesList = el.querySelector('.film-episodes-list');\n if (!episodesList) return null;\n\n const section = episodesList.closest('.updated-box') || episodesList.closest('section') || el;\n const headerText = section.querySelector('.updated-box-header h3')?.textContent?.trim() ?? '';\n\n if (headerText.includes('Série')) return 'seasons';\n if (headerText.includes('Epizody')) return 'episodes';\n return null;\n};\n\nexport const getSeasonOrEpisodeParent = (el: HTMLElement): CSFDParent | null => {\n let parents = el.querySelectorAll('.film-series-content h2 a');\n if (parents.length === 0) parents = el.querySelectorAll('.film-header-name h1 a');\n if (parents.length === 0) return null;\n\n const [parentSeries, parentSeason] = parents;\n\n const seriesId = parseIdFromUrl(parentSeries?.getAttribute('href'));\n const seasonId = parseLastIdFromUrl(parentSeason?.getAttribute('href') || '');\n const seriesTitle = parentSeries?.textContent?.trim() || null;\n const seasonTitle = parentSeason?.textContent?.trim() || null;\n\n const series = seriesId && seriesTitle ? { id: seriesId, title: seriesTitle } : null;\n const season = seasonId && seasonTitle ? { id: seasonId, title: seasonTitle } : null;\n\n if (!series && !season) return null;\n\n return { series, season };\n};\n\nexport const getMovieGroup = (\n el: HTMLElement,\n group: CSFDCreatorGroups | CSFDCreatorGroupsEnglish | CSFDCreatorGroupsSlovak\n): CSFDMovieCreator[] => {\n const creators = el.querySelectorAll('.creators h4');\n const element = creators.find((elem) => elem.textContent.trim().includes(group as string));\n if (element?.parentNode) {\n return parseMoviePeople(element.parentNode as HTMLElement);\n } else {\n return [];\n }\n};\n\nexport const getMovieType = (el: HTMLElement): CSFDFilmTypes => {\n const type = el.querySelector('.film-header-name .type');\n const text = type?.innerText?.replace(/[{()}]/g, '').split('\\n')[0].trim() || 'film';\n return parseFilmType(text);\n};\n\nexport const getMovieVods = (el: HTMLElement | null): CSFDVod[] => {\n let vods: CSFDVod[] = [];\n if (el) {\n const buttonsVod = el.querySelectorAll('.box-film-vod .vod-badge-link');\n vods = buttonsVod.map((btn) => {\n return {\n title: btn.textContent.trim() as CSFDVodService,\n url: btn.attributes.href\n };\n });\n }\n return vods.length ? vods : [];\n};\n\n// Get box content\nconst getBoxContent = (el: HTMLElement, box: string): HTMLElement => {\n const headers = el.querySelectorAll('section .updated-box-header');\n return headers.find(\n (header) =>\n header.querySelector('h3')?.textContent.trim() === box ||\n header.querySelector('h2')?.textContent.trim() === box\n )?.parentNode;\n};\n\nexport const getMovieBoxMovies = (\n el: HTMLElement,\n boxName: CSFDBoxContent\n): CSFDMovieListItem[] => {\n const movieListItem: CSFDMovieListItem[] = [];\n const box = getBoxContent(el, boxName);\n const movieTitleNodes = box?.querySelectorAll('.article-header .film-title-name');\n if (movieTitleNodes?.length) {\n for (const item of movieTitleNodes) {\n movieListItem.push({\n id: parseIdFromUrl(item.attributes.href),\n title: item.textContent.trim(),\n url: `https://www.csfd.cz${item.attributes.href}`\n });\n }\n }\n return movieListItem;\n};\n\nexport const getMoviePremieres = (el: HTMLElement): CSFDPremiere[] => {\n const premiereNodes = el.querySelectorAll('.box-premieres li');\n const premiere: CSFDPremiere[] = [];\n for (const premiereNode of premiereNodes) {\n const title = premiereNode.querySelector('p + span').attributes.title;\n\n if (title) {\n const [dateRaw, ...company] = title?.split(' ');\n const date = parseDate(dateRaw);\n\n if (date) {\n premiere.push({\n country: premiereNode.querySelector('.flag')?.attributes.title || null,\n format: premiereNode.querySelector('p').textContent.trim()?.split(' od')[0],\n date,\n company: company.join(' ')\n });\n }\n }\n }\n return premiere;\n};\n\nexport const getMovieTags = (el: HTMLElement): string[] => {\n const tagsNodes = el.querySelectorAll('.updated-box-content-tags a');\n return tagsNodes.map((tag) => tag.textContent.trim());\n};\n"],"mappings":"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"}
|
package/package.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-csfd-api",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.8.0",
|
|
4
4
|
"description": "ČSFD API in JavaScript. Amazing NPM library for scrapping csfd.cz :)",
|
|
5
5
|
"author": "BART! <bart@bartweb.cz>",
|
|
6
6
|
"publishConfig": {
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"registry": "https://registry.npmjs.org"
|
|
9
9
|
},
|
|
10
10
|
"dependencies": {
|
|
11
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
11
|
+
"@modelcontextprotocol/sdk": "^1.28.0",
|
|
12
12
|
"dotenv": "^17.3.1",
|
|
13
13
|
"express": "^5.2.1",
|
|
14
14
|
"node-html-parser": "^7.1.0",
|
|
@@ -11,19 +11,19 @@ var MovieScraper = class {
|
|
|
11
11
|
const pageClasses = movieHtml.querySelector(".page-content").classNames.split(" ");
|
|
12
12
|
const asideNode = movieHtml.querySelector(".aside-movie-profile");
|
|
13
13
|
const movieNode = movieHtml.querySelector(".main-movie-profile");
|
|
14
|
-
const jsonLdString = movieHtml.querySelector("script[type=\"application/ld+json\"]")
|
|
14
|
+
const jsonLdString = movieHtml.querySelector("script[type=\"application/ld+json\"]")?.textContent;
|
|
15
15
|
let jsonLd = null;
|
|
16
16
|
try {
|
|
17
17
|
jsonLd = JSON.parse(jsonLdString);
|
|
18
18
|
} catch (e) {
|
|
19
19
|
console.error(require_vars.LIB_PREFIX + " Error parsing JSON-LD", e);
|
|
20
20
|
}
|
|
21
|
-
return this.buildMovie(+movieId, movieNode, asideNode, pageClasses, jsonLd, options);
|
|
21
|
+
return this.buildMovie(+movieId, movieHtml, movieNode, asideNode, pageClasses, jsonLd, options);
|
|
22
22
|
}
|
|
23
|
-
buildMovie(movieId, el, asideEl, pageClasses, jsonLd, options) {
|
|
23
|
+
buildMovie(movieId, movieHtml, el, asideEl, pageClasses, jsonLd, options) {
|
|
24
24
|
const type = require_movie_helper.getMovieType(el);
|
|
25
25
|
const { seriesName = null, seasonName = null } = type === "season" ? require_movie_helper.getSeriesAndSeasonTitle(el) : {};
|
|
26
|
-
const seasonOrEpisodeListType = require_movie_helper.detectSeasonOrEpisodeListType(
|
|
26
|
+
const seasonOrEpisodeListType = require_movie_helper.detectSeasonOrEpisodeListType(movieHtml);
|
|
27
27
|
return {
|
|
28
28
|
id: movieId,
|
|
29
29
|
title: type === "season" && seriesName ? seriesName : require_movie_helper.getMovieTitle(el),
|
|
@@ -42,13 +42,13 @@ var MovieScraper = class {
|
|
|
42
42
|
photo: require_movie_helper.getMovieRandomPhoto(el),
|
|
43
43
|
trivia: require_movie_helper.getMovieTrivia(el),
|
|
44
44
|
creators: require_movie_helper.getMovieCreators(el, options),
|
|
45
|
-
vod: require_movie_helper.getMovieVods(
|
|
45
|
+
vod: require_movie_helper.getMovieVods(el),
|
|
46
46
|
tags: require_movie_helper.getMovieTags(asideEl),
|
|
47
47
|
premieres: require_movie_helper.getMoviePremieres(asideEl),
|
|
48
48
|
related: require_movie_helper.getMovieBoxMovies(asideEl, "Související"),
|
|
49
49
|
similar: require_movie_helper.getMovieBoxMovies(asideEl, "Podobné"),
|
|
50
|
-
seasons: seasonOrEpisodeListType === "seasons" ? require_movie_helper.getSeasonsOrEpisodes(
|
|
51
|
-
episodes: seasonOrEpisodeListType === "episodes" ? require_movie_helper.getSeasonsOrEpisodes(
|
|
50
|
+
seasons: seasonOrEpisodeListType === "seasons" ? require_movie_helper.getSeasonsOrEpisodes(movieHtml) : null,
|
|
51
|
+
episodes: seasonOrEpisodeListType === "episodes" ? require_movie_helper.getSeasonsOrEpisodes(movieHtml) : null,
|
|
52
52
|
parent: type === "season" || type === "episode" ? require_movie_helper.getSeasonOrEpisodeParent(el) : null,
|
|
53
53
|
episodeCode: type === "episode" ? require_movie_helper.getEpisodeCode(el) : null,
|
|
54
54
|
seasonName
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"movie.service.cjs","names":["fetchPage","movieUrl","LIB_PREFIX","getMovieType","getSeriesAndSeasonTitle","detectSeasonOrEpisodeListType","getMovieTitle","getMovieYear","getMovieDuration","getMovieDescriptions","getMovieGenres","getMovieOrigins","getMovieColorRating","getMovieRating","getMovieRatingCount","getMovieTitlesOther","getMoviePoster","getMovieRandomPhoto","getMovieTrivia","getMovieCreators","getMovieVods","getMovieTags","getMoviePremieres","getMovieBoxMovies","getSeasonsOrEpisodes","getSeasonOrEpisodeParent","getEpisodeCode"],"sources":["../../src/services/movie.service.ts"],"sourcesContent":["import { HTMLElement, parse } from 'node-html-parser';\nimport { CSFDFilmTypes } from '../dto/global';\nimport { CSFDMovie, MovieJsonLd } from '../dto/movie';\nimport { fetchPage } from '../fetchers';\nimport {\n detectSeasonOrEpisodeListType,\n getEpisodeCode,\n getMovieBoxMovies,\n getMovieColorRating,\n getMovieCreators,\n getMovieDescriptions,\n getMovieDuration,\n getMovieGenres,\n getMovieOrigins,\n getMoviePoster,\n getMoviePremieres,\n getMovieRandomPhoto,\n getMovieRating,\n getMovieRatingCount,\n getMovieTags,\n getMovieTitle,\n getMovieTitlesOther,\n getMovieTrivia,\n getMovieType,\n getMovieVods,\n getMovieYear,\n getSeasonOrEpisodeParent,\n getSeasonsOrEpisodes,\n getSeriesAndSeasonTitle\n} from '../helpers/movie.helper';\nimport { CSFDOptions } from '../types';\nimport { LIB_PREFIX, movieUrl } from '../vars';\n\nexport class MovieScraper {\n public async movie(movieId: number, options?: CSFDOptions): Promise<CSFDMovie> {\n const id = Number(movieId);\n if (isNaN(id)) {\n throw new Error('node-csfd-api: movieId must be a valid number');\n }\n const url = movieUrl(id, { language: options?.language });\n const response = await fetchPage(url, { ...options?.request });\n\n const movieHtml = parse(response);\n\n const pageClasses = movieHtml.querySelector('.page-content').classNames.split(' ');\n const asideNode = movieHtml.querySelector('.aside-movie-profile');\n const movieNode = movieHtml.querySelector('.main-movie-profile');\n const jsonLdString = movieHtml.querySelector('script[type=\"application/ld+json\"]')
|
|
1
|
+
{"version":3,"file":"movie.service.cjs","names":["fetchPage","movieUrl","LIB_PREFIX","getMovieType","getSeriesAndSeasonTitle","detectSeasonOrEpisodeListType","getMovieTitle","getMovieYear","getMovieDuration","getMovieDescriptions","getMovieGenres","getMovieOrigins","getMovieColorRating","getMovieRating","getMovieRatingCount","getMovieTitlesOther","getMoviePoster","getMovieRandomPhoto","getMovieTrivia","getMovieCreators","getMovieVods","getMovieTags","getMoviePremieres","getMovieBoxMovies","getSeasonsOrEpisodes","getSeasonOrEpisodeParent","getEpisodeCode"],"sources":["../../src/services/movie.service.ts"],"sourcesContent":["import { HTMLElement, parse } from 'node-html-parser';\nimport { CSFDFilmTypes } from '../dto/global';\nimport { CSFDMovie, MovieJsonLd } from '../dto/movie';\nimport { fetchPage } from '../fetchers';\nimport {\n detectSeasonOrEpisodeListType,\n getEpisodeCode,\n getMovieBoxMovies,\n getMovieColorRating,\n getMovieCreators,\n getMovieDescriptions,\n getMovieDuration,\n getMovieGenres,\n getMovieOrigins,\n getMoviePoster,\n getMoviePremieres,\n getMovieRandomPhoto,\n getMovieRating,\n getMovieRatingCount,\n getMovieTags,\n getMovieTitle,\n getMovieTitlesOther,\n getMovieTrivia,\n getMovieType,\n getMovieVods,\n getMovieYear,\n getSeasonOrEpisodeParent,\n getSeasonsOrEpisodes,\n getSeriesAndSeasonTitle\n} from '../helpers/movie.helper';\nimport { CSFDOptions } from '../types';\nimport { LIB_PREFIX, movieUrl } from '../vars';\n\nexport class MovieScraper {\n public async movie(movieId: number, options?: CSFDOptions): Promise<CSFDMovie> {\n const id = Number(movieId);\n if (isNaN(id)) {\n throw new Error('node-csfd-api: movieId must be a valid number');\n }\n const url = movieUrl(id, { language: options?.language });\n const response = await fetchPage(url, { ...options?.request });\n\n const movieHtml = parse(response);\n\n const pageClasses = movieHtml.querySelector('.page-content').classNames.split(' ');\n const asideNode = movieHtml.querySelector('.aside-movie-profile');\n const movieNode = movieHtml.querySelector('.main-movie-profile');\n const jsonLdString = movieHtml.querySelector('script[type=\"application/ld+json\"]')?.textContent;\n let jsonLd: MovieJsonLd | null = null;\n try {\n jsonLd = JSON.parse(jsonLdString);\n } catch (e) {\n console.error(LIB_PREFIX + ' Error parsing JSON-LD', e);\n }\n return this.buildMovie(+movieId, movieHtml, movieNode as HTMLElement, asideNode as HTMLElement, pageClasses, jsonLd, options);\n }\n\n private buildMovie(\n movieId: number,\n movieHtml: HTMLElement,\n el: HTMLElement,\n asideEl: HTMLElement,\n pageClasses: string[],\n jsonLd: MovieJsonLd | null,\n options: CSFDOptions\n ) {\n const type = getMovieType(el) as CSFDFilmTypes;\n const { seriesName = null, seasonName = null } =\n type === 'season' ? getSeriesAndSeasonTitle(el) : {};\n const seasonOrEpisodeListType = detectSeasonOrEpisodeListType(movieHtml);\n\n const title = type === 'season' && seriesName ? seriesName : getMovieTitle(el);\n return {\n id: movieId,\n title,\n year: getMovieYear(jsonLd),\n duration: getMovieDuration(jsonLd, el),\n descriptions: getMovieDescriptions(el),\n genres: getMovieGenres(el),\n type,\n url: movieUrl(movieId, { language: options?.language }),\n origins: getMovieOrigins(el),\n colorRating: getMovieColorRating(pageClasses),\n rating: getMovieRating(asideEl),\n ratingCount: getMovieRatingCount(asideEl),\n titlesOther: getMovieTitlesOther(el),\n poster: getMoviePoster(el),\n photo: getMovieRandomPhoto(el),\n trivia: getMovieTrivia(el),\n creators: getMovieCreators(el, options),\n vod: getMovieVods(el),\n tags: getMovieTags(asideEl),\n premieres: getMoviePremieres(asideEl),\n related: getMovieBoxMovies(asideEl, 'Související'),\n similar: getMovieBoxMovies(asideEl, 'Podobné'),\n seasons: seasonOrEpisodeListType === 'seasons' ? getSeasonsOrEpisodes(movieHtml) : null,\n episodes: seasonOrEpisodeListType === 'episodes' ? getSeasonsOrEpisodes(movieHtml) : null,\n parent: type === 'season' || type === 'episode' ? getSeasonOrEpisodeParent(el) : null,\n episodeCode: type === 'episode' ? getEpisodeCode(el) : null,\n seasonName\n };\n }\n}\n"],"mappings":";;;;;AAiCA,IAAa,eAAb,MAA0B;CACxB,MAAa,MAAM,SAAiB,SAA2C;EAC7E,MAAM,KAAK,OAAO,QAAQ;AAC1B,MAAI,MAAM,GAAG,CACX,OAAM,IAAI,MAAM,gDAAgD;EAKlE,MAAM,aAAA,GAAA,iBAAA,OAFW,MAAMA,cAAAA,UADXC,aAAAA,SAAS,IAAI,EAAE,UAAU,SAAS,UAAU,CAAC,EACnB,EAAE,GAAG,SAAS,SAAS,CAAC,CAE7B;EAEjC,MAAM,cAAc,UAAU,cAAc,gBAAgB,CAAC,WAAW,MAAM,IAAI;EAClF,MAAM,YAAY,UAAU,cAAc,uBAAuB;EACjE,MAAM,YAAY,UAAU,cAAc,sBAAsB;EAChE,MAAM,eAAe,UAAU,cAAc,uCAAqC,EAAE;EACpF,IAAI,SAA6B;AACjC,MAAI;AACF,YAAS,KAAK,MAAM,aAAa;WAC1B,GAAG;AACV,WAAQ,MAAMC,aAAAA,aAAa,0BAA0B,EAAE;;AAEzD,SAAO,KAAK,WAAW,CAAC,SAAS,WAAW,WAA0B,WAA0B,aAAa,QAAQ,QAAQ;;CAG/H,WACE,SACA,WACA,IACA,SACA,aACA,QACA,SACA;EACA,MAAM,OAAOC,qBAAAA,aAAa,GAAG;EAC7B,MAAM,EAAE,aAAa,MAAM,aAAa,SACtC,SAAS,WAAWC,qBAAAA,wBAAwB,GAAG,GAAG,EAAE;EACtD,MAAM,0BAA0BC,qBAAAA,8BAA8B,UAAU;AAGxE,SAAO;GACL,IAAI;GACJ,OAHY,SAAS,YAAY,aAAa,aAAaC,qBAAAA,cAAc,GAAG;GAI5E,MAAMC,qBAAAA,aAAa,OAAO;GAC1B,UAAUC,qBAAAA,iBAAiB,QAAQ,GAAG;GACtC,cAAcC,qBAAAA,qBAAqB,GAAG;GACtC,QAAQC,qBAAAA,eAAe,GAAG;GAC1B;GACA,KAAKT,aAAAA,SAAS,SAAS,EAAE,UAAU,SAAS,UAAU,CAAC;GACvD,SAASU,qBAAAA,gBAAgB,GAAG;GAC5B,aAAaC,qBAAAA,oBAAoB,YAAY;GAC7C,QAAQC,qBAAAA,eAAe,QAAQ;GAC/B,aAAaC,qBAAAA,oBAAoB,QAAQ;GACzC,aAAaC,qBAAAA,oBAAoB,GAAG;GACpC,QAAQC,qBAAAA,eAAe,GAAG;GAC1B,OAAOC,qBAAAA,oBAAoB,GAAG;GAC9B,QAAQC,qBAAAA,eAAe,GAAG;GAC1B,UAAUC,qBAAAA,iBAAiB,IAAI,QAAQ;GACvC,KAAKC,qBAAAA,aAAa,GAAG;GACrB,MAAMC,qBAAAA,aAAa,QAAQ;GAC3B,WAAWC,qBAAAA,kBAAkB,QAAQ;GACrC,SAASC,qBAAAA,kBAAkB,SAAS,cAAc;GAClD,SAASA,qBAAAA,kBAAkB,SAAS,UAAU;GAC9C,SAAS,4BAA4B,YAAYC,qBAAAA,qBAAqB,UAAU,GAAG;GACnF,UAAU,4BAA4B,aAAaA,qBAAAA,qBAAqB,UAAU,GAAG;GACrF,QAAQ,SAAS,YAAY,SAAS,YAAYC,qBAAAA,yBAAyB,GAAG,GAAG;GACjF,aAAa,SAAS,YAAYC,qBAAAA,eAAe,GAAG,GAAG;GACvD;GACD"}
|
|
@@ -11,19 +11,19 @@ var MovieScraper = class {
|
|
|
11
11
|
const pageClasses = movieHtml.querySelector(".page-content").classNames.split(" ");
|
|
12
12
|
const asideNode = movieHtml.querySelector(".aside-movie-profile");
|
|
13
13
|
const movieNode = movieHtml.querySelector(".main-movie-profile");
|
|
14
|
-
const jsonLdString = movieHtml.querySelector("script[type=\"application/ld+json\"]")
|
|
14
|
+
const jsonLdString = movieHtml.querySelector("script[type=\"application/ld+json\"]")?.textContent;
|
|
15
15
|
let jsonLd = null;
|
|
16
16
|
try {
|
|
17
17
|
jsonLd = JSON.parse(jsonLdString);
|
|
18
18
|
} catch (e) {
|
|
19
19
|
console.error(LIB_PREFIX + " Error parsing JSON-LD", e);
|
|
20
20
|
}
|
|
21
|
-
return this.buildMovie(+movieId, movieNode, asideNode, pageClasses, jsonLd, options);
|
|
21
|
+
return this.buildMovie(+movieId, movieHtml, movieNode, asideNode, pageClasses, jsonLd, options);
|
|
22
22
|
}
|
|
23
|
-
buildMovie(movieId, el, asideEl, pageClasses, jsonLd, options) {
|
|
23
|
+
buildMovie(movieId, movieHtml, el, asideEl, pageClasses, jsonLd, options) {
|
|
24
24
|
const type = getMovieType(el);
|
|
25
25
|
const { seriesName = null, seasonName = null } = type === "season" ? getSeriesAndSeasonTitle(el) : {};
|
|
26
|
-
const seasonOrEpisodeListType = detectSeasonOrEpisodeListType(
|
|
26
|
+
const seasonOrEpisodeListType = detectSeasonOrEpisodeListType(movieHtml);
|
|
27
27
|
return {
|
|
28
28
|
id: movieId,
|
|
29
29
|
title: type === "season" && seriesName ? seriesName : getMovieTitle(el),
|
|
@@ -42,13 +42,13 @@ var MovieScraper = class {
|
|
|
42
42
|
photo: getMovieRandomPhoto(el),
|
|
43
43
|
trivia: getMovieTrivia(el),
|
|
44
44
|
creators: getMovieCreators(el, options),
|
|
45
|
-
vod: getMovieVods(
|
|
45
|
+
vod: getMovieVods(el),
|
|
46
46
|
tags: getMovieTags(asideEl),
|
|
47
47
|
premieres: getMoviePremieres(asideEl),
|
|
48
48
|
related: getMovieBoxMovies(asideEl, "Související"),
|
|
49
49
|
similar: getMovieBoxMovies(asideEl, "Podobné"),
|
|
50
|
-
seasons: seasonOrEpisodeListType === "seasons" ? getSeasonsOrEpisodes(
|
|
51
|
-
episodes: seasonOrEpisodeListType === "episodes" ? getSeasonsOrEpisodes(
|
|
50
|
+
seasons: seasonOrEpisodeListType === "seasons" ? getSeasonsOrEpisodes(movieHtml) : null,
|
|
51
|
+
episodes: seasonOrEpisodeListType === "episodes" ? getSeasonsOrEpisodes(movieHtml) : null,
|
|
52
52
|
parent: type === "season" || type === "episode" ? getSeasonOrEpisodeParent(el) : null,
|
|
53
53
|
episodeCode: type === "episode" ? getEpisodeCode(el) : null,
|
|
54
54
|
seasonName
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"movie.service.js","names":[],"sources":["../../src/services/movie.service.ts"],"sourcesContent":["import { HTMLElement, parse } from 'node-html-parser';\nimport { CSFDFilmTypes } from '../dto/global';\nimport { CSFDMovie, MovieJsonLd } from '../dto/movie';\nimport { fetchPage } from '../fetchers';\nimport {\n detectSeasonOrEpisodeListType,\n getEpisodeCode,\n getMovieBoxMovies,\n getMovieColorRating,\n getMovieCreators,\n getMovieDescriptions,\n getMovieDuration,\n getMovieGenres,\n getMovieOrigins,\n getMoviePoster,\n getMoviePremieres,\n getMovieRandomPhoto,\n getMovieRating,\n getMovieRatingCount,\n getMovieTags,\n getMovieTitle,\n getMovieTitlesOther,\n getMovieTrivia,\n getMovieType,\n getMovieVods,\n getMovieYear,\n getSeasonOrEpisodeParent,\n getSeasonsOrEpisodes,\n getSeriesAndSeasonTitle\n} from '../helpers/movie.helper';\nimport { CSFDOptions } from '../types';\nimport { LIB_PREFIX, movieUrl } from '../vars';\n\nexport class MovieScraper {\n public async movie(movieId: number, options?: CSFDOptions): Promise<CSFDMovie> {\n const id = Number(movieId);\n if (isNaN(id)) {\n throw new Error('node-csfd-api: movieId must be a valid number');\n }\n const url = movieUrl(id, { language: options?.language });\n const response = await fetchPage(url, { ...options?.request });\n\n const movieHtml = parse(response);\n\n const pageClasses = movieHtml.querySelector('.page-content').classNames.split(' ');\n const asideNode = movieHtml.querySelector('.aside-movie-profile');\n const movieNode = movieHtml.querySelector('.main-movie-profile');\n const jsonLdString = movieHtml.querySelector('script[type=\"application/ld+json\"]')
|
|
1
|
+
{"version":3,"file":"movie.service.js","names":[],"sources":["../../src/services/movie.service.ts"],"sourcesContent":["import { HTMLElement, parse } from 'node-html-parser';\nimport { CSFDFilmTypes } from '../dto/global';\nimport { CSFDMovie, MovieJsonLd } from '../dto/movie';\nimport { fetchPage } from '../fetchers';\nimport {\n detectSeasonOrEpisodeListType,\n getEpisodeCode,\n getMovieBoxMovies,\n getMovieColorRating,\n getMovieCreators,\n getMovieDescriptions,\n getMovieDuration,\n getMovieGenres,\n getMovieOrigins,\n getMoviePoster,\n getMoviePremieres,\n getMovieRandomPhoto,\n getMovieRating,\n getMovieRatingCount,\n getMovieTags,\n getMovieTitle,\n getMovieTitlesOther,\n getMovieTrivia,\n getMovieType,\n getMovieVods,\n getMovieYear,\n getSeasonOrEpisodeParent,\n getSeasonsOrEpisodes,\n getSeriesAndSeasonTitle\n} from '../helpers/movie.helper';\nimport { CSFDOptions } from '../types';\nimport { LIB_PREFIX, movieUrl } from '../vars';\n\nexport class MovieScraper {\n public async movie(movieId: number, options?: CSFDOptions): Promise<CSFDMovie> {\n const id = Number(movieId);\n if (isNaN(id)) {\n throw new Error('node-csfd-api: movieId must be a valid number');\n }\n const url = movieUrl(id, { language: options?.language });\n const response = await fetchPage(url, { ...options?.request });\n\n const movieHtml = parse(response);\n\n const pageClasses = movieHtml.querySelector('.page-content').classNames.split(' ');\n const asideNode = movieHtml.querySelector('.aside-movie-profile');\n const movieNode = movieHtml.querySelector('.main-movie-profile');\n const jsonLdString = movieHtml.querySelector('script[type=\"application/ld+json\"]')?.textContent;\n let jsonLd: MovieJsonLd | null = null;\n try {\n jsonLd = JSON.parse(jsonLdString);\n } catch (e) {\n console.error(LIB_PREFIX + ' Error parsing JSON-LD', e);\n }\n return this.buildMovie(+movieId, movieHtml, movieNode as HTMLElement, asideNode as HTMLElement, pageClasses, jsonLd, options);\n }\n\n private buildMovie(\n movieId: number,\n movieHtml: HTMLElement,\n el: HTMLElement,\n asideEl: HTMLElement,\n pageClasses: string[],\n jsonLd: MovieJsonLd | null,\n options: CSFDOptions\n ) {\n const type = getMovieType(el) as CSFDFilmTypes;\n const { seriesName = null, seasonName = null } =\n type === 'season' ? getSeriesAndSeasonTitle(el) : {};\n const seasonOrEpisodeListType = detectSeasonOrEpisodeListType(movieHtml);\n\n const title = type === 'season' && seriesName ? seriesName : getMovieTitle(el);\n return {\n id: movieId,\n title,\n year: getMovieYear(jsonLd),\n duration: getMovieDuration(jsonLd, el),\n descriptions: getMovieDescriptions(el),\n genres: getMovieGenres(el),\n type,\n url: movieUrl(movieId, { language: options?.language }),\n origins: getMovieOrigins(el),\n colorRating: getMovieColorRating(pageClasses),\n rating: getMovieRating(asideEl),\n ratingCount: getMovieRatingCount(asideEl),\n titlesOther: getMovieTitlesOther(el),\n poster: getMoviePoster(el),\n photo: getMovieRandomPhoto(el),\n trivia: getMovieTrivia(el),\n creators: getMovieCreators(el, options),\n vod: getMovieVods(el),\n tags: getMovieTags(asideEl),\n premieres: getMoviePremieres(asideEl),\n related: getMovieBoxMovies(asideEl, 'Související'),\n similar: getMovieBoxMovies(asideEl, 'Podobné'),\n seasons: seasonOrEpisodeListType === 'seasons' ? getSeasonsOrEpisodes(movieHtml) : null,\n episodes: seasonOrEpisodeListType === 'episodes' ? getSeasonsOrEpisodes(movieHtml) : null,\n parent: type === 'season' || type === 'episode' ? getSeasonOrEpisodeParent(el) : null,\n episodeCode: type === 'episode' ? getEpisodeCode(el) : null,\n seasonName\n };\n }\n}\n"],"mappings":";;;;;AAiCA,IAAa,eAAb,MAA0B;CACxB,MAAa,MAAM,SAAiB,SAA2C;EAC7E,MAAM,KAAK,OAAO,QAAQ;AAC1B,MAAI,MAAM,GAAG,CACX,OAAM,IAAI,MAAM,gDAAgD;EAKlE,MAAM,YAAY,MAFD,MAAM,UADX,SAAS,IAAI,EAAE,UAAU,SAAS,UAAU,CAAC,EACnB,EAAE,GAAG,SAAS,SAAS,CAAC,CAE7B;EAEjC,MAAM,cAAc,UAAU,cAAc,gBAAgB,CAAC,WAAW,MAAM,IAAI;EAClF,MAAM,YAAY,UAAU,cAAc,uBAAuB;EACjE,MAAM,YAAY,UAAU,cAAc,sBAAsB;EAChE,MAAM,eAAe,UAAU,cAAc,uCAAqC,EAAE;EACpF,IAAI,SAA6B;AACjC,MAAI;AACF,YAAS,KAAK,MAAM,aAAa;WAC1B,GAAG;AACV,WAAQ,MAAM,aAAa,0BAA0B,EAAE;;AAEzD,SAAO,KAAK,WAAW,CAAC,SAAS,WAAW,WAA0B,WAA0B,aAAa,QAAQ,QAAQ;;CAG/H,WACE,SACA,WACA,IACA,SACA,aACA,QACA,SACA;EACA,MAAM,OAAO,aAAa,GAAG;EAC7B,MAAM,EAAE,aAAa,MAAM,aAAa,SACtC,SAAS,WAAW,wBAAwB,GAAG,GAAG,EAAE;EACtD,MAAM,0BAA0B,8BAA8B,UAAU;AAGxE,SAAO;GACL,IAAI;GACJ,OAHY,SAAS,YAAY,aAAa,aAAa,cAAc,GAAG;GAI5E,MAAM,aAAa,OAAO;GAC1B,UAAU,iBAAiB,QAAQ,GAAG;GACtC,cAAc,qBAAqB,GAAG;GACtC,QAAQ,eAAe,GAAG;GAC1B;GACA,KAAK,SAAS,SAAS,EAAE,UAAU,SAAS,UAAU,CAAC;GACvD,SAAS,gBAAgB,GAAG;GAC5B,aAAa,oBAAoB,YAAY;GAC7C,QAAQ,eAAe,QAAQ;GAC/B,aAAa,oBAAoB,QAAQ;GACzC,aAAa,oBAAoB,GAAG;GACpC,QAAQ,eAAe,GAAG;GAC1B,OAAO,oBAAoB,GAAG;GAC9B,QAAQ,eAAe,GAAG;GAC1B,UAAU,iBAAiB,IAAI,QAAQ;GACvC,KAAK,aAAa,GAAG;GACrB,MAAM,aAAa,QAAQ;GAC3B,WAAW,kBAAkB,QAAQ;GACrC,SAAS,kBAAkB,SAAS,cAAc;GAClD,SAAS,kBAAkB,SAAS,UAAU;GAC9C,SAAS,4BAA4B,YAAY,qBAAqB,UAAU,GAAG;GACnF,UAAU,4BAA4B,aAAa,qBAAqB,UAAU,GAAG;GACrF,QAAQ,SAAS,YAAY,SAAS,YAAY,yBAAyB,GAAG,GAAG;GACjF,aAAa,SAAS,YAAY,eAAe,GAAG,GAAG;GACvD;GACD"}
|