node-csfd-api-racintom 1.4.0 → 1.5.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.
Files changed (111) hide show
  1. package/fetchers/fetch.polyfill.d.ts +1 -0
  2. package/fetchers/fetch.polyfill.js +9 -0
  3. package/fetchers/index.d.ts +1 -0
  4. package/fetchers/index.js +27 -0
  5. package/helpers/creator.helper.d.ts +17 -0
  6. package/helpers/creator.helper.js +87 -0
  7. package/helpers/global.helper.d.ts +17 -0
  8. package/helpers/global.helper.js +68 -0
  9. package/helpers/movie.helper.d.ts +26 -0
  10. package/helpers/movie.helper.js +270 -0
  11. package/helpers/search-user.helper.d.ts +5 -0
  12. package/helpers/search-user.helper.js +22 -0
  13. package/helpers/search.helper.d.ts +11 -0
  14. package/helpers/search.helper.js +62 -0
  15. package/helpers/user-ratings.helper.d.ts +13 -0
  16. package/helpers/user-ratings.helper.js +61 -0
  17. package/index.d.ts +24 -0
  18. package/index.js +39 -0
  19. package/interfaces/creator.interface.d.ts +12 -0
  20. package/interfaces/creator.interface.js +2 -0
  21. package/interfaces/global.d.ts +22 -0
  22. package/interfaces/global.js +2 -0
  23. package/interfaces/movie.interface.d.ts +73 -0
  24. package/interfaces/movie.interface.js +2 -0
  25. package/interfaces/search.interface.d.ts +27 -0
  26. package/interfaces/search.interface.js +2 -0
  27. package/interfaces/season.interface.d.ts +11 -0
  28. package/interfaces/season.interface.js +2 -0
  29. package/interfaces/user-ratings.interface.d.ts +18 -0
  30. package/interfaces/user-ratings.interface.js +2 -0
  31. package/package.json +62 -86
  32. package/services/creator.service.d.ts +6 -0
  33. package/services/creator.service.js +32 -0
  34. package/services/movie.service.d.ts +6 -0
  35. package/services/movie.service.js +59 -0
  36. package/services/search.service.d.ts +5 -0
  37. package/services/search.service.js +80 -0
  38. package/services/season.service.d.ts +9 -0
  39. package/services/season.service.js +42 -0
  40. package/services/user-ratings.service.d.ts +7 -0
  41. package/services/user-ratings.service.js +84 -0
  42. package/vars.d.ts +5 -0
  43. package/vars.js +13 -0
  44. package/.editorconfig +0 -13
  45. package/.eslintrc.json +0 -33
  46. package/.gitattributes +0 -2
  47. package/.github/FUNDING.yml +0 -8
  48. package/.github/pull_request_template.md +0 -19
  49. package/.github/workflows/main.yml +0 -40
  50. package/.github/workflows/publish.yml +0 -73
  51. package/.github/workflows/test.yml +0 -43
  52. package/.husky/pre-commit +0 -1
  53. package/.idea/codeStyles/Project.xml +0 -72
  54. package/.idea/codeStyles/codeStyleConfig.xml +0 -5
  55. package/.idea/inspectionProfiles/Project_Default.xml +0 -6
  56. package/.idea/misc.xml +0 -6
  57. package/.idea/modules.xml +0 -8
  58. package/.idea/node-csfd-api.iml +0 -9
  59. package/.idea/prettier.xml +0 -6
  60. package/.idea/vcs.xml +0 -7
  61. package/.nvmrc +0 -1
  62. package/.prettierignore +0 -8
  63. package/.prettierrc +0 -10
  64. package/.vscode/settings.json +0 -16
  65. package/Dockerfile +0 -19
  66. package/demo.ts +0 -35
  67. package/eslint.config.mjs +0 -55
  68. package/server.ts +0 -66
  69. package/src/fetchers/fetch.polyfill.ts +0 -7
  70. package/src/fetchers/index.ts +0 -25
  71. package/src/helpers/creator.helper.ts +0 -95
  72. package/src/helpers/global.helper.ts +0 -70
  73. package/src/helpers/movie.helper.ts +0 -276
  74. package/src/helpers/search-user.helper.ts +0 -19
  75. package/src/helpers/search.helper.ts +0 -66
  76. package/src/helpers/user-ratings.helper.ts +0 -62
  77. package/src/index.ts +0 -50
  78. package/src/interfaces/creator.interface.ts +0 -14
  79. package/src/interfaces/global.ts +0 -36
  80. package/src/interfaces/movie.interface.ts +0 -157
  81. package/src/interfaces/search.interface.ts +0 -32
  82. package/src/interfaces/season.interface.ts +0 -12
  83. package/src/interfaces/user-ratings.interface.ts +0 -21
  84. package/src/services/creator.service.ts +0 -34
  85. package/src/services/movie.service.ts +0 -89
  86. package/src/services/search.service.ts +0 -101
  87. package/src/services/season.service.ts +0 -55
  88. package/src/services/user-ratings.service.ts +0 -106
  89. package/src/vars.ts +0 -16
  90. package/tests/creator.test.ts +0 -182
  91. package/tests/fetchers.test.ts +0 -109
  92. package/tests/global.test.ts +0 -35
  93. package/tests/helpers.test.ts +0 -59
  94. package/tests/mocks/creator-actor.html.ts +0 -2244
  95. package/tests/mocks/creator-composer-empty.html.ts +0 -683
  96. package/tests/mocks/creator-director.html.ts +0 -3407
  97. package/tests/mocks/movie1.html.ts +0 -1430
  98. package/tests/mocks/movie2.html.ts +0 -740
  99. package/tests/mocks/movie3.html.ts +0 -1843
  100. package/tests/mocks/movie4.html.ts +0 -1568
  101. package/tests/mocks/search.html.ts +0 -838
  102. package/tests/mocks/series1.html.ts +0 -1540
  103. package/tests/mocks/userRatings.html.ts +0 -1354
  104. package/tests/movie.test.ts +0 -606
  105. package/tests/search.test.ts +0 -379
  106. package/tests/season.test.ts +0 -115
  107. package/tests/services.test.ts +0 -106
  108. package/tests/user-ratings.test.ts +0 -142
  109. package/tests/vars.test.ts +0 -34
  110. package/tsconfig.json +0 -23
  111. package/vitest.config.mts +0 -10
@@ -1,16 +0,0 @@
1
- {
2
- "workbench.colorCustomizations": {
3
- "statusBar.background": "#000"
4
- },
5
- "editor.codeActionsOnSave": {
6
- "source.organizeImports": "explicit",
7
- "source.fixAll.eslint": "explicit"
8
- },
9
- "[html]": {
10
- "editor.defaultFormatter": "esbenp.prettier-vscode",
11
- "editor.formatOnSave": false
12
- },
13
- "editor.defaultFormatter": "esbenp.prettier-vscode",
14
- "editor.formatOnSave": true,
15
- "typescript.tsdk": "node_modules/typescript/lib"
16
- }
package/Dockerfile DELETED
@@ -1,19 +0,0 @@
1
- FROM node:24-alpine
2
-
3
- # Create app directory
4
- WORKDIR /usr/src/app
5
-
6
- # Copy package.json and yarn.lock
7
- COPY package.json yarn.lock ./
8
-
9
- # Install dependencies
10
- RUN yarn install
11
-
12
- # Copy the application code
13
- COPY . .
14
-
15
- # Expose the app's port
16
- EXPOSE 3000
17
-
18
- # Start the application
19
- CMD ["yarn", "tsx", "server.ts"]
package/demo.ts DELETED
@@ -1,35 +0,0 @@
1
- // import { writeFile } from 'fs';
2
- import { csfd } from './src';
3
-
4
- // Parse movie
5
- csfd.movie(10135).then((movie) => console.log(movie));
6
-
7
- // csfd.search('matrix').then((search) => console.log(search));
8
-
9
- // Parse creator
10
- csfd.creator(2120).then((creator) => console.log(creator));
11
-
12
- /**
13
- * USER RATINGS
14
- */
15
-
16
- // Save all pages in json file
17
- // const userId = 912;
18
-
19
- // csfd
20
- // .userRatings(userId, {
21
- // excludes: ['epizoda', 'pořad', 'série'],
22
- // allPages: false,
23
- // allPagesDelay: 2000
24
- // })
25
- // .then((ratings) => {
26
- // console.log('Saved in file:', `./${userId}.json`);
27
- // writeFile(`${userId}.json`, JSON.stringify(ratings), (err) => {
28
- // if (err) return console.log(err);
29
- // });
30
- // });
31
-
32
- // Only TV series
33
- // csfd
34
- // .userRatings('912-bart', { includesOnly: ['seriál'] })
35
- // .then((ratings) => console.log(ratings));
package/eslint.config.mjs DELETED
@@ -1,55 +0,0 @@
1
- import { FlatCompat } from '@eslint/eslintrc';
2
- import js from '@eslint/js';
3
- import typescriptEslint from '@typescript-eslint/eslint-plugin';
4
- import tsParser from '@typescript-eslint/parser';
5
- import prettier from 'eslint-plugin-prettier';
6
- import globals from 'globals';
7
- import path from 'node:path';
8
- import { fileURLToPath } from 'node:url';
9
-
10
- const __filename = fileURLToPath(import.meta.url);
11
- const __dirname = path.dirname(__filename);
12
- const compat = new FlatCompat({
13
- baseDirectory: __dirname,
14
- recommendedConfig: js.configs.recommended,
15
- allConfig: js.configs.all
16
- });
17
-
18
- export default [
19
- ...compat.extends('google', 'plugin:prettier/recommended'),
20
- {
21
- plugins: {
22
- '@typescript-eslint': typescriptEslint,
23
- prettier
24
- },
25
-
26
- languageOptions: {
27
- globals: {
28
- ...globals.browser,
29
- ...globals.node,
30
- Atomics: 'readonly',
31
- SharedArrayBuffer: 'readonly'
32
- },
33
-
34
- parser: tsParser,
35
- ecmaVersion: 2018,
36
- sourceType: 'module'
37
- },
38
-
39
- rules: {
40
- 'require-jsdoc': 'off',
41
- 'no-unused-vars': 'off',
42
-
43
- '@typescript-eslint/no-unused-vars': [
44
- 'error',
45
- {
46
- vars: 'all',
47
- args: 'after-used',
48
- ignoreRestSiblings: false
49
- }
50
- ],
51
-
52
- 'prettier/prettier': 'error'
53
- }
54
- }
55
- ];
package/server.ts DELETED
@@ -1,66 +0,0 @@
1
- import express from 'express';
2
- import packageJson from './package.json';
3
- import { csfd } from './src';
4
- import { CSFDFilmTypes } from './src/interfaces/global';
5
-
6
- const app = express();
7
- const port = process.env.PORT || 3000;
8
-
9
- app.get('/', (_, res) => {
10
- res.json({
11
- name: packageJson.name,
12
- version: packageJson.version,
13
- docs: packageJson.homepage,
14
- links: ['/movie/:id', '/creator/:id', '/search/:query', '/user-ratings/:id']
15
- });
16
- });
17
-
18
- app.get(['/movie/', '/creator/', '/search/', '/user-ratings/'], (req, res) => {
19
- res.json({ error: `ID is missing. Provide ID like this: ${req.url}${req.url.endsWith('/') ? '' : '/'}1234` });
20
- });
21
-
22
- app.get('/movie/:id', async (req, res) => {
23
- try {
24
- const movie = await csfd.movie(+req.params.id);
25
- res.json(movie);
26
- } catch (error) {
27
- res.status(500).json({ error: 'Failed to fetch movie data' });
28
- }
29
- });
30
-
31
- app.get('/creator/:id', async (req, res) => {
32
- try {
33
- const result = await csfd.creator(+req.params.id);
34
- res.json(result);
35
- } catch (error) {
36
- res.status(500).json({ error: 'Failed to fetch creator data: ' + error });
37
- }
38
- });
39
-
40
- app.get('/search/:query', async (req, res) => {
41
- try {
42
- const result = await csfd.search(req.params.query);
43
- res.json(result);
44
- } catch (error) {
45
- res.status(500).json({ error: 'Failed to fetch search data: ' + error });
46
- }
47
- });
48
-
49
- app.get('/user-ratings/:id', async (req, res) => {
50
- const { allPages, allPagesDelay, excludes, includesOnly } = req.query;
51
- try {
52
- const result = await csfd.userRatings(req.params.id, {
53
- allPages: allPages === 'true',
54
- allPagesDelay: allPagesDelay ? +allPagesDelay : undefined,
55
- excludes: excludes ? (excludes as string).split(',') as CSFDFilmTypes[] : undefined,
56
- includesOnly: includesOnly ? (includesOnly as string).split(',') as CSFDFilmTypes[] : undefined
57
- });
58
- res.json(result);
59
- } catch (error) {
60
- res.status(500).json({ error: 'Failed to fetch user-ratings data: ' + error });
61
- }
62
- });
63
-
64
- app.listen(port, () => {
65
- console.log(`API is running on http://localhost:${port}`);
66
- });
@@ -1,7 +0,0 @@
1
- // Check if `fetch` is available in global scope (nodejs 18+) or in window (browser). If not, use cross-fetch polyfill.
2
- import { fetch as crossFetch } from 'cross-fetch';
3
- export const fetchSafe =
4
- (typeof fetch === 'function' && fetch) || // ServiceWorker fetch (Cloud Functions + Chrome extension)
5
- (typeof global === 'object' && global.fetch) || // Node.js 18+ fetch
6
- (typeof window !== 'undefined' && window.fetch) || // Browser fetch
7
- crossFetch; // Polyfill fetch
@@ -1,25 +0,0 @@
1
- import { fetchSafe } from './fetch.polyfill';
2
-
3
- const USER_AGENTS = [
4
- 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36',
5
- 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/87.0.4280.77 Mobile/15E148 Safari/604.1',
6
- 'Mozilla/5.0 (Linux; Android 10; SM-A205U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.101 Mobile Safari/537.36',
7
- 'Mozilla/5.0 (Linux; Android 10) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.101 Mobile Safari/537.36'
8
- ];
9
-
10
- const headers = {
11
- 'User-Agent': USER_AGENTS[Math.floor(Math.random() * USER_AGENTS.length)]
12
- };
13
-
14
- export const fetchPage = async (url: string): Promise<string> => {
15
- try {
16
- const response = await fetchSafe(url, { headers });
17
- if (response.status >= 400 && response.status < 600) {
18
- throw new Error(`node-csfd-api: Bad response ${response.status} for url: ${url}`);
19
- }
20
- return await response.text();
21
- } catch (e) {
22
- console.error(e);
23
- return 'Error';
24
- }
25
- };
@@ -1,95 +0,0 @@
1
- import { HTMLElement } from 'node-html-parser';
2
- import { CSFDCreatorScreening } from '../interfaces/creator.interface';
3
- import { CSFDColorRating } from '../interfaces/global';
4
- import { Colors } from '../interfaces/user-ratings.interface';
5
- import { addProtocol, parseColor, parseIdFromUrl } from './global.helper';
6
-
7
- export const getColorRating = (el: HTMLElement): CSFDColorRating => {
8
- return parseColor(el?.classNames.split(' ').pop() as Colors);
9
- };
10
-
11
- export const getId = (url: string): number => {
12
- if (url) {
13
- return parseIdFromUrl(url);
14
- }
15
- return null;
16
- };
17
-
18
- export const getName = (el: HTMLElement | null): string => {
19
- return el.querySelector('h1').innerText.trim();
20
- };
21
-
22
- export const getBirthdayInfo = (
23
- el: HTMLElement | null
24
- ): { birthday: string; age: number; birthPlace: string } => {
25
- const infoBlock = el.querySelector('h1 + p');
26
- const text = infoBlock?.innerHTML.trim();
27
-
28
- const birthPlaceRow = infoBlock?.querySelector('.info-place')?.innerHTML.trim();
29
- const ageRow = infoBlock?.querySelector('.info')?.innerHTML.trim();
30
-
31
- let birthday: string = '';
32
-
33
- if (text) {
34
- const parts = text.split('\n');
35
- const birthdayRow = parts.find((x) => x.includes('nar.'));
36
- birthday = birthdayRow ? parseBirthday(birthdayRow) : '';
37
- }
38
-
39
- const age = ageRow ? +parseAge(ageRow) : null;
40
- const birthPlace = birthPlaceRow ? parseBirthPlace(birthPlaceRow) : '';
41
-
42
- return { birthday, age, birthPlace };
43
- };
44
-
45
- export const getBio = (el: HTMLElement | null): string => {
46
- return el.querySelector('.article-content p')?.text.trim().split('\n')[0].trim() || null;
47
- };
48
-
49
- export const getPhoto = (el: HTMLElement | null): string => {
50
- const image = el.querySelector('img').attributes.src;
51
- return addProtocol(image);
52
- };
53
-
54
- export const parseBirthday = (text: string): any => {
55
- return text.replace(/nar./g, '').trim();
56
- };
57
-
58
- export const parseAge = (text: string): any => {
59
- return text.trim().replace(/\(/g, '').replace(/let\)/g, '').trim();
60
- };
61
-
62
- export const parseBirthPlace = (text: string): any => {
63
- return text.trim().replace(/<br>/g, '').trim();
64
- };
65
-
66
- export const getFilms = (el: HTMLElement | null): CSFDCreatorScreening[] => {
67
- const filmNodes = el.querySelectorAll('.box')[0]?.querySelectorAll('table tr');
68
- let yearCache: number;
69
- const films = filmNodes.map((filmNode) => {
70
- const id = getId(filmNode.querySelector('td.name .film-title-name')?.attributes.href);
71
- const title = filmNode.querySelector('.name')?.text.trim();
72
- const year = +filmNode.querySelector('.year')?.text.trim();
73
- const colorRating = getColorRating(filmNode.querySelector('.name .icon'));
74
-
75
- // Cache year from previous film because there is a gap between movies with same year
76
- if (year) {
77
- yearCache = +year;
78
- }
79
-
80
- if (id && title) {
81
- return {
82
- id,
83
- title,
84
- year: year || yearCache,
85
- colorRating
86
- };
87
- }
88
- return {};
89
- });
90
- // Remove empty objects
91
- const filmsUnique = films.filter(
92
- (value) => Object.keys(value).length !== 0
93
- ) as CSFDCreatorScreening[];
94
- return filmsUnique;
95
- };
@@ -1,70 +0,0 @@
1
- import { CSFDColorRating } from '../interfaces/global';
2
- import { Colors } from '../interfaces/user-ratings.interface';
3
-
4
- export const parseIdFromUrl = (url: string): number => {
5
- if (url) {
6
- const idSlug = url?.split('/')[2];
7
- const id = idSlug?.split('-')[0];
8
- return +id || null;
9
- } else {
10
- return null;
11
- }
12
- };
13
-
14
- export const getColor = (cls: string): CSFDColorRating => {
15
- switch (cls) {
16
- case 'page-lightgrey':
17
- return 'unknown';
18
- case 'page-red':
19
- return 'good';
20
- case 'page-blue':
21
- return 'average';
22
- case 'page-grey':
23
- return 'bad';
24
- default:
25
- return 'unknown';
26
- }
27
- };
28
-
29
- export const parseColor = (quality: Colors): CSFDColorRating => {
30
- switch (quality) {
31
- case 'lightgrey':
32
- return 'unknown';
33
- case 'red':
34
- return 'good';
35
- case 'blue':
36
- return 'average';
37
- case 'grey':
38
- return 'bad';
39
- default:
40
- return 'unknown';
41
- }
42
- };
43
-
44
- export const addProtocol = (url: string): string => {
45
- return url.startsWith('//') ? 'https:' + url : url;
46
- };
47
-
48
- export const getDuration = (matches: any[]) => {
49
- return {
50
- sign: matches[1] === undefined ? '+' : '-',
51
- years: matches[2] === undefined ? 0 : matches[2],
52
- months: matches[3] === undefined ? 0 : matches[3],
53
- weeks: matches[4] === undefined ? 0 : matches[4],
54
- days: matches[5] === undefined ? 0 : matches[5],
55
- hours: matches[6] === undefined ? 0 : matches[6],
56
- minutes: matches[7] === undefined ? 0 : matches[7],
57
- seconds: matches[8] === undefined ? 0 : matches[8]
58
- };
59
- };
60
-
61
- export const parseISO8601Duration = (iso: string): number => {
62
- const iso8601DurationRegex =
63
- /(-)?P(?:([.,\d]+)Y)?(?:([.,\d]+)M)?(?:([.,\d]+)W)?(?:([.,\d]+)D)?T(?:([.,\d]+)H)?(?:([.,\d]+)M)?(?:([.,\d]+)S)?/;
64
-
65
- const matches = iso.match(iso8601DurationRegex);
66
-
67
- const duration = getDuration(matches);
68
-
69
- return +duration.minutes;
70
- };
@@ -1,276 +0,0 @@
1
- import { HTMLElement } from 'node-html-parser';
2
- import { CSFDColorRating } from '../interfaces/global';
3
- import {
4
- CSFDBoxContent,
5
- CSFDCreator,
6
- CSFDCreatorGroups,
7
- CSFDGenres,
8
- CSFDMovieListItem,
9
- CSFDPremiere, CSFDSeasons,
10
- CSFDTitlesOther,
11
- CSFDVod,
12
- CSFDVodService
13
- } from '../interfaces/movie.interface';
14
- import { addProtocol, getColor, parseISO8601Duration, parseIdFromUrl } from './global.helper';
15
-
16
- export const getId = (el: HTMLElement): number => {
17
- const url = el.querySelector('.tabs .tab-nav-list a').attributes.href;
18
- return parseIdFromUrl(url);
19
- };
20
-
21
- export const getTitle = (el: HTMLElement): string => {
22
- return el.querySelector('h1').innerText.split(`(`)[0].trim();
23
- };
24
-
25
- export const getGenres = (el: HTMLElement): CSFDGenres[] => {
26
- const genresRaw = el.querySelector('.genres').textContent;
27
- return genresRaw.split(' / ') as CSFDGenres[];
28
- };
29
-
30
- export const getOrigins = (el: HTMLElement): string[] => {
31
- const originsRaw = el.querySelector('.origin').textContent;
32
- const origins = originsRaw.split(',')[0];
33
- return origins.split(' / ');
34
- };
35
-
36
- export const getColorRating = (bodyClasses: string[]): CSFDColorRating => {
37
- return getColor(bodyClasses[1]);
38
- };
39
-
40
- export const getRating = (el: HTMLElement): number => {
41
- const ratingRaw = el.querySelector('.film-rating-average').textContent;
42
- const rating = ratingRaw?.replace(/%/g, '').trim();
43
- const ratingInt = parseInt(rating);
44
-
45
- if (Number.isInteger(ratingInt)) {
46
- return ratingInt;
47
- } else {
48
- return null;
49
- }
50
- };
51
-
52
- export const getRatingCount = (el: HTMLElement): number => {
53
- const ratingCountRaw = el.querySelector('.box-rating-container .counter')?.textContent;
54
- const ratingCount = +ratingCountRaw?.replace(/[(\s)]/g, '');
55
- if (Number.isInteger(ratingCount)) {
56
- return ratingCount;
57
- } else {
58
- return null;
59
- }
60
- };
61
-
62
- export const getYear = (el: string): number => {
63
- try {
64
- const jsonLd = JSON.parse(el);
65
- return +jsonLd.dateCreated;
66
- } catch (error) {
67
- console.error('node-csfd-api: Error parsing JSON-LD', error);
68
- return null;
69
- }
70
- };
71
-
72
- export const getDuration = (jsonLdRaw: string, el: HTMLElement): number => {
73
- let duration = null;
74
- try {
75
- const jsonLd = JSON.parse(jsonLdRaw);
76
- duration = jsonLd.duration;
77
- return parseISO8601Duration(duration);
78
- } catch (error) {
79
- const origin = el.querySelector('.origin').innerText;
80
- const timeString = origin.split(',');
81
- if (timeString.length > 2) {
82
- // Get last time elelment
83
- const timeString2 = timeString.pop().trim();
84
- // Clean it
85
- const timeRaw = timeString2.split('(')[0].trim();
86
- // Split by minutes and hours
87
- const hoursMinsRaw = timeRaw.split('min')[0];
88
- const hoursMins = hoursMinsRaw.split('h');
89
- // Resolve hours + minutes format
90
- duration = hoursMins.length > 1 ? +hoursMins[0] * 60 + +hoursMins[1] : +hoursMins[0];
91
- return duration;
92
- } else {
93
- return null;
94
- }
95
- }
96
- };
97
-
98
- export const getTitlesOther = (el: HTMLElement): CSFDTitlesOther[] => {
99
- const namesNode = el.querySelectorAll('.film-names li');
100
-
101
- if (!namesNode.length) {
102
- return [];
103
- }
104
-
105
- const titlesOther = namesNode.map((el) => {
106
- const country = el.querySelector('img.flag').attributes.alt;
107
- const title = el.textContent.trim().split('\n')[0];
108
-
109
- if (country && title) {
110
- return {
111
- country,
112
- title
113
- };
114
- } else {
115
- return null;
116
- }
117
- });
118
-
119
- return titlesOther.filter((x) => x);
120
- };
121
-
122
- export const getPoster = (el: HTMLElement | null): string => {
123
- const poster = el.querySelector('.film-posters img');
124
- // Resolve empty image
125
- if (poster) {
126
- if (poster.classNames?.includes('empty-image')) {
127
- return null;
128
- } else {
129
- // Full sized image (not thumb)
130
- const imageThumb = poster.attributes.src.split('?')[0];
131
- const image = imageThumb.replace(/\/w140\//, '/w1080/');
132
- return addProtocol(image);
133
- }
134
- } else {
135
- return null;
136
- }
137
- };
138
-
139
- export const getRandomPhoto = (el: HTMLElement | null): string => {
140
- const imageNode = el.querySelector('.gallery-item picture img');
141
- const image = imageNode?.attributes?.src;
142
- if (image) {
143
- return image.replace(/\/w663\//, '/w1326/');
144
- } else {
145
- return null;
146
- }
147
- };
148
-
149
- export const getTrivia = (el: HTMLElement | null): string[] => {
150
- const triviaNodes = el.querySelectorAll('.article-trivia ul li');
151
- if (triviaNodes?.length) {
152
- return triviaNodes.map((node) => node.textContent.trim().replace(/(\r\n|\n|\r|\t)/gm, ''));
153
- } else {
154
- return null;
155
- }
156
- };
157
-
158
- export const getDescriptions = (el: HTMLElement): string[] => {
159
- return el
160
- .querySelectorAll('.body--plots .plot-full p, .body--plots .plots .plots-item p')
161
- .map((movie) => movie.textContent?.trim().replace(/(\r\n|\n|\r|\t)/gm, ''));
162
- };
163
-
164
- export const parsePeople = (el: HTMLElement): CSFDCreator[] => {
165
- const people = el.querySelectorAll('a');
166
- return (
167
- people
168
- // Filter out "more" links
169
- .filter((x) => x.classNames.length === 0)
170
- .map((person) => {
171
- return {
172
- id: parseIdFromUrl(person.attributes.href),
173
- name: person.innerText.trim(),
174
- url: `https://www.csfd.cz${person.attributes.href}`
175
- };
176
- })
177
- );
178
- };
179
-
180
- export const getGroup = (el: HTMLElement, group: CSFDCreatorGroups): CSFDCreator[] => {
181
- const creators = el.querySelectorAll('.creators h4');
182
- const element = creators.filter((elem) => elem.textContent.trim().includes(group))[0];
183
- if (element?.parentNode) {
184
- return parsePeople(element.parentNode as HTMLElement);
185
- } else {
186
- return [];
187
- }
188
- };
189
-
190
- export const getType = (el: HTMLElement): string => {
191
- const type = el.querySelector('.film-header-name .type');
192
- return type?.innerText?.replace(/[{()}]/g, '') || 'film';
193
- };
194
-
195
- export const getVods = (el: HTMLElement | null): CSFDVod[] => {
196
- let vods: CSFDVod[] = [];
197
- if (el) {
198
- const buttons = el.querySelectorAll('.box-buttons .button');
199
- const buttonsVod = buttons.filter((x) => !x.classNames.includes('button-social'));
200
- vods = buttonsVod.map((btn) => {
201
- return {
202
- title: btn.textContent.trim() as CSFDVodService,
203
- url: btn.attributes.href
204
- };
205
- });
206
- }
207
- return vods.length ? vods : [];
208
- };
209
-
210
- // Get box content
211
- export const getBoxContent = (el: HTMLElement, box: string): HTMLElement => {
212
- const headers = el.querySelectorAll('section.box .box-header');
213
- return headers.find((header) => header.querySelector('h3').textContent.trim().includes(box))
214
- ?.parentNode;
215
- };
216
-
217
- export const getBoxMovies = (el: HTMLElement, boxName: CSFDBoxContent): CSFDMovieListItem[] => {
218
- const movieListItem: CSFDMovieListItem[] = [];
219
- const box = getBoxContent(el, boxName);
220
- const movieTitleNodes = box?.querySelectorAll('.article-header .film-title-name');
221
- if (movieTitleNodes?.length) {
222
- for (const item of movieTitleNodes) {
223
- movieListItem.push({
224
- id: parseIdFromUrl(item.attributes.href),
225
- title: item.textContent.trim(),
226
- url: `https://www.csfd.cz${item.attributes.href}`
227
- });
228
- }
229
- }
230
- return movieListItem;
231
- };
232
-
233
- export const getPremieres = (el: HTMLElement): CSFDPremiere[] => {
234
- const premiereNodes = el.querySelectorAll('.box-premieres li');
235
- const premiere: CSFDPremiere[] = [];
236
- for (const premiereNode of premiereNodes) {
237
- const title = premiereNode.querySelector('p + span').attributes.title;
238
-
239
- if (title) {
240
- const [date, ...company] = title?.split(' ');
241
-
242
- premiere.push({
243
- country: premiereNode.querySelector('.flag')?.attributes.title || null,
244
- format: premiereNode.querySelector('p').textContent.trim()?.split(' od')[0],
245
- date,
246
- company: company.join(' ')
247
- });
248
- }
249
- }
250
- return premiere;
251
- };
252
-
253
- export const getTags = (el: HTMLElement): string[] => {
254
- const tagsRaw = el.querySelectorAll('.box-content a[href*="/podrobne-vyhledavani/?tag="]');
255
- return tagsRaw.map((tag) => tag.textContent);
256
- };
257
-
258
- export const getSeasonsInfo = (el: HTMLElement): CSFDSeasons => {
259
- const seasonsList = el.querySelector('.film-episodes-list')
260
- if (seasonsList === null) {
261
- return null
262
- }
263
-
264
- const seasons = seasonsList.querySelectorAll('.film-title')
265
-
266
- return seasons.map(season => {
267
- const nameContainer = season.querySelector('.film-title-name')
268
- const infoContainer = season.querySelector('.film-title-info')
269
-
270
- return {
271
- linkToDetail: nameContainer.getAttribute('href'),
272
- name: nameContainer.textContent,
273
- additionalInfo: infoContainer.textContent
274
- }
275
- })
276
- }
@@ -1,19 +0,0 @@
1
- import { HTMLElement } from 'node-html-parser';
2
- import { addProtocol } from './global.helper';
3
-
4
- export const getUser = (el: HTMLElement): string => {
5
- return el.querySelector('.user-title-name').text;
6
- };
7
-
8
- export const getUserRealName = (el: HTMLElement): string => {
9
- return el.querySelector('.user-real-name')?.text.trim() || null;
10
- };
11
-
12
- export const getAvatar = (el: HTMLElement): string => {
13
- const image = el.querySelector('.article-img img').attributes.src;
14
- return addProtocol(image);
15
- };
16
-
17
- export const getUserUrl = (el: HTMLElement): string => {
18
- return el.querySelector('.user-title-name').attributes.href;
19
- };