node-csfd-api 5.1.0-beta.0 โ†’ 5.2.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/README.md CHANGED
@@ -6,9 +6,9 @@
6
6
 
7
7
  <div align="center">
8
8
 
9
- # CSFD API ๐ŸŽฌ + CSFD Export + MCP Server ๐Ÿค–
9
+ # CSFD API ๐ŸŽฌ + CSFD Export ๐Ÿ’พ + CSFD MCP ๐Ÿค–
10
10
 
11
- #### Modern TypeScript NPM library for scraping **CSFD.CZ**. Scraper, API, Export and MCP Server in one package. _(unofficial)_
11
+ #### Modern TypeScript NPM library for scraping **CSFD.CZ**. Scraper, API Rest Server, Exporter and MCP Server in one package. _(unofficial)_
12
12
 
13
13
  [Features](#-features) โ€ข [Installation](#-installation) โ€ข [Quick Start](#-quick-start) โ€ข [API Reference](#-api-reference) โ€ข [Examples](#-usage-examples) โ€ข [MCP Server](#-mcp-server-model-context-protocol) โ€ข [Docker](#-docker-support)
14
14
 
@@ -22,6 +22,7 @@
22
22
  - ๐Ÿงช **Well-tested** - ~100% code coverage
23
23
  - ๐Ÿš€ **Universal** - Works in Node.js, browsers, and serverless environments
24
24
  - ๐Ÿณ **Docker ready** - Pre-built Docker images available
25
+ - ๐Ÿบ **Homebrew support** - Easy globally installed CLI via Homebrew tap
25
26
  - ๐Ÿค– **MCP Server** - Use CSFD data directly within LLMs like Claude Desktop
26
27
  - ๐Ÿ”„ **Modern API** - Promise-based with async/await support
27
28
  - ๐Ÿ“ฆ **Few dependencies** - Lightweight and efficient
@@ -31,6 +32,7 @@
31
32
  - Node.js (ESM & CommonJS)
32
33
  - Browsers (with CORS considerations)
33
34
  - Docker containers
35
+ - macOS/Linux CLI (via Homebrew)
34
36
  - MCP Server (Claude Desktop, etc.)
35
37
  - Serverless (Firebase Functions, AWS Lambda, CloudFlare Workers, etc.)
36
38
  - Chrome Extensions
@@ -434,13 +436,13 @@ const excludeEpisodes = await csfd.userRatings('912-bart', {
434
436
 
435
437
  #### UserRatingsOptions
436
438
 
437
- | Option | Type | Default | Description |
438
- | --------------- | ----------------- | ------- | -------------------------------------------------------------------- |
439
+ | Option | Type | Default | Description |
440
+ | --------------- | ----------------- | ------- | ---------------------------------------------------------------- |
439
441
  | `includesOnly` | `CSFDFilmTypes[]` | `null` | Include only specific content types (e.g., `['film', 'series']`) |
440
- | `exclude` | `CSFDFilmTypes[]` | `null` | Exclude specific content types (e.g., `['episode']`) |
441
- | `allPages` | `boolean` | `false` | Fetch all pages of ratings |
442
- | `allPagesDelay` | `number` | `0` | Delay between page requests in milliseconds |
443
- | `page` | `number` | `1` | Fetch specific page number |
442
+ | `exclude` | `CSFDFilmTypes[]` | `null` | Exclude specific content types (e.g., `['episode']`) |
443
+ | `allPages` | `boolean` | `false` | Fetch all pages of ratings |
444
+ | `allPagesDelay` | `number` | `0` | Delay between page requests in milliseconds |
445
+ | `page` | `number` | `1` | Fetch specific page number |
444
446
 
445
447
  > ๐Ÿ“ **Note**: `includesOnly` and `exclude` are mutually exclusive. If both are provided, `includesOnly` takes precedence.
446
448
  >
@@ -522,7 +524,27 @@ Same options as [UserRatingsOptions](#userrationsoptions).
522
524
 
523
525
  ## ๐Ÿ’ป CLI Tools
524
526
 
525
- This library comes with a powerful CLI that exposes several tools.
527
+ This library comes with a powerful CLI that exposes several tools. You can run the CLI either via `npx` (without installation) or by installing it globally via Homebrew (macOS/Linux).
528
+
529
+ ### Installation / Usage
530
+
531
+ **Option A: NPX (No installation required)**
532
+
533
+ ```bash
534
+ npx node-csfd-api <command>
535
+ ```
536
+
537
+ **Option B: Homebrew for macOS/Linux (Global installation)**
538
+
539
+ ```bash
540
+ brew install bartholomej/tap/csfd
541
+
542
+ csfd <command>
543
+ ```
544
+
545
+ > ๐Ÿ’ก _Note: The examples below use `npx node-csfd-api`, but if you installed via Homebrew, simply replace it with `csfd` (e.g., `csfd export ratings 912`)._
546
+
547
+ ---
526
548
 
527
549
  ### 1. Export Ratings (CSV, JSON & Letterboxd)
528
550
 
@@ -548,7 +570,7 @@ npx node-csfd-api server
548
570
  # Server listening on port 3000
549
571
  ```
550
572
 
551
- ### 3. MCP Server
573
+ ### 3. MCP Server for AI Agents
552
574
 
553
575
  Run the Model Context Protocol server for AI agents.
554
576
 
@@ -656,7 +678,7 @@ This library powers several production applications:
656
678
  - Related content (similar movies, trivia)
657
679
  - Alternative titles, premieres, tags
658
680
  - **Search**
659
- - Movies, TV series, and users
681
+ - Movies, TV series, Creators and users
660
682
  - **Creators**
661
683
  - Biography and filmography
662
684
  - **User Data**
@@ -665,9 +687,9 @@ This library powers several production applications:
665
687
 
666
688
  ### Planned Features ๐Ÿšง
667
689
 
690
+ - [x] Search: Creator search functionality
668
691
  - [ ] Movie: reviews from movie detail page
669
692
  - [ ] Movie: Original soundtracks (OST) information
670
- - [ ] Search: Creator search functionality
671
693
  - [ ] Server: Caching layer for improved performance
672
694
  - [ ] Server: Rate limiting helpers
673
695
 
@@ -1 +1 @@
1
- {"version":3,"file":"movie.helper.js","names":["getColor","parseISO8601Duration","addProtocol","parseIdFromUrl","parseLastIdFromUrl","parseFilmType","parseDate"],"sources":["../../src/helpers/movie.helper.ts"],"sourcesContent":["import { HTMLElement } from 'node-html-parser';\nimport { CSFDColorRating, CSFDFilmTypes } from '../dto/global';\nimport {\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 { 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 };\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 ] 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 = (\n el: HTMLElement,\n serie?: { id: number; title: string }\n): 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 = (\n el: HTMLElement,\n serie?: { id: number; title: string }\n): 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) {\n if (!serie) return null;\n return { series: serie, season: null };\n }\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.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 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":["getColor","parseISO8601Duration","addProtocol","parseIdFromUrl","parseLastIdFromUrl","parseFilmType","parseDate"],"sources":["../../src/helpers/movie.helper.ts"],"sourcesContent":["import { HTMLElement } from 'node-html-parser';\nimport { CSFDColorRating, CSFDFilmTypes } from '../dto/global';\nimport {\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 { 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 };\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 ] 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 = (\n el: HTMLElement,\n serie?: { id: number; title: string }\n): 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 = (\n el: HTMLElement,\n serie?: { id: number; title: string }\n): 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) {\n if (!serie) return null;\n return { series: serie, season: null };\n }\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":";;;AA8BA,MAAM,iBAGF;CACF,IAAI;EACF,WAAW;EACX,SAAS;EACT,gBAAgB;EAChB,OAAO;EACP,QAAQ;EACR,SAAS;EACT,WAAW;EACX,aAAa;EACb,eAAe;EACf,kBAAkB;EAClB,SAAS;EACT,OAAO;EACP,QAAQ;EACT;CACD,IAAI;EACF,WAAW;EACX,SAAS;EACT,gBAAgB;EAChB,OAAO;EACP,QAAQ;EACR,SAAS;EACT,WAAW;EACX,aAAa;EACb,eAAe;EACf,kBAAkB;EAClB,SAAS;EACT,OAAO;EACP,QAAQ;EACT;CACD,IAAI;EACF,WAAW;EACX,SAAS;EACT,gBAAgB;EAChB,OAAO;EACP,QAAQ;EACR,SAAS;EACT,WAAW;EACX,aAAa;EACb,eAAe;EACf,kBAAkB;EAClB,SAAS;EACT,OAAO;EACP,QAAQ;EACT;CACF;;;;;;;AAQD,MAAa,4BACX,UACA,QAc2E;AAE3E,SAAQ,eADK,YAAY,SACO,eAAe,OAAO;;AAQxD,MAAa,2BACX,OAC6D;CAC7D,MAAM,eAAe,GAAG,cAAc,KAAK;AAC3C,KAAI,CAAC,aACH,QAAO;EAAE,YAAY;EAAM,YAAY;EAAM;CAG/C,MAAM,WAAW,aAAa,UAAU,MAAM;AAG9C,KAAI,SAAS,SAAS,MAAM,EAAE;EAC5B,MAAM,CAAC,YAAY,cAAc,SAAS,MAAM,MAAM,CAAC,KAAK,SAAS,KAAK,MAAM,CAAC;AACjF,SAAO;GAAE;GAAY;GAAY;;AAInC,QAAO;EAAE,YAAY;EAAU,YAAY;EAAM;;AAGnD,MAAa,iBAAiB,OAA4B;AACxD,QAAO,GAAG,cAAc,KAAK,CAAC,UAAU,MAAM,IAAI,CAAC,GAAG,MAAM;;AAG9D,MAAa,kBAAkB,OAAkC;AAE/D,QADkB,GAAG,cAAc,UAAU,CAAC,YAC7B,MAAM,MAAM;;AAG/B,MAAa,mBAAmB,OAA8B;AAG5D,QAFmB,GAAG,cAAc,UAAU,CAAC,YACpB,MAAM,IAAI,CAAC,GACvB,MAAM,MAAM;;AAG7B,MAAa,uBAAuB,gBAA2C;AAC7E,QAAOA,+BAAS,YAAY,GAAG;;AAGjC,MAAa,kBAAkB,OAA4B;CAEzD,MAAM,SADY,GAAG,cAAc,uBAAuB,CAAC,aACjC,QAAQ,MAAM,GAAG,CAAC,MAAM;CAClD,MAAM,YAAY,SAAS,OAAO;AAElC,KAAI,OAAO,UAAU,UAAU,CAC7B,QAAO;KAEP,QAAO;;AAIX,MAAa,uBAAuB,OAA4B;CAE9D,MAAM,cAAc,EADG,GAAG,cAAc,iCAAiC,EAAE,cACtC,QAAQ,WAAW,GAAG;AAC3D,KAAI,OAAO,UAAU,YAAY,CAC/B,QAAO;KAEP,QAAO;;AAIX,MAAa,gBAAgB,WAAuC;AAClE,KAAI,UAAU,OAAO,YACnB,QAAO,CAAC,OAAO;AAEjB,QAAO;;AAGT,MAAa,oBAAoB,QAA4B,OAA4B;AACvF,KAAI,UAAU,OAAO,SACnB,KAAI;AACF,SAAOC,2CAAqB,OAAO,SAAS;UACrC,GAAG;AAKd,KAAI;EAEF,MAAM,aADS,GAAG,cAAc,UAAU,CAAC,UACjB,MAAM,IAAI;AACpC,MAAI,WAAW,SAAS,GAAG;GAOzB,MAAM,YALc,WAAW,KAAK,CAAC,MAAM,CAEf,MAAM,IAAI,CAAC,GAAG,MAAM,CAEnB,MAAM,MAAM,CAAC,GACX,MAAM,IAAI;AAGzC,UADiB,UAAU,SAAS,IAAI,CAAC,UAAU,KAAK,KAAK,CAAC,UAAU,KAAK,CAAC,UAAU;QAGxF,QAAO;UAEF,OAAO;AACd,SAAO;;;AAIX,MAAa,uBAAuB,OAAuC;CACzE,MAAM,YAAY,GAAG,iBAAiB,iBAAiB;AAEvD,KAAI,CAAC,UAAU,OACb,QAAO,EAAE;AAiBX,QAdoB,UAAU,KAAK,OAAO;EACxC,MAAM,UAAU,GAAG,cAAc,WAAW,CAAC,WAAW;EACxD,MAAM,QAAQ,GAAG,YAAY,MAAM,CAAC,MAAM,KAAK,CAAC;AAEhD,MAAI,WAAW,MACb,QAAO;GACL;GACA;GACD;MAED,QAAO;GAET,CAEiB,QAAQ,MAAM,EAAE;;AAGrC,MAAa,kBAAkB,OAAmC;CAChE,MAAM,SAAS,GAAG,cAAc,oBAAoB;AAEpD,KAAI,OACF,KAAI,OAAO,YAAY,SAAS,cAAc,CAC5C,QAAO;KAKP,QAAOC,kCAFY,OAAO,WAAW,IAAI,MAAM,IAAI,CAAC,GAC3B,QAAQ,YAAY,UAAU,CAC9B;KAG3B,QAAO;;AAIX,MAAa,uBAAuB,OAAmC;CAErE,MAAM,QADY,GAAG,cAAc,4BAA4B,EACtC,YAAY;AACrC,KAAI,MACF,QAAO,MAAM,QAAQ,YAAY,UAAU;KAE3C,QAAO;;AAIX,MAAa,kBAAkB,OAAqC;CAClE,MAAM,cAAc,GAAG,iBAAiB,wBAAwB;AAChE,KAAI,aAAa,OACf,QAAO,YAAY,KAAK,SAAS,KAAK,YAAY,MAAM,CAAC,QAAQ,qBAAqB,GAAG,CAAC;KAE1F,QAAO;;AAIX,MAAa,wBAAwB,OAA8B;AACjE,QAAO,GACJ,iBAAiB,+DAA+D,CAChF,KAAK,UAAU,MAAM,aAAa,MAAM,CAAC,QAAQ,qBAAqB,GAAG,CAAC;;AAG/E,MAAM,oBAAoB,OAAwC;AAEhE,QADe,GAAG,iBAAiB,IAAI,CAIlC,QAAQ,MAAM,EAAE,WAAW,WAAW,EAAE,CACxC,KAAK,WAAW;AACf,SAAO;GACL,IAAIC,qCAAe,OAAO,WAAW,KAAK;GAC1C,MAAM,OAAO,UAAU,MAAM;GAC7B,KAAK,sBAAsB,OAAO,WAAW;GAC9C;GACD;;AAcR,MAAa,oBAAoB,IAAiB,YAAwC;CACxF,MAAM,WAAyB;EAC7B,WAAW,EAAE;EACb,SAAS,EAAE;EACX,gBAAgB,EAAE;EAClB,OAAO,EAAE;EACT,QAAQ,EAAE;EACV,SAAS,EAAE;EACX,WAAW,EAAE;EACb,aAAa,EAAE;EACf,eAAe,EAAE;EACjB,kBAAkB,EAAE;EACrB;CAED,MAAM,SAAS,GAAG,iBAAiB,eAAe;CAelD,MAAM,kBAbO;EACX;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CAE4B,KAAK,SAAS;EACzC;EACA,OAAO,yBAAyB,SAAS,UAAU,IAAI;EACxD,EAAE;AAEH,MAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,OAAO,MAAM,YAAY,MAAM;AACrC,OAAK,MAAM,EAAE,KAAK,WAAW,gBAC3B,KAAI,KAAK,SAAS,MAAM,EAAE;AACxB,OAAI,MAAM,WACR,UAAS,OAAO,iBAAiB,MAAM,WAA0B;AAEnE;;;AAKN,QAAO;;AAGT,MAAa,wBACX,IACA,UAC6B;CAC7B,MAAM,eAAe,GAAG,cAAc,sBAAsB;AAC5D,KAAI,CAAC,aAAc,QAAO;CAE1B,MAAM,gBAAgB,aAAa,iBAAiB,cAAc;AAClE,KAAI,CAAC,eAAe,OAAQ,QAAO,EAAE;AAErC,QAAO,cAAc,KAAK,WAAW;EACnC,MAAM,gBAAgB,OAAO,cAAc,mBAAmB;EAC9D,MAAM,gBAAgB,OAAO,cAAc,QAAQ;EAEnD,MAAM,OAAO,eAAe,aAAa,OAAO;EAChD,MAAM,MAAM,OAAQ,KAAK,WAAW,IAAI,GAAG,sBAAsB,SAAS,OAAQ;AAElF,SAAO;GACL,IAAIC,yCAAmB,QAAQ,GAAG;GAClC,OAAO,eAAe,aAAa,MAAM,IAAI;GAC7C;GACA,MAAM,eAAe,aAAa,QAAQ,WAAW,GAAG,CAAC,MAAM,IAAI;GACpE;GACD;;AAGJ,MAAa,kBAAkB,OAAmC;CAChE,MAAM,iBAAiB,GAAG,cAAc,uBAAuB;AAC/D,KAAI,CAAC,eAAgB,QAAO;CAG5B,MAAM,SADO,eAAe,aAAa,MAAM,IAAI,IAChC,MAAM,cAAc;AAGvC,QAFa,QAAQ,MAAM,KAAK;;AAKlC,MAAa,iCAAiC,OAAoB;CAChE,MAAM,aAAa,GAAG,cAAc,iBAAiB,EAAE,UAAU,MAAM,IAAI;AAE3E,KAAI,WAAW,SAAS,QAAQ,CAAE,QAAO;AACzC,KAAI,WAAW,WAAW,UAAU,CAAE,QAAO;AAC7C,QAAO;;AAGT,MAAa,4BACX,IACA,UACsB;CAEtB,IAAI,UAAU,GAAG,iBAAiB,oBAAoB;AACtD,KAAI,QAAQ,WAAW,EACrB,WAAU,GAAG,iBAAiB,oBAAoB;AAGpD,KAAI,QAAQ,WAAW,GAAG;AACxB,MAAI,CAAC,MAAO,QAAO;AACnB,SAAO;GAAE,QAAQ;GAAO,QAAQ;GAAM;;CAGxC,MAAM,CAAC,cAAc,gBAAgB;CAErC,MAAM,WAAWD,qCAAe,cAAc,aAAa,OAAO,CAAC;CACnE,MAAM,WAAWC,yCAAmB,cAAc,aAAa,OAAO,IAAI,GAAG;CAC7E,MAAM,cAAc,cAAc,aAAa,MAAM,IAAI;CACzD,MAAM,cAAc,cAAc,aAAa,MAAM,IAAI;CAEzD,MAAM,SAAS,YAAY,cAAc;EAAE,IAAI;EAAU,OAAO;EAAa,GAAG;CAChF,MAAM,SAAS,YAAY,cAAc;EAAE,IAAI;EAAU,OAAO;EAAa,GAAG;AAEhF,KAAI,CAAC,UAAU,CAAC,OAAQ,QAAO;AAE/B,QAAO;EAAE;EAAQ;EAAQ;;AAgB3B,MAAa,gBAAgB,OAAmC;AAE9D,QAAOC,oCADM,GAAG,cAAc,0BAA0B,EAC7B,WAAW,QAAQ,WAAW,GAAG,IAAI,OAAO;;AAGzE,MAAa,gBAAgB,OAAsC;CACjE,IAAI,OAAkB,EAAE;AACxB,KAAI,GAEF,QADmB,GAAG,iBAAiB,gCAAgC,CACrD,KAAK,QAAQ;AAC7B,SAAO;GACL,OAAO,IAAI,YAAY,MAAM;GAC7B,KAAK,IAAI,WAAW;GACrB;GACD;AAEJ,QAAO,KAAK,SAAS,OAAO,EAAE;;AAIhC,MAAM,iBAAiB,IAAiB,QAA6B;AAEnE,QADgB,GAAG,iBAAiB,0BAA0B,CAC/C,MAAM,WAAW,OAAO,cAAc,KAAK,EAAE,YAAY,MAAM,CAAC,SAAS,IAAI,CAAC,EACzF;;AAGN,MAAa,qBACX,IACA,YACwB;CACxB,MAAM,gBAAqC,EAAE;CAE7C,MAAM,kBADM,cAAc,IAAI,QAAQ,EACT,iBAAiB,mCAAmC;AACjF,KAAI,iBAAiB,OACnB,MAAK,MAAM,QAAQ,gBACjB,eAAc,KAAK;EACjB,IAAIF,qCAAe,KAAK,WAAW,KAAK;EACxC,OAAO,KAAK,YAAY,MAAM;EAC9B,KAAK,sBAAsB,KAAK,WAAW;EAC5C,CAAC;AAGN,QAAO;;AAGT,MAAa,qBAAqB,OAAoC;CACpE,MAAM,gBAAgB,GAAG,iBAAiB,oBAAoB;CAC9D,MAAM,WAA2B,EAAE;AACnC,MAAK,MAAM,gBAAgB,eAAe;EACxC,MAAM,QAAQ,aAAa,cAAc,WAAW,CAAC,WAAW;AAEhE,MAAI,OAAO;GACT,MAAM,CAAC,SAAS,GAAG,WAAW,OAAO,MAAM,IAAI;GAC/C,MAAM,OAAOG,gCAAU,QAAQ;AAE/B,OAAI,KACF,UAAS,KAAK;IACZ,SAAS,aAAa,cAAc,QAAQ,EAAE,WAAW,SAAS;IAClE,QAAQ,aAAa,cAAc,IAAI,CAAC,YAAY,MAAM,EAAE,MAAM,MAAM,CAAC;IACzE;IACA,SAAS,QAAQ,KAAK,IAAI;IAC3B,CAAC;;;AAIR,QAAO;;AAGT,MAAa,gBAAgB,OAA8B;AAEzD,QADgB,GAAG,iBAAiB,kCAAgC,CACrD,KAAK,QAAQ,IAAI,YAAY"}
@@ -1 +1 @@
1
- {"version":3,"file":"movie.helper.mjs","names":[],"sources":["../../src/helpers/movie.helper.ts"],"sourcesContent":["import { HTMLElement } from 'node-html-parser';\nimport { CSFDColorRating, CSFDFilmTypes } from '../dto/global';\nimport {\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 { 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 };\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 ] 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 = (\n el: HTMLElement,\n serie?: { id: number; title: string }\n): 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 = (\n el: HTMLElement,\n serie?: { id: number; title: string }\n): 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) {\n if (!serie) return null;\n return { series: serie, season: null };\n }\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.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 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.mjs","names":[],"sources":["../../src/helpers/movie.helper.ts"],"sourcesContent":["import { HTMLElement } from 'node-html-parser';\nimport { CSFDColorRating, CSFDFilmTypes } from '../dto/global';\nimport {\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 { 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 };\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 ] 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 = (\n el: HTMLElement,\n serie?: { id: number; title: string }\n): 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 = (\n el: HTMLElement,\n serie?: { id: number; title: string }\n): 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) {\n if (!serie) return null;\n return { series: serie, season: null };\n }\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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"}
@@ -0,0 +1,19 @@
1
+ const require_global_helper = require('./global.helper.js');
2
+
3
+ //#region src/helpers/search-creator.helper.ts
4
+ const getCreatorName = (el) => {
5
+ return el.querySelector(".user-title").text.trim();
6
+ };
7
+ const getCreatorImage = (el) => {
8
+ const image = el.querySelector("img").attributes.src;
9
+ return require_global_helper.addProtocol(image);
10
+ };
11
+ const getCreatorUrl = (el) => {
12
+ return el.querySelector(".user-title a").attributes.href;
13
+ };
14
+
15
+ //#endregion
16
+ exports.getCreatorImage = getCreatorImage;
17
+ exports.getCreatorName = getCreatorName;
18
+ exports.getCreatorUrl = getCreatorUrl;
19
+ //# sourceMappingURL=search-creator.helper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-creator.helper.js","names":["addProtocol"],"sources":["../../src/helpers/search-creator.helper.ts"],"sourcesContent":["import { HTMLElement } from 'node-html-parser';\nimport { addProtocol } from './global.helper';\n\nexport const getCreatorName = (el: HTMLElement): string => {\n return el.querySelector('.user-title').text.trim();\n};\n\nexport const getCreatorImage = (el: HTMLElement): string => {\n const image = el.querySelector('img').attributes.src;\n return addProtocol(image);\n};\n\nexport const getCreatorUrl = (el: HTMLElement): string => {\n return el.querySelector('.user-title a').attributes.href;\n};\n"],"mappings":";;;AAGA,MAAa,kBAAkB,OAA4B;AACzD,QAAO,GAAG,cAAc,cAAc,CAAC,KAAK,MAAM;;AAGpD,MAAa,mBAAmB,OAA4B;CAC1D,MAAM,QAAQ,GAAG,cAAc,MAAM,CAAC,WAAW;AACjD,QAAOA,kCAAY,MAAM;;AAG3B,MAAa,iBAAiB,OAA4B;AACxD,QAAO,GAAG,cAAc,gBAAgB,CAAC,WAAW"}
@@ -0,0 +1,17 @@
1
+ import { addProtocol } from "./global.helper.mjs";
2
+
3
+ //#region src/helpers/search-creator.helper.ts
4
+ const getCreatorName = (el) => {
5
+ return el.querySelector(".user-title").text.trim();
6
+ };
7
+ const getCreatorImage = (el) => {
8
+ const image = el.querySelector("img").attributes.src;
9
+ return addProtocol(image);
10
+ };
11
+ const getCreatorUrl = (el) => {
12
+ return el.querySelector(".user-title a").attributes.href;
13
+ };
14
+
15
+ //#endregion
16
+ export { getCreatorImage, getCreatorName, getCreatorUrl };
17
+ //# sourceMappingURL=search-creator.helper.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-creator.helper.mjs","names":[],"sources":["../../src/helpers/search-creator.helper.ts"],"sourcesContent":["import { HTMLElement } from 'node-html-parser';\nimport { addProtocol } from './global.helper';\n\nexport const getCreatorName = (el: HTMLElement): string => {\n return el.querySelector('.user-title').text.trim();\n};\n\nexport const getCreatorImage = (el: HTMLElement): string => {\n const image = el.querySelector('img').attributes.src;\n return addProtocol(image);\n};\n\nexport const getCreatorUrl = (el: HTMLElement): string => {\n return el.querySelector('.user-title a').attributes.href;\n};\n"],"mappings":";;;AAGA,MAAa,kBAAkB,OAA4B;AACzD,QAAO,GAAG,cAAc,cAAc,CAAC,KAAK,MAAM;;AAGpD,MAAa,mBAAmB,OAA4B;CAC1D,MAAM,QAAQ,GAAG,cAAc,MAAM,CAAC,WAAW;AACjD,QAAO,YAAY,MAAM;;AAG3B,MAAa,iBAAiB,OAA4B;AACxD,QAAO,GAAG,cAAc,gBAAgB,CAAC,WAAW"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-csfd-api",
3
- "version": "5.1.0-beta.0",
3
+ "version": "5.2.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": {
@@ -12,7 +12,7 @@
12
12
  "cross-fetch": "^4.1.0",
13
13
  "dotenv": "^17.3.1",
14
14
  "express": "^5.2.1",
15
- "node-html-parser": "^7.0.2",
15
+ "node-html-parser": "^7.1.0",
16
16
  "zod": "^4.3.6"
17
17
  },
18
18
  "repository": {
package/package.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  //#region package.json
3
3
  var name = "node-csfd-api";
4
- var version = "5.1.0-beta.0";
4
+ var version = "5.2.0";
5
5
  var homepage = "https://github.com/bartholomej/node-csfd-api#readme";
6
6
 
7
7
  //#endregion
@@ -1,6 +1,7 @@
1
1
  const require_index = require('../fetchers/index.js');
2
2
  const require_vars = require('../vars.js');
3
3
  const require_global_helper = require('../helpers/global.helper.js');
4
+ const require_search_creator_helper = require('../helpers/search-creator.helper.js');
4
5
  const require_search_user_helper = require('../helpers/search-user.helper.js');
5
6
  const require_search_helper = require('../helpers/search.helper.js');
6
7
  let node_html_parser = require("node-html-parser");
@@ -12,13 +13,15 @@ var SearchScraper = class {
12
13
  const moviesNode = html.querySelectorAll(".main-movies article");
13
14
  const usersNode = html.querySelectorAll(".main-users article");
14
15
  const tvSeriesNode = html.querySelectorAll(".main-series article");
15
- return this.parseSearch(moviesNode, usersNode, tvSeriesNode, options?.language);
16
+ const creatorsNode = html.querySelectorAll(".main-authors article");
17
+ return this.parseSearch(moviesNode, usersNode, tvSeriesNode, creatorsNode, options?.language);
16
18
  }
17
- parseSearch(moviesNode, usersNode, tvSeriesNode, language) {
19
+ parseSearch(moviesNode, usersNode, tvSeriesNode, creatorsNode, language) {
18
20
  const baseUrl = require_vars.getUrlByLanguage(language);
19
21
  const movies = [];
20
22
  const users = [];
21
23
  const tvSeries = [];
24
+ const creators = [];
22
25
  const movieMapper = (m) => {
23
26
  const url = require_search_helper.getSearchUrl(m);
24
27
  return {
@@ -46,14 +49,24 @@ var SearchScraper = class {
46
49
  url: `${baseUrl}${url}`
47
50
  };
48
51
  };
52
+ const creatorMapper = (m) => {
53
+ const url = require_search_creator_helper.getCreatorUrl(m);
54
+ return {
55
+ id: require_global_helper.parseIdFromUrl(url),
56
+ name: require_search_creator_helper.getCreatorName(m),
57
+ image: require_search_creator_helper.getCreatorImage(m),
58
+ url: `${baseUrl}${url}`
59
+ };
60
+ };
49
61
  movies.push(...moviesNode.map(movieMapper));
50
62
  users.push(...usersNode.map(userMapper));
51
63
  tvSeries.push(...tvSeriesNode.map(movieMapper));
64
+ creators.push(...creatorsNode.map(creatorMapper));
52
65
  return {
53
66
  movies,
54
67
  users,
55
68
  tvSeries,
56
- creators: []
69
+ creators
57
70
  };
58
71
  }
59
72
  };
@@ -1 +1 @@
1
- {"version":3,"file":"search.service.js","names":["fetchPage","searchUrl","getUrlByLanguage","getSearchUrl","parseIdFromUrl","getSearchTitle","getSearchYear","getSearchType","getSearchColorRating","getSearchPoster","getSearchOrigins","parseSearchPeople","getUserUrl","getUser","getUserRealName","getAvatar"],"sources":["../../src/services/search.service.ts"],"sourcesContent":["import { HTMLElement, parse } from 'node-html-parser';\nimport { CSFDSearch, CSFDSearchMovie, CSFDSearchUser } from '../dto/search';\nimport { fetchPage } from '../fetchers';\nimport { parseIdFromUrl } from '../helpers/global.helper';\nimport { getAvatar, getUser, getUserRealName, getUserUrl } from '../helpers/search-user.helper';\nimport {\n getSearchColorRating,\n getSearchOrigins,\n getSearchPoster,\n getSearchTitle,\n getSearchType,\n getSearchUrl,\n getSearchYear,\n parseSearchPeople\n} from '../helpers/search.helper';\nimport { CSFDLanguage, CSFDOptions } from '../types';\nimport { getUrlByLanguage, searchUrl } from '../vars';\n\nexport class SearchScraper {\n public async search(text: string, options?: CSFDOptions): Promise<CSFDSearch> {\n const url = searchUrl(text, { language: options?.language });\n const response = await fetchPage(url, { ...options?.request });\n\n const html = parse(response);\n const moviesNode = html.querySelectorAll('.main-movies article');\n const usersNode = html.querySelectorAll('.main-users article');\n const tvSeriesNode = html.querySelectorAll('.main-series article');\n\n return this.parseSearch(moviesNode, usersNode, tvSeriesNode, options?.language);\n }\n\n private parseSearch(\n moviesNode: HTMLElement[],\n usersNode: HTMLElement[],\n tvSeriesNode: HTMLElement[],\n language?: CSFDLanguage\n ) {\n const baseUrl = getUrlByLanguage(language);\n\n const movies: CSFDSearchMovie[] = [];\n const users: CSFDSearchUser[] = [];\n const tvSeries: CSFDSearchMovie[] = [];\n\n const movieMapper = (m: HTMLElement): CSFDSearchMovie => {\n const url = getSearchUrl(m);\n return {\n id: parseIdFromUrl(url),\n title: getSearchTitle(m),\n year: getSearchYear(m),\n url: `${baseUrl}${url}`,\n type: getSearchType(m),\n colorRating: getSearchColorRating(m),\n poster: getSearchPoster(m),\n origins: getSearchOrigins(m),\n creators: {\n directors: parseSearchPeople(m, 'directors'),\n actors: parseSearchPeople(m, 'actors')\n }\n };\n };\n\n const userMapper = (m: HTMLElement): CSFDSearchUser => {\n const url = getUserUrl(m);\n return {\n id: parseIdFromUrl(url),\n user: getUser(m),\n userRealName: getUserRealName(m),\n avatar: getAvatar(m),\n url: `${baseUrl}${url}`\n };\n };\n\n movies.push(...moviesNode.map(movieMapper));\n users.push(...usersNode.map(userMapper));\n tvSeries.push(...tvSeriesNode.map(movieMapper));\n\n const search: CSFDSearch = {\n movies: movies,\n users: users,\n tvSeries: tvSeries,\n creators: []\n };\n return search;\n }\n}\n"],"mappings":";;;;;;;;AAkBA,IAAa,gBAAb,MAA2B;CACzB,MAAa,OAAO,MAAc,SAA4C;EAI5E,MAAM,mCAFW,MAAMA,wBADXC,uBAAU,MAAM,EAAE,UAAU,SAAS,UAAU,CAAC,EACtB,EAAE,GAAG,SAAS,SAAS,CAAC,CAElC;EAC5B,MAAM,aAAa,KAAK,iBAAiB,uBAAuB;EAChE,MAAM,YAAY,KAAK,iBAAiB,sBAAsB;EAC9D,MAAM,eAAe,KAAK,iBAAiB,uBAAuB;AAElE,SAAO,KAAK,YAAY,YAAY,WAAW,cAAc,SAAS,SAAS;;CAGjF,AAAQ,YACN,YACA,WACA,cACA,UACA;EACA,MAAM,UAAUC,8BAAiB,SAAS;EAE1C,MAAM,SAA4B,EAAE;EACpC,MAAM,QAA0B,EAAE;EAClC,MAAM,WAA8B,EAAE;EAEtC,MAAM,eAAe,MAAoC;GACvD,MAAM,MAAMC,mCAAa,EAAE;AAC3B,UAAO;IACL,IAAIC,qCAAe,IAAI;IACvB,OAAOC,qCAAe,EAAE;IACxB,MAAMC,oCAAc,EAAE;IACtB,KAAK,GAAG,UAAU;IAClB,MAAMC,oCAAc,EAAE;IACtB,aAAaC,2CAAqB,EAAE;IACpC,QAAQC,sCAAgB,EAAE;IAC1B,SAASC,uCAAiB,EAAE;IAC5B,UAAU;KACR,WAAWC,wCAAkB,GAAG,YAAY;KAC5C,QAAQA,wCAAkB,GAAG,SAAS;KACvC;IACF;;EAGH,MAAM,cAAc,MAAmC;GACrD,MAAM,MAAMC,sCAAW,EAAE;AACzB,UAAO;IACL,IAAIR,qCAAe,IAAI;IACvB,MAAMS,mCAAQ,EAAE;IAChB,cAAcC,2CAAgB,EAAE;IAChC,QAAQC,qCAAU,EAAE;IACpB,KAAK,GAAG,UAAU;IACnB;;AAGH,SAAO,KAAK,GAAG,WAAW,IAAI,YAAY,CAAC;AAC3C,QAAM,KAAK,GAAG,UAAU,IAAI,WAAW,CAAC;AACxC,WAAS,KAAK,GAAG,aAAa,IAAI,YAAY,CAAC;AAQ/C,SAN2B;GACjB;GACD;GACG;GACV,UAAU,EAAE;GACb"}
1
+ {"version":3,"file":"search.service.js","names":["fetchPage","searchUrl","getUrlByLanguage","getSearchUrl","parseIdFromUrl","getSearchTitle","getSearchYear","getSearchType","getSearchColorRating","getSearchPoster","getSearchOrigins","parseSearchPeople","getUserUrl","getUser","getUserRealName","getAvatar","getCreatorUrl","getCreatorName","getCreatorImage"],"sources":["../../src/services/search.service.ts"],"sourcesContent":["import { HTMLElement, parse } from 'node-html-parser';\nimport { CSFDSearch, CSFDSearchCreator, CSFDSearchMovie, CSFDSearchUser } from '../dto/search';\nimport { fetchPage } from '../fetchers';\nimport { parseIdFromUrl } from '../helpers/global.helper';\nimport { getCreatorImage, getCreatorName, getCreatorUrl } from '../helpers/search-creator.helper';\nimport { getAvatar, getUser, getUserRealName, getUserUrl } from '../helpers/search-user.helper';\nimport {\n getSearchColorRating,\n getSearchOrigins,\n getSearchPoster,\n getSearchTitle,\n getSearchType,\n getSearchUrl,\n getSearchYear,\n parseSearchPeople\n} from '../helpers/search.helper';\nimport { CSFDLanguage, CSFDOptions } from '../types';\nimport { getUrlByLanguage, searchUrl } from '../vars';\n\nexport class SearchScraper {\n public async search(text: string, options?: CSFDOptions): Promise<CSFDSearch> {\n const url = searchUrl(text, { language: options?.language });\n const response = await fetchPage(url, { ...options?.request });\n\n const html = parse(response);\n const moviesNode = html.querySelectorAll('.main-movies article');\n const usersNode = html.querySelectorAll('.main-users article');\n const tvSeriesNode = html.querySelectorAll('.main-series article');\n const creatorsNode = html.querySelectorAll('.main-authors article');\n\n return this.parseSearch(moviesNode, usersNode, tvSeriesNode, creatorsNode, options?.language);\n }\n\n private parseSearch(\n moviesNode: HTMLElement[],\n usersNode: HTMLElement[],\n tvSeriesNode: HTMLElement[],\n creatorsNode: HTMLElement[],\n language?: CSFDLanguage\n ) {\n const baseUrl = getUrlByLanguage(language);\n\n const movies: CSFDSearchMovie[] = [];\n const users: CSFDSearchUser[] = [];\n const tvSeries: CSFDSearchMovie[] = [];\n const creators: CSFDSearchCreator[] = [];\n\n const movieMapper = (m: HTMLElement): CSFDSearchMovie => {\n const url = getSearchUrl(m);\n return {\n id: parseIdFromUrl(url),\n title: getSearchTitle(m),\n year: getSearchYear(m),\n url: `${baseUrl}${url}`,\n type: getSearchType(m),\n colorRating: getSearchColorRating(m),\n poster: getSearchPoster(m),\n origins: getSearchOrigins(m),\n creators: {\n directors: parseSearchPeople(m, 'directors'),\n actors: parseSearchPeople(m, 'actors')\n }\n };\n };\n\n const userMapper = (m: HTMLElement): CSFDSearchUser => {\n const url = getUserUrl(m);\n return {\n id: parseIdFromUrl(url),\n user: getUser(m),\n userRealName: getUserRealName(m),\n avatar: getAvatar(m),\n url: `${baseUrl}${url}`\n };\n };\n\n const creatorMapper = (m: HTMLElement): CSFDSearchCreator => {\n const url = getCreatorUrl(m);\n return {\n id: parseIdFromUrl(url),\n name: getCreatorName(m),\n image: getCreatorImage(m),\n url: `${baseUrl}${url}`\n };\n };\n\n movies.push(...moviesNode.map(movieMapper));\n users.push(...usersNode.map(userMapper));\n tvSeries.push(...tvSeriesNode.map(movieMapper));\n creators.push(...creatorsNode.map(creatorMapper));\n\n const search: CSFDSearch = {\n movies,\n users,\n tvSeries,\n creators\n };\n return search;\n }\n}\n"],"mappings":";;;;;;;;;AAmBA,IAAa,gBAAb,MAA2B;CACzB,MAAa,OAAO,MAAc,SAA4C;EAI5E,MAAM,mCAFW,MAAMA,wBADXC,uBAAU,MAAM,EAAE,UAAU,SAAS,UAAU,CAAC,EACtB,EAAE,GAAG,SAAS,SAAS,CAAC,CAElC;EAC5B,MAAM,aAAa,KAAK,iBAAiB,uBAAuB;EAChE,MAAM,YAAY,KAAK,iBAAiB,sBAAsB;EAC9D,MAAM,eAAe,KAAK,iBAAiB,uBAAuB;EAClE,MAAM,eAAe,KAAK,iBAAiB,wBAAwB;AAEnE,SAAO,KAAK,YAAY,YAAY,WAAW,cAAc,cAAc,SAAS,SAAS;;CAG/F,AAAQ,YACN,YACA,WACA,cACA,cACA,UACA;EACA,MAAM,UAAUC,8BAAiB,SAAS;EAE1C,MAAM,SAA4B,EAAE;EACpC,MAAM,QAA0B,EAAE;EAClC,MAAM,WAA8B,EAAE;EACtC,MAAM,WAAgC,EAAE;EAExC,MAAM,eAAe,MAAoC;GACvD,MAAM,MAAMC,mCAAa,EAAE;AAC3B,UAAO;IACL,IAAIC,qCAAe,IAAI;IACvB,OAAOC,qCAAe,EAAE;IACxB,MAAMC,oCAAc,EAAE;IACtB,KAAK,GAAG,UAAU;IAClB,MAAMC,oCAAc,EAAE;IACtB,aAAaC,2CAAqB,EAAE;IACpC,QAAQC,sCAAgB,EAAE;IAC1B,SAASC,uCAAiB,EAAE;IAC5B,UAAU;KACR,WAAWC,wCAAkB,GAAG,YAAY;KAC5C,QAAQA,wCAAkB,GAAG,SAAS;KACvC;IACF;;EAGH,MAAM,cAAc,MAAmC;GACrD,MAAM,MAAMC,sCAAW,EAAE;AACzB,UAAO;IACL,IAAIR,qCAAe,IAAI;IACvB,MAAMS,mCAAQ,EAAE;IAChB,cAAcC,2CAAgB,EAAE;IAChC,QAAQC,qCAAU,EAAE;IACpB,KAAK,GAAG,UAAU;IACnB;;EAGH,MAAM,iBAAiB,MAAsC;GAC3D,MAAM,MAAMC,4CAAc,EAAE;AAC5B,UAAO;IACL,IAAIZ,qCAAe,IAAI;IACvB,MAAMa,6CAAe,EAAE;IACvB,OAAOC,8CAAgB,EAAE;IACzB,KAAK,GAAG,UAAU;IACnB;;AAGH,SAAO,KAAK,GAAG,WAAW,IAAI,YAAY,CAAC;AAC3C,QAAM,KAAK,GAAG,UAAU,IAAI,WAAW,CAAC;AACxC,WAAS,KAAK,GAAG,aAAa,IAAI,YAAY,CAAC;AAC/C,WAAS,KAAK,GAAG,aAAa,IAAI,cAAc,CAAC;AAQjD,SAN2B;GACzB;GACA;GACA;GACA;GACD"}
@@ -1,6 +1,7 @@
1
1
  import { fetchPage } from "../fetchers/index.mjs";
2
2
  import { getUrlByLanguage, searchUrl } from "../vars.mjs";
3
3
  import { parseIdFromUrl } from "../helpers/global.helper.mjs";
4
+ import { getCreatorImage, getCreatorName, getCreatorUrl } from "../helpers/search-creator.helper.mjs";
4
5
  import { getAvatar, getUser, getUserRealName, getUserUrl } from "../helpers/search-user.helper.mjs";
5
6
  import { getSearchColorRating, getSearchOrigins, getSearchPoster, getSearchTitle, getSearchType, getSearchUrl, getSearchYear, parseSearchPeople } from "../helpers/search.helper.mjs";
6
7
  import { parse } from "node-html-parser";
@@ -12,13 +13,15 @@ var SearchScraper = class {
12
13
  const moviesNode = html.querySelectorAll(".main-movies article");
13
14
  const usersNode = html.querySelectorAll(".main-users article");
14
15
  const tvSeriesNode = html.querySelectorAll(".main-series article");
15
- return this.parseSearch(moviesNode, usersNode, tvSeriesNode, options?.language);
16
+ const creatorsNode = html.querySelectorAll(".main-authors article");
17
+ return this.parseSearch(moviesNode, usersNode, tvSeriesNode, creatorsNode, options?.language);
16
18
  }
17
- parseSearch(moviesNode, usersNode, tvSeriesNode, language) {
19
+ parseSearch(moviesNode, usersNode, tvSeriesNode, creatorsNode, language) {
18
20
  const baseUrl = getUrlByLanguage(language);
19
21
  const movies = [];
20
22
  const users = [];
21
23
  const tvSeries = [];
24
+ const creators = [];
22
25
  const movieMapper = (m) => {
23
26
  const url = getSearchUrl(m);
24
27
  return {
@@ -46,14 +49,24 @@ var SearchScraper = class {
46
49
  url: `${baseUrl}${url}`
47
50
  };
48
51
  };
52
+ const creatorMapper = (m) => {
53
+ const url = getCreatorUrl(m);
54
+ return {
55
+ id: parseIdFromUrl(url),
56
+ name: getCreatorName(m),
57
+ image: getCreatorImage(m),
58
+ url: `${baseUrl}${url}`
59
+ };
60
+ };
49
61
  movies.push(...moviesNode.map(movieMapper));
50
62
  users.push(...usersNode.map(userMapper));
51
63
  tvSeries.push(...tvSeriesNode.map(movieMapper));
64
+ creators.push(...creatorsNode.map(creatorMapper));
52
65
  return {
53
66
  movies,
54
67
  users,
55
68
  tvSeries,
56
- creators: []
69
+ creators
57
70
  };
58
71
  }
59
72
  };
@@ -1 +1 @@
1
- {"version":3,"file":"search.service.mjs","names":[],"sources":["../../src/services/search.service.ts"],"sourcesContent":["import { HTMLElement, parse } from 'node-html-parser';\nimport { CSFDSearch, CSFDSearchMovie, CSFDSearchUser } from '../dto/search';\nimport { fetchPage } from '../fetchers';\nimport { parseIdFromUrl } from '../helpers/global.helper';\nimport { getAvatar, getUser, getUserRealName, getUserUrl } from '../helpers/search-user.helper';\nimport {\n getSearchColorRating,\n getSearchOrigins,\n getSearchPoster,\n getSearchTitle,\n getSearchType,\n getSearchUrl,\n getSearchYear,\n parseSearchPeople\n} from '../helpers/search.helper';\nimport { CSFDLanguage, CSFDOptions } from '../types';\nimport { getUrlByLanguage, searchUrl } from '../vars';\n\nexport class SearchScraper {\n public async search(text: string, options?: CSFDOptions): Promise<CSFDSearch> {\n const url = searchUrl(text, { language: options?.language });\n const response = await fetchPage(url, { ...options?.request });\n\n const html = parse(response);\n const moviesNode = html.querySelectorAll('.main-movies article');\n const usersNode = html.querySelectorAll('.main-users article');\n const tvSeriesNode = html.querySelectorAll('.main-series article');\n\n return this.parseSearch(moviesNode, usersNode, tvSeriesNode, options?.language);\n }\n\n private parseSearch(\n moviesNode: HTMLElement[],\n usersNode: HTMLElement[],\n tvSeriesNode: HTMLElement[],\n language?: CSFDLanguage\n ) {\n const baseUrl = getUrlByLanguage(language);\n\n const movies: CSFDSearchMovie[] = [];\n const users: CSFDSearchUser[] = [];\n const tvSeries: CSFDSearchMovie[] = [];\n\n const movieMapper = (m: HTMLElement): CSFDSearchMovie => {\n const url = getSearchUrl(m);\n return {\n id: parseIdFromUrl(url),\n title: getSearchTitle(m),\n year: getSearchYear(m),\n url: `${baseUrl}${url}`,\n type: getSearchType(m),\n colorRating: getSearchColorRating(m),\n poster: getSearchPoster(m),\n origins: getSearchOrigins(m),\n creators: {\n directors: parseSearchPeople(m, 'directors'),\n actors: parseSearchPeople(m, 'actors')\n }\n };\n };\n\n const userMapper = (m: HTMLElement): CSFDSearchUser => {\n const url = getUserUrl(m);\n return {\n id: parseIdFromUrl(url),\n user: getUser(m),\n userRealName: getUserRealName(m),\n avatar: getAvatar(m),\n url: `${baseUrl}${url}`\n };\n };\n\n movies.push(...moviesNode.map(movieMapper));\n users.push(...usersNode.map(userMapper));\n tvSeries.push(...tvSeriesNode.map(movieMapper));\n\n const search: CSFDSearch = {\n movies: movies,\n users: users,\n tvSeries: tvSeries,\n creators: []\n };\n return search;\n }\n}\n"],"mappings":";;;;;;;;AAkBA,IAAa,gBAAb,MAA2B;CACzB,MAAa,OAAO,MAAc,SAA4C;EAI5E,MAAM,OAAO,MAFI,MAAM,UADX,UAAU,MAAM,EAAE,UAAU,SAAS,UAAU,CAAC,EACtB,EAAE,GAAG,SAAS,SAAS,CAAC,CAElC;EAC5B,MAAM,aAAa,KAAK,iBAAiB,uBAAuB;EAChE,MAAM,YAAY,KAAK,iBAAiB,sBAAsB;EAC9D,MAAM,eAAe,KAAK,iBAAiB,uBAAuB;AAElE,SAAO,KAAK,YAAY,YAAY,WAAW,cAAc,SAAS,SAAS;;CAGjF,AAAQ,YACN,YACA,WACA,cACA,UACA;EACA,MAAM,UAAU,iBAAiB,SAAS;EAE1C,MAAM,SAA4B,EAAE;EACpC,MAAM,QAA0B,EAAE;EAClC,MAAM,WAA8B,EAAE;EAEtC,MAAM,eAAe,MAAoC;GACvD,MAAM,MAAM,aAAa,EAAE;AAC3B,UAAO;IACL,IAAI,eAAe,IAAI;IACvB,OAAO,eAAe,EAAE;IACxB,MAAM,cAAc,EAAE;IACtB,KAAK,GAAG,UAAU;IAClB,MAAM,cAAc,EAAE;IACtB,aAAa,qBAAqB,EAAE;IACpC,QAAQ,gBAAgB,EAAE;IAC1B,SAAS,iBAAiB,EAAE;IAC5B,UAAU;KACR,WAAW,kBAAkB,GAAG,YAAY;KAC5C,QAAQ,kBAAkB,GAAG,SAAS;KACvC;IACF;;EAGH,MAAM,cAAc,MAAmC;GACrD,MAAM,MAAM,WAAW,EAAE;AACzB,UAAO;IACL,IAAI,eAAe,IAAI;IACvB,MAAM,QAAQ,EAAE;IAChB,cAAc,gBAAgB,EAAE;IAChC,QAAQ,UAAU,EAAE;IACpB,KAAK,GAAG,UAAU;IACnB;;AAGH,SAAO,KAAK,GAAG,WAAW,IAAI,YAAY,CAAC;AAC3C,QAAM,KAAK,GAAG,UAAU,IAAI,WAAW,CAAC;AACxC,WAAS,KAAK,GAAG,aAAa,IAAI,YAAY,CAAC;AAQ/C,SAN2B;GACjB;GACD;GACG;GACV,UAAU,EAAE;GACb"}
1
+ {"version":3,"file":"search.service.mjs","names":[],"sources":["../../src/services/search.service.ts"],"sourcesContent":["import { HTMLElement, parse } from 'node-html-parser';\nimport { CSFDSearch, CSFDSearchCreator, CSFDSearchMovie, CSFDSearchUser } from '../dto/search';\nimport { fetchPage } from '../fetchers';\nimport { parseIdFromUrl } from '../helpers/global.helper';\nimport { getCreatorImage, getCreatorName, getCreatorUrl } from '../helpers/search-creator.helper';\nimport { getAvatar, getUser, getUserRealName, getUserUrl } from '../helpers/search-user.helper';\nimport {\n getSearchColorRating,\n getSearchOrigins,\n getSearchPoster,\n getSearchTitle,\n getSearchType,\n getSearchUrl,\n getSearchYear,\n parseSearchPeople\n} from '../helpers/search.helper';\nimport { CSFDLanguage, CSFDOptions } from '../types';\nimport { getUrlByLanguage, searchUrl } from '../vars';\n\nexport class SearchScraper {\n public async search(text: string, options?: CSFDOptions): Promise<CSFDSearch> {\n const url = searchUrl(text, { language: options?.language });\n const response = await fetchPage(url, { ...options?.request });\n\n const html = parse(response);\n const moviesNode = html.querySelectorAll('.main-movies article');\n const usersNode = html.querySelectorAll('.main-users article');\n const tvSeriesNode = html.querySelectorAll('.main-series article');\n const creatorsNode = html.querySelectorAll('.main-authors article');\n\n return this.parseSearch(moviesNode, usersNode, tvSeriesNode, creatorsNode, options?.language);\n }\n\n private parseSearch(\n moviesNode: HTMLElement[],\n usersNode: HTMLElement[],\n tvSeriesNode: HTMLElement[],\n creatorsNode: HTMLElement[],\n language?: CSFDLanguage\n ) {\n const baseUrl = getUrlByLanguage(language);\n\n const movies: CSFDSearchMovie[] = [];\n const users: CSFDSearchUser[] = [];\n const tvSeries: CSFDSearchMovie[] = [];\n const creators: CSFDSearchCreator[] = [];\n\n const movieMapper = (m: HTMLElement): CSFDSearchMovie => {\n const url = getSearchUrl(m);\n return {\n id: parseIdFromUrl(url),\n title: getSearchTitle(m),\n year: getSearchYear(m),\n url: `${baseUrl}${url}`,\n type: getSearchType(m),\n colorRating: getSearchColorRating(m),\n poster: getSearchPoster(m),\n origins: getSearchOrigins(m),\n creators: {\n directors: parseSearchPeople(m, 'directors'),\n actors: parseSearchPeople(m, 'actors')\n }\n };\n };\n\n const userMapper = (m: HTMLElement): CSFDSearchUser => {\n const url = getUserUrl(m);\n return {\n id: parseIdFromUrl(url),\n user: getUser(m),\n userRealName: getUserRealName(m),\n avatar: getAvatar(m),\n url: `${baseUrl}${url}`\n };\n };\n\n const creatorMapper = (m: HTMLElement): CSFDSearchCreator => {\n const url = getCreatorUrl(m);\n return {\n id: parseIdFromUrl(url),\n name: getCreatorName(m),\n image: getCreatorImage(m),\n url: `${baseUrl}${url}`\n };\n };\n\n movies.push(...moviesNode.map(movieMapper));\n users.push(...usersNode.map(userMapper));\n tvSeries.push(...tvSeriesNode.map(movieMapper));\n creators.push(...creatorsNode.map(creatorMapper));\n\n const search: CSFDSearch = {\n movies,\n users,\n tvSeries,\n creators\n };\n return search;\n }\n}\n"],"mappings":";;;;;;;;;AAmBA,IAAa,gBAAb,MAA2B;CACzB,MAAa,OAAO,MAAc,SAA4C;EAI5E,MAAM,OAAO,MAFI,MAAM,UADX,UAAU,MAAM,EAAE,UAAU,SAAS,UAAU,CAAC,EACtB,EAAE,GAAG,SAAS,SAAS,CAAC,CAElC;EAC5B,MAAM,aAAa,KAAK,iBAAiB,uBAAuB;EAChE,MAAM,YAAY,KAAK,iBAAiB,sBAAsB;EAC9D,MAAM,eAAe,KAAK,iBAAiB,uBAAuB;EAClE,MAAM,eAAe,KAAK,iBAAiB,wBAAwB;AAEnE,SAAO,KAAK,YAAY,YAAY,WAAW,cAAc,cAAc,SAAS,SAAS;;CAG/F,AAAQ,YACN,YACA,WACA,cACA,cACA,UACA;EACA,MAAM,UAAU,iBAAiB,SAAS;EAE1C,MAAM,SAA4B,EAAE;EACpC,MAAM,QAA0B,EAAE;EAClC,MAAM,WAA8B,EAAE;EACtC,MAAM,WAAgC,EAAE;EAExC,MAAM,eAAe,MAAoC;GACvD,MAAM,MAAM,aAAa,EAAE;AAC3B,UAAO;IACL,IAAI,eAAe,IAAI;IACvB,OAAO,eAAe,EAAE;IACxB,MAAM,cAAc,EAAE;IACtB,KAAK,GAAG,UAAU;IAClB,MAAM,cAAc,EAAE;IACtB,aAAa,qBAAqB,EAAE;IACpC,QAAQ,gBAAgB,EAAE;IAC1B,SAAS,iBAAiB,EAAE;IAC5B,UAAU;KACR,WAAW,kBAAkB,GAAG,YAAY;KAC5C,QAAQ,kBAAkB,GAAG,SAAS;KACvC;IACF;;EAGH,MAAM,cAAc,MAAmC;GACrD,MAAM,MAAM,WAAW,EAAE;AACzB,UAAO;IACL,IAAI,eAAe,IAAI;IACvB,MAAM,QAAQ,EAAE;IAChB,cAAc,gBAAgB,EAAE;IAChC,QAAQ,UAAU,EAAE;IACpB,KAAK,GAAG,UAAU;IACnB;;EAGH,MAAM,iBAAiB,MAAsC;GAC3D,MAAM,MAAM,cAAc,EAAE;AAC5B,UAAO;IACL,IAAI,eAAe,IAAI;IACvB,MAAM,eAAe,EAAE;IACvB,OAAO,gBAAgB,EAAE;IACzB,KAAK,GAAG,UAAU;IACnB;;AAGH,SAAO,KAAK,GAAG,WAAW,IAAI,YAAY,CAAC;AAC3C,QAAM,KAAK,GAAG,UAAU,IAAI,WAAW,CAAC;AACxC,WAAS,KAAK,GAAG,aAAa,IAAI,YAAY,CAAC;AAC/C,WAAS,KAAK,GAAG,aAAa,IAAI,cAAc,CAAC;AAQjD,SAN2B;GACzB;GACA;GACA;GACA;GACD"}
@@ -35,12 +35,14 @@ var UserRatingsScraper = class {
35
35
  You can not use both parameters 'includesOnly' and 'excludes'.
36
36
  Parameter 'includesOnly' will be used now:`, config.includesOnly);
37
37
  }
38
+ const includesSet = config?.includesOnly?.length ? new Set(config.includesOnly) : null;
39
+ const excludesSet = config?.excludes?.length ? new Set(config.excludes) : null;
38
40
  for (const el of movies) {
39
41
  const type = require_user_ratings_helper.getUserRatingType(el);
40
- if (config?.includesOnly?.length) {
41
- if (config.includesOnly.some((include) => type === include)) films.push(this.buildUserRatings(el, type));
42
- } else if (config?.excludes?.length) {
43
- if (!config.excludes.some((exclude) => type === exclude)) films.push(this.buildUserRatings(el, type));
42
+ if (includesSet) {
43
+ if (includesSet.has(type)) films.push(this.buildUserRatings(el, type));
44
+ } else if (excludesSet) {
45
+ if (!excludesSet.has(type)) films.push(this.buildUserRatings(el, type));
44
46
  } else films.push(this.buildUserRatings(el, type));
45
47
  }
46
48
  return films;
@@ -1 +1 @@
1
- {"version":3,"file":"user-ratings.service.js","names":["userRatingsUrl","fetchPage","sleep","getUserRatingType","getUserRatingId","getUserRatingTitle","getUserRatingYear","getUserRatingUrl","getUserRatingColorRating","getUserRatingDate","getUserRating"],"sources":["../../src/services/user-ratings.service.ts"],"sourcesContent":["import { HTMLElement, parse } from 'node-html-parser';\nimport { CSFDColorRating, CSFDFilmTypes, CSFDStars } from '../dto/global';\nimport { CSFDUserRatingConfig, CSFDUserRatings } from '../dto/user-ratings';\nimport { fetchPage } from '../fetchers';\nimport { sleep } from '../helpers/global.helper';\nimport {\n getUserRating,\n getUserRatingColorRating,\n getUserRatingDate,\n getUserRatingId,\n getUserRatingTitle,\n getUserRatingType,\n getUserRatingUrl,\n getUserRatingYear\n} from '../helpers/user-ratings.helper';\nimport { CSFDOptions } from '../types';\nimport { userRatingsUrl } from '../vars';\n\nexport class UserRatingsScraper {\n public async userRatings(\n user: string | number,\n config?: CSFDUserRatingConfig,\n options?: CSFDOptions\n ): Promise<CSFDUserRatings[]> {\n let allMovies: CSFDUserRatings[] = [];\n const pageToFetch = config?.page || 1;\n const url = userRatingsUrl(user, pageToFetch > 1 ? pageToFetch : undefined, {\n language: options?.language\n });\n const response = await fetchPage(url, { ...options?.request });\n const items = parse(response);\n const movies = items.querySelectorAll('#snippet--ratings table tr');\n\n // Get number of pages\n const pagesNode = items.querySelector('.pagination');\n const pages = +pagesNode?.childNodes[pagesNode.childNodes.length - 4].rawText || 1;\n\n allMovies = this.getPage(config, movies);\n\n if (config?.allPages) {\n console.log('User', user, url);\n console.log('Fetching all pages', pages);\n for (let i = 2; i <= pages; i++) {\n console.log('Fetching page', i, 'out of', pages, '...');\n const url = userRatingsUrl(user, i, { language: options?.language });\n const response = await fetchPage(url, { ...options?.request });\n\n const items = parse(response);\n const movies = items.querySelectorAll('#snippet--ratings table tr');\n allMovies = [...allMovies, ...this.getPage(config, movies)];\n\n // Sleep\n if (config.allPagesDelay) {\n await sleep(config.allPagesDelay);\n }\n }\n return allMovies;\n }\n\n return allMovies;\n }\n\n private getPage(config: CSFDUserRatingConfig, movies: HTMLElement[]) {\n const films: CSFDUserRatings[] = [];\n if (config) {\n if (config.includesOnly?.length && config.excludes?.length) {\n console.warn(\n `node-csfd-api:\n You can not use both parameters 'includesOnly' and 'excludes'.\n Parameter 'includesOnly' will be used now:`,\n config.includesOnly\n );\n }\n }\n\n for (const el of movies) {\n const type = getUserRatingType(el);\n\n // Filtering includesOnly\n if (config?.includesOnly?.length) {\n if (config.includesOnly.some((include) => type === include)) {\n films.push(this.buildUserRatings(el, type));\n }\n // Filter excludes\n } else if (config?.excludes?.length) {\n if (!config.excludes.some((exclude) => type === exclude)) {\n films.push(this.buildUserRatings(el, type));\n }\n } else {\n // Without filtering\n films.push(this.buildUserRatings(el, type));\n }\n }\n return films;\n }\n\n private buildUserRatings(el: HTMLElement, type: CSFDFilmTypes): CSFDUserRatings {\n return {\n id: getUserRatingId(el),\n title: getUserRatingTitle(el),\n year: getUserRatingYear(el),\n type,\n url: getUserRatingUrl(el),\n colorRating: getUserRatingColorRating(el) as CSFDColorRating,\n userDate: getUserRatingDate(el),\n userRating: getUserRating(el) as CSFDStars\n };\n }\n}\n"],"mappings":";;;;;;;AAkBA,IAAa,qBAAb,MAAgC;CAC9B,MAAa,YACX,MACA,QACA,SAC4B;EAC5B,IAAI,YAA+B,EAAE;EACrC,MAAM,cAAc,QAAQ,QAAQ;EACpC,MAAM,MAAMA,4BAAe,MAAM,cAAc,IAAI,cAAc,QAAW,EAC1E,UAAU,SAAS,UACpB,CAAC;EAEF,MAAM,oCADW,MAAMC,wBAAU,KAAK,EAAE,GAAG,SAAS,SAAS,CAAC,CACjC;EAC7B,MAAM,SAAS,MAAM,iBAAiB,6BAA6B;EAGnE,MAAM,YAAY,MAAM,cAAc,cAAc;EACpD,MAAM,QAAQ,CAAC,WAAW,WAAW,UAAU,WAAW,SAAS,GAAG,WAAW;AAEjF,cAAY,KAAK,QAAQ,QAAQ,OAAO;AAExC,MAAI,QAAQ,UAAU;AACpB,WAAQ,IAAI,QAAQ,MAAM,IAAI;AAC9B,WAAQ,IAAI,sBAAsB,MAAM;AACxC,QAAK,IAAI,IAAI,GAAG,KAAK,OAAO,KAAK;AAC/B,YAAQ,IAAI,iBAAiB,GAAG,UAAU,OAAO,MAAM;IAKvD,MAAM,qCAHW,MAAMA,wBADXD,4BAAe,MAAM,GAAG,EAAE,UAAU,SAAS,UAAU,CAAC,EAC9B,EAAE,GAAG,SAAS,SAAS,CAAC,CAEjC,CACR,iBAAiB,6BAA6B;AACnE,gBAAY,CAAC,GAAG,WAAW,GAAG,KAAK,QAAQ,QAAQ,OAAO,CAAC;AAG3D,QAAI,OAAO,cACT,OAAME,4BAAM,OAAO,cAAc;;AAGrC,UAAO;;AAGT,SAAO;;CAGT,AAAQ,QAAQ,QAA8B,QAAuB;EACnE,MAAM,QAA2B,EAAE;AACnC,MAAI,QACF;OAAI,OAAO,cAAc,UAAU,OAAO,UAAU,OAClD,SAAQ,KACN;;qDAGA,OAAO,aACR;;AAIL,OAAK,MAAM,MAAM,QAAQ;GACvB,MAAM,OAAOC,8CAAkB,GAAG;AAGlC,OAAI,QAAQ,cAAc,QACxB;QAAI,OAAO,aAAa,MAAM,YAAY,SAAS,QAAQ,CACzD,OAAM,KAAK,KAAK,iBAAiB,IAAI,KAAK,CAAC;cAGpC,QAAQ,UAAU,QAC3B;QAAI,CAAC,OAAO,SAAS,MAAM,YAAY,SAAS,QAAQ,CACtD,OAAM,KAAK,KAAK,iBAAiB,IAAI,KAAK,CAAC;SAI7C,OAAM,KAAK,KAAK,iBAAiB,IAAI,KAAK,CAAC;;AAG/C,SAAO;;CAGT,AAAQ,iBAAiB,IAAiB,MAAsC;AAC9E,SAAO;GACL,IAAIC,4CAAgB,GAAG;GACvB,OAAOC,+CAAmB,GAAG;GAC7B,MAAMC,8CAAkB,GAAG;GAC3B;GACA,KAAKC,6CAAiB,GAAG;GACzB,aAAaC,qDAAyB,GAAG;GACzC,UAAUC,8CAAkB,GAAG;GAC/B,YAAYC,0CAAc,GAAG;GAC9B"}
1
+ {"version":3,"file":"user-ratings.service.js","names":["userRatingsUrl","fetchPage","sleep","getUserRatingType","getUserRatingId","getUserRatingTitle","getUserRatingYear","getUserRatingUrl","getUserRatingColorRating","getUserRatingDate","getUserRating"],"sources":["../../src/services/user-ratings.service.ts"],"sourcesContent":["import { HTMLElement, parse } from 'node-html-parser';\nimport { CSFDColorRating, CSFDFilmTypes, CSFDStars } from '../dto/global';\nimport { CSFDUserRatingConfig, CSFDUserRatings } from '../dto/user-ratings';\nimport { fetchPage } from '../fetchers';\nimport { sleep } from '../helpers/global.helper';\nimport {\n getUserRating,\n getUserRatingColorRating,\n getUserRatingDate,\n getUserRatingId,\n getUserRatingTitle,\n getUserRatingType,\n getUserRatingUrl,\n getUserRatingYear\n} from '../helpers/user-ratings.helper';\nimport { CSFDOptions } from '../types';\nimport { userRatingsUrl } from '../vars';\n\nexport class UserRatingsScraper {\n public async userRatings(\n user: string | number,\n config?: CSFDUserRatingConfig,\n options?: CSFDOptions\n ): Promise<CSFDUserRatings[]> {\n let allMovies: CSFDUserRatings[] = [];\n const pageToFetch = config?.page || 1;\n const url = userRatingsUrl(user, pageToFetch > 1 ? pageToFetch : undefined, {\n language: options?.language\n });\n const response = await fetchPage(url, { ...options?.request });\n const items = parse(response);\n const movies = items.querySelectorAll('#snippet--ratings table tr');\n\n // Get number of pages\n const pagesNode = items.querySelector('.pagination');\n const pages = +pagesNode?.childNodes[pagesNode.childNodes.length - 4].rawText || 1;\n\n allMovies = this.getPage(config, movies);\n\n if (config?.allPages) {\n console.log('User', user, url);\n console.log('Fetching all pages', pages);\n for (let i = 2; i <= pages; i++) {\n console.log('Fetching page', i, 'out of', pages, '...');\n const url = userRatingsUrl(user, i, { language: options?.language });\n const response = await fetchPage(url, { ...options?.request });\n\n const items = parse(response);\n const movies = items.querySelectorAll('#snippet--ratings table tr');\n allMovies = [...allMovies, ...this.getPage(config, movies)];\n\n // Sleep\n if (config.allPagesDelay) {\n await sleep(config.allPagesDelay);\n }\n }\n return allMovies;\n }\n\n return allMovies;\n }\n\n private getPage(config: CSFDUserRatingConfig, movies: HTMLElement[]) {\n const films: CSFDUserRatings[] = [];\n if (config) {\n if (config.includesOnly?.length && config.excludes?.length) {\n console.warn(\n `node-csfd-api:\n You can not use both parameters 'includesOnly' and 'excludes'.\n Parameter 'includesOnly' will be used now:`,\n config.includesOnly\n );\n }\n }\n\n const includesSet = config?.includesOnly?.length ? new Set(config.includesOnly) : null;\n const excludesSet = config?.excludes?.length ? new Set(config.excludes) : null;\n\n for (const el of movies) {\n const type = getUserRatingType(el);\n\n // Filtering includesOnly\n if (includesSet) {\n if (includesSet.has(type)) {\n films.push(this.buildUserRatings(el, type));\n }\n // Filter excludes\n } else if (excludesSet) {\n if (!excludesSet.has(type)) {\n films.push(this.buildUserRatings(el, type));\n }\n } else {\n // Without filtering\n films.push(this.buildUserRatings(el, type));\n }\n }\n return films;\n }\n\n private buildUserRatings(el: HTMLElement, type: CSFDFilmTypes): CSFDUserRatings {\n return {\n id: getUserRatingId(el),\n title: getUserRatingTitle(el),\n year: getUserRatingYear(el),\n type,\n url: getUserRatingUrl(el),\n colorRating: getUserRatingColorRating(el) as CSFDColorRating,\n userDate: getUserRatingDate(el),\n userRating: getUserRating(el) as CSFDStars\n };\n }\n}\n"],"mappings":";;;;;;;AAkBA,IAAa,qBAAb,MAAgC;CAC9B,MAAa,YACX,MACA,QACA,SAC4B;EAC5B,IAAI,YAA+B,EAAE;EACrC,MAAM,cAAc,QAAQ,QAAQ;EACpC,MAAM,MAAMA,4BAAe,MAAM,cAAc,IAAI,cAAc,QAAW,EAC1E,UAAU,SAAS,UACpB,CAAC;EAEF,MAAM,oCADW,MAAMC,wBAAU,KAAK,EAAE,GAAG,SAAS,SAAS,CAAC,CACjC;EAC7B,MAAM,SAAS,MAAM,iBAAiB,6BAA6B;EAGnE,MAAM,YAAY,MAAM,cAAc,cAAc;EACpD,MAAM,QAAQ,CAAC,WAAW,WAAW,UAAU,WAAW,SAAS,GAAG,WAAW;AAEjF,cAAY,KAAK,QAAQ,QAAQ,OAAO;AAExC,MAAI,QAAQ,UAAU;AACpB,WAAQ,IAAI,QAAQ,MAAM,IAAI;AAC9B,WAAQ,IAAI,sBAAsB,MAAM;AACxC,QAAK,IAAI,IAAI,GAAG,KAAK,OAAO,KAAK;AAC/B,YAAQ,IAAI,iBAAiB,GAAG,UAAU,OAAO,MAAM;IAKvD,MAAM,qCAHW,MAAMA,wBADXD,4BAAe,MAAM,GAAG,EAAE,UAAU,SAAS,UAAU,CAAC,EAC9B,EAAE,GAAG,SAAS,SAAS,CAAC,CAEjC,CACR,iBAAiB,6BAA6B;AACnE,gBAAY,CAAC,GAAG,WAAW,GAAG,KAAK,QAAQ,QAAQ,OAAO,CAAC;AAG3D,QAAI,OAAO,cACT,OAAME,4BAAM,OAAO,cAAc;;AAGrC,UAAO;;AAGT,SAAO;;CAGT,AAAQ,QAAQ,QAA8B,QAAuB;EACnE,MAAM,QAA2B,EAAE;AACnC,MAAI,QACF;OAAI,OAAO,cAAc,UAAU,OAAO,UAAU,OAClD,SAAQ,KACN;;qDAGA,OAAO,aACR;;EAIL,MAAM,cAAc,QAAQ,cAAc,SAAS,IAAI,IAAI,OAAO,aAAa,GAAG;EAClF,MAAM,cAAc,QAAQ,UAAU,SAAS,IAAI,IAAI,OAAO,SAAS,GAAG;AAE1E,OAAK,MAAM,MAAM,QAAQ;GACvB,MAAM,OAAOC,8CAAkB,GAAG;AAGlC,OAAI,aACF;QAAI,YAAY,IAAI,KAAK,CACvB,OAAM,KAAK,KAAK,iBAAiB,IAAI,KAAK,CAAC;cAGpC,aACT;QAAI,CAAC,YAAY,IAAI,KAAK,CACxB,OAAM,KAAK,KAAK,iBAAiB,IAAI,KAAK,CAAC;SAI7C,OAAM,KAAK,KAAK,iBAAiB,IAAI,KAAK,CAAC;;AAG/C,SAAO;;CAGT,AAAQ,iBAAiB,IAAiB,MAAsC;AAC9E,SAAO;GACL,IAAIC,4CAAgB,GAAG;GACvB,OAAOC,+CAAmB,GAAG;GAC7B,MAAMC,8CAAkB,GAAG;GAC3B;GACA,KAAKC,6CAAiB,GAAG;GACzB,aAAaC,qDAAyB,GAAG;GACzC,UAAUC,8CAAkB,GAAG;GAC/B,YAAYC,0CAAc,GAAG;GAC9B"}
@@ -35,12 +35,14 @@ var UserRatingsScraper = class {
35
35
  You can not use both parameters 'includesOnly' and 'excludes'.
36
36
  Parameter 'includesOnly' will be used now:`, config.includesOnly);
37
37
  }
38
+ const includesSet = config?.includesOnly?.length ? new Set(config.includesOnly) : null;
39
+ const excludesSet = config?.excludes?.length ? new Set(config.excludes) : null;
38
40
  for (const el of movies) {
39
41
  const type = getUserRatingType(el);
40
- if (config?.includesOnly?.length) {
41
- if (config.includesOnly.some((include) => type === include)) films.push(this.buildUserRatings(el, type));
42
- } else if (config?.excludes?.length) {
43
- if (!config.excludes.some((exclude) => type === exclude)) films.push(this.buildUserRatings(el, type));
42
+ if (includesSet) {
43
+ if (includesSet.has(type)) films.push(this.buildUserRatings(el, type));
44
+ } else if (excludesSet) {
45
+ if (!excludesSet.has(type)) films.push(this.buildUserRatings(el, type));
44
46
  } else films.push(this.buildUserRatings(el, type));
45
47
  }
46
48
  return films;
@@ -1 +1 @@
1
- {"version":3,"file":"user-ratings.service.mjs","names":[],"sources":["../../src/services/user-ratings.service.ts"],"sourcesContent":["import { HTMLElement, parse } from 'node-html-parser';\nimport { CSFDColorRating, CSFDFilmTypes, CSFDStars } from '../dto/global';\nimport { CSFDUserRatingConfig, CSFDUserRatings } from '../dto/user-ratings';\nimport { fetchPage } from '../fetchers';\nimport { sleep } from '../helpers/global.helper';\nimport {\n getUserRating,\n getUserRatingColorRating,\n getUserRatingDate,\n getUserRatingId,\n getUserRatingTitle,\n getUserRatingType,\n getUserRatingUrl,\n getUserRatingYear\n} from '../helpers/user-ratings.helper';\nimport { CSFDOptions } from '../types';\nimport { userRatingsUrl } from '../vars';\n\nexport class UserRatingsScraper {\n public async userRatings(\n user: string | number,\n config?: CSFDUserRatingConfig,\n options?: CSFDOptions\n ): Promise<CSFDUserRatings[]> {\n let allMovies: CSFDUserRatings[] = [];\n const pageToFetch = config?.page || 1;\n const url = userRatingsUrl(user, pageToFetch > 1 ? pageToFetch : undefined, {\n language: options?.language\n });\n const response = await fetchPage(url, { ...options?.request });\n const items = parse(response);\n const movies = items.querySelectorAll('#snippet--ratings table tr');\n\n // Get number of pages\n const pagesNode = items.querySelector('.pagination');\n const pages = +pagesNode?.childNodes[pagesNode.childNodes.length - 4].rawText || 1;\n\n allMovies = this.getPage(config, movies);\n\n if (config?.allPages) {\n console.log('User', user, url);\n console.log('Fetching all pages', pages);\n for (let i = 2; i <= pages; i++) {\n console.log('Fetching page', i, 'out of', pages, '...');\n const url = userRatingsUrl(user, i, { language: options?.language });\n const response = await fetchPage(url, { ...options?.request });\n\n const items = parse(response);\n const movies = items.querySelectorAll('#snippet--ratings table tr');\n allMovies = [...allMovies, ...this.getPage(config, movies)];\n\n // Sleep\n if (config.allPagesDelay) {\n await sleep(config.allPagesDelay);\n }\n }\n return allMovies;\n }\n\n return allMovies;\n }\n\n private getPage(config: CSFDUserRatingConfig, movies: HTMLElement[]) {\n const films: CSFDUserRatings[] = [];\n if (config) {\n if (config.includesOnly?.length && config.excludes?.length) {\n console.warn(\n `node-csfd-api:\n You can not use both parameters 'includesOnly' and 'excludes'.\n Parameter 'includesOnly' will be used now:`,\n config.includesOnly\n );\n }\n }\n\n for (const el of movies) {\n const type = getUserRatingType(el);\n\n // Filtering includesOnly\n if (config?.includesOnly?.length) {\n if (config.includesOnly.some((include) => type === include)) {\n films.push(this.buildUserRatings(el, type));\n }\n // Filter excludes\n } else if (config?.excludes?.length) {\n if (!config.excludes.some((exclude) => type === exclude)) {\n films.push(this.buildUserRatings(el, type));\n }\n } else {\n // Without filtering\n films.push(this.buildUserRatings(el, type));\n }\n }\n return films;\n }\n\n private buildUserRatings(el: HTMLElement, type: CSFDFilmTypes): CSFDUserRatings {\n return {\n id: getUserRatingId(el),\n title: getUserRatingTitle(el),\n year: getUserRatingYear(el),\n type,\n url: getUserRatingUrl(el),\n colorRating: getUserRatingColorRating(el) as CSFDColorRating,\n userDate: getUserRatingDate(el),\n userRating: getUserRating(el) as CSFDStars\n };\n }\n}\n"],"mappings":";;;;;;;AAkBA,IAAa,qBAAb,MAAgC;CAC9B,MAAa,YACX,MACA,QACA,SAC4B;EAC5B,IAAI,YAA+B,EAAE;EACrC,MAAM,cAAc,QAAQ,QAAQ;EACpC,MAAM,MAAM,eAAe,MAAM,cAAc,IAAI,cAAc,QAAW,EAC1E,UAAU,SAAS,UACpB,CAAC;EAEF,MAAM,QAAQ,MADG,MAAM,UAAU,KAAK,EAAE,GAAG,SAAS,SAAS,CAAC,CACjC;EAC7B,MAAM,SAAS,MAAM,iBAAiB,6BAA6B;EAGnE,MAAM,YAAY,MAAM,cAAc,cAAc;EACpD,MAAM,QAAQ,CAAC,WAAW,WAAW,UAAU,WAAW,SAAS,GAAG,WAAW;AAEjF,cAAY,KAAK,QAAQ,QAAQ,OAAO;AAExC,MAAI,QAAQ,UAAU;AACpB,WAAQ,IAAI,QAAQ,MAAM,IAAI;AAC9B,WAAQ,IAAI,sBAAsB,MAAM;AACxC,QAAK,IAAI,IAAI,GAAG,KAAK,OAAO,KAAK;AAC/B,YAAQ,IAAI,iBAAiB,GAAG,UAAU,OAAO,MAAM;IAKvD,MAAM,SADQ,MAFG,MAAM,UADX,eAAe,MAAM,GAAG,EAAE,UAAU,SAAS,UAAU,CAAC,EAC9B,EAAE,GAAG,SAAS,SAAS,CAAC,CAEjC,CACR,iBAAiB,6BAA6B;AACnE,gBAAY,CAAC,GAAG,WAAW,GAAG,KAAK,QAAQ,QAAQ,OAAO,CAAC;AAG3D,QAAI,OAAO,cACT,OAAM,MAAM,OAAO,cAAc;;AAGrC,UAAO;;AAGT,SAAO;;CAGT,AAAQ,QAAQ,QAA8B,QAAuB;EACnE,MAAM,QAA2B,EAAE;AACnC,MAAI,QACF;OAAI,OAAO,cAAc,UAAU,OAAO,UAAU,OAClD,SAAQ,KACN;;qDAGA,OAAO,aACR;;AAIL,OAAK,MAAM,MAAM,QAAQ;GACvB,MAAM,OAAO,kBAAkB,GAAG;AAGlC,OAAI,QAAQ,cAAc,QACxB;QAAI,OAAO,aAAa,MAAM,YAAY,SAAS,QAAQ,CACzD,OAAM,KAAK,KAAK,iBAAiB,IAAI,KAAK,CAAC;cAGpC,QAAQ,UAAU,QAC3B;QAAI,CAAC,OAAO,SAAS,MAAM,YAAY,SAAS,QAAQ,CACtD,OAAM,KAAK,KAAK,iBAAiB,IAAI,KAAK,CAAC;SAI7C,OAAM,KAAK,KAAK,iBAAiB,IAAI,KAAK,CAAC;;AAG/C,SAAO;;CAGT,AAAQ,iBAAiB,IAAiB,MAAsC;AAC9E,SAAO;GACL,IAAI,gBAAgB,GAAG;GACvB,OAAO,mBAAmB,GAAG;GAC7B,MAAM,kBAAkB,GAAG;GAC3B;GACA,KAAK,iBAAiB,GAAG;GACzB,aAAa,yBAAyB,GAAG;GACzC,UAAU,kBAAkB,GAAG;GAC/B,YAAY,cAAc,GAAG;GAC9B"}
1
+ {"version":3,"file":"user-ratings.service.mjs","names":[],"sources":["../../src/services/user-ratings.service.ts"],"sourcesContent":["import { HTMLElement, parse } from 'node-html-parser';\nimport { CSFDColorRating, CSFDFilmTypes, CSFDStars } from '../dto/global';\nimport { CSFDUserRatingConfig, CSFDUserRatings } from '../dto/user-ratings';\nimport { fetchPage } from '../fetchers';\nimport { sleep } from '../helpers/global.helper';\nimport {\n getUserRating,\n getUserRatingColorRating,\n getUserRatingDate,\n getUserRatingId,\n getUserRatingTitle,\n getUserRatingType,\n getUserRatingUrl,\n getUserRatingYear\n} from '../helpers/user-ratings.helper';\nimport { CSFDOptions } from '../types';\nimport { userRatingsUrl } from '../vars';\n\nexport class UserRatingsScraper {\n public async userRatings(\n user: string | number,\n config?: CSFDUserRatingConfig,\n options?: CSFDOptions\n ): Promise<CSFDUserRatings[]> {\n let allMovies: CSFDUserRatings[] = [];\n const pageToFetch = config?.page || 1;\n const url = userRatingsUrl(user, pageToFetch > 1 ? pageToFetch : undefined, {\n language: options?.language\n });\n const response = await fetchPage(url, { ...options?.request });\n const items = parse(response);\n const movies = items.querySelectorAll('#snippet--ratings table tr');\n\n // Get number of pages\n const pagesNode = items.querySelector('.pagination');\n const pages = +pagesNode?.childNodes[pagesNode.childNodes.length - 4].rawText || 1;\n\n allMovies = this.getPage(config, movies);\n\n if (config?.allPages) {\n console.log('User', user, url);\n console.log('Fetching all pages', pages);\n for (let i = 2; i <= pages; i++) {\n console.log('Fetching page', i, 'out of', pages, '...');\n const url = userRatingsUrl(user, i, { language: options?.language });\n const response = await fetchPage(url, { ...options?.request });\n\n const items = parse(response);\n const movies = items.querySelectorAll('#snippet--ratings table tr');\n allMovies = [...allMovies, ...this.getPage(config, movies)];\n\n // Sleep\n if (config.allPagesDelay) {\n await sleep(config.allPagesDelay);\n }\n }\n return allMovies;\n }\n\n return allMovies;\n }\n\n private getPage(config: CSFDUserRatingConfig, movies: HTMLElement[]) {\n const films: CSFDUserRatings[] = [];\n if (config) {\n if (config.includesOnly?.length && config.excludes?.length) {\n console.warn(\n `node-csfd-api:\n You can not use both parameters 'includesOnly' and 'excludes'.\n Parameter 'includesOnly' will be used now:`,\n config.includesOnly\n );\n }\n }\n\n const includesSet = config?.includesOnly?.length ? new Set(config.includesOnly) : null;\n const excludesSet = config?.excludes?.length ? new Set(config.excludes) : null;\n\n for (const el of movies) {\n const type = getUserRatingType(el);\n\n // Filtering includesOnly\n if (includesSet) {\n if (includesSet.has(type)) {\n films.push(this.buildUserRatings(el, type));\n }\n // Filter excludes\n } else if (excludesSet) {\n if (!excludesSet.has(type)) {\n films.push(this.buildUserRatings(el, type));\n }\n } else {\n // Without filtering\n films.push(this.buildUserRatings(el, type));\n }\n }\n return films;\n }\n\n private buildUserRatings(el: HTMLElement, type: CSFDFilmTypes): CSFDUserRatings {\n return {\n id: getUserRatingId(el),\n title: getUserRatingTitle(el),\n year: getUserRatingYear(el),\n type,\n url: getUserRatingUrl(el),\n colorRating: getUserRatingColorRating(el) as CSFDColorRating,\n userDate: getUserRatingDate(el),\n userRating: getUserRating(el) as CSFDStars\n };\n }\n}\n"],"mappings":";;;;;;;AAkBA,IAAa,qBAAb,MAAgC;CAC9B,MAAa,YACX,MACA,QACA,SAC4B;EAC5B,IAAI,YAA+B,EAAE;EACrC,MAAM,cAAc,QAAQ,QAAQ;EACpC,MAAM,MAAM,eAAe,MAAM,cAAc,IAAI,cAAc,QAAW,EAC1E,UAAU,SAAS,UACpB,CAAC;EAEF,MAAM,QAAQ,MADG,MAAM,UAAU,KAAK,EAAE,GAAG,SAAS,SAAS,CAAC,CACjC;EAC7B,MAAM,SAAS,MAAM,iBAAiB,6BAA6B;EAGnE,MAAM,YAAY,MAAM,cAAc,cAAc;EACpD,MAAM,QAAQ,CAAC,WAAW,WAAW,UAAU,WAAW,SAAS,GAAG,WAAW;AAEjF,cAAY,KAAK,QAAQ,QAAQ,OAAO;AAExC,MAAI,QAAQ,UAAU;AACpB,WAAQ,IAAI,QAAQ,MAAM,IAAI;AAC9B,WAAQ,IAAI,sBAAsB,MAAM;AACxC,QAAK,IAAI,IAAI,GAAG,KAAK,OAAO,KAAK;AAC/B,YAAQ,IAAI,iBAAiB,GAAG,UAAU,OAAO,MAAM;IAKvD,MAAM,SADQ,MAFG,MAAM,UADX,eAAe,MAAM,GAAG,EAAE,UAAU,SAAS,UAAU,CAAC,EAC9B,EAAE,GAAG,SAAS,SAAS,CAAC,CAEjC,CACR,iBAAiB,6BAA6B;AACnE,gBAAY,CAAC,GAAG,WAAW,GAAG,KAAK,QAAQ,QAAQ,OAAO,CAAC;AAG3D,QAAI,OAAO,cACT,OAAM,MAAM,OAAO,cAAc;;AAGrC,UAAO;;AAGT,SAAO;;CAGT,AAAQ,QAAQ,QAA8B,QAAuB;EACnE,MAAM,QAA2B,EAAE;AACnC,MAAI,QACF;OAAI,OAAO,cAAc,UAAU,OAAO,UAAU,OAClD,SAAQ,KACN;;qDAGA,OAAO,aACR;;EAIL,MAAM,cAAc,QAAQ,cAAc,SAAS,IAAI,IAAI,OAAO,aAAa,GAAG;EAClF,MAAM,cAAc,QAAQ,UAAU,SAAS,IAAI,IAAI,OAAO,SAAS,GAAG;AAE1E,OAAK,MAAM,MAAM,QAAQ;GACvB,MAAM,OAAO,kBAAkB,GAAG;AAGlC,OAAI,aACF;QAAI,YAAY,IAAI,KAAK,CACvB,OAAM,KAAK,KAAK,iBAAiB,IAAI,KAAK,CAAC;cAGpC,aACT;QAAI,CAAC,YAAY,IAAI,KAAK,CACxB,OAAM,KAAK,KAAK,iBAAiB,IAAI,KAAK,CAAC;SAI7C,OAAM,KAAK,KAAK,iBAAiB,IAAI,KAAK,CAAC;;AAG/C,SAAO;;CAGT,AAAQ,iBAAiB,IAAiB,MAAsC;AAC9E,SAAO;GACL,IAAI,gBAAgB,GAAG;GACvB,OAAO,mBAAmB,GAAG;GAC7B,MAAM,kBAAkB,GAAG;GAC3B;GACA,KAAK,iBAAiB,GAAG;GACzB,aAAa,yBAAyB,GAAG;GACzC,UAAU,kBAAkB,GAAG;GAC/B,YAAY,cAAc,GAAG;GAC9B"}
@@ -35,12 +35,14 @@ var UserReviewsScraper = class {
35
35
  You can not use both parameters 'includesOnly' and 'excludes'.
36
36
  Parameter 'includesOnly' will be used now:`, config.includesOnly);
37
37
  }
38
+ const includesSet = config?.includesOnly?.length ? new Set(config.includesOnly) : null;
39
+ const excludesSet = config?.excludes?.length ? new Set(config.excludes) : null;
38
40
  for (const el of reviews) {
39
41
  const type = require_user_reviews_helper.getUserReviewType(el);
40
- if (config?.includesOnly?.length) {
41
- if (config.includesOnly.some((include) => type === include)) films.push(this.buildUserReviews(el, type));
42
- } else if (config?.excludes?.length) {
43
- if (!config.excludes.some((exclude) => type === exclude)) films.push(this.buildUserReviews(el, type));
42
+ if (includesSet) {
43
+ if (includesSet.has(type)) films.push(this.buildUserReviews(el, type));
44
+ } else if (excludesSet) {
45
+ if (!excludesSet.has(type)) films.push(this.buildUserReviews(el, type));
44
46
  } else films.push(this.buildUserReviews(el, type));
45
47
  }
46
48
  return films;
@@ -1 +1 @@
1
- {"version":3,"file":"user-reviews.service.js","names":["userReviewsUrl","fetchPage","sleep","getUserReviewType","getUserReviewId","getUserReviewTitle","getUserReviewYear","getUserReviewUrl","getUserReviewColorRating","getUserReviewDate","getUserReviewRating","getUserReviewText","getUserReviewPoster"],"sources":["../../src/services/user-reviews.service.ts"],"sourcesContent":["import { HTMLElement, parse } from 'node-html-parser';\nimport { CSFDColorRating, CSFDFilmTypes, CSFDStars } from '../dto/global';\nimport { CSFDUserReviews, CSFDUserReviewsConfig } from '../dto/user-reviews';\nimport { fetchPage } from '../fetchers';\nimport { sleep } from '../helpers/global.helper';\nimport {\n getUserReviewColorRating,\n getUserReviewDate,\n getUserReviewId,\n getUserReviewPoster,\n getUserReviewRating,\n getUserReviewText,\n getUserReviewTitle,\n getUserReviewType,\n getUserReviewUrl,\n getUserReviewYear\n} from '../helpers/user-reviews.helper';\nimport { CSFDOptions } from '../types';\nimport { userReviewsUrl } from '../vars';\n\nexport class UserReviewsScraper {\n public async userReviews(\n user: string | number,\n config?: CSFDUserReviewsConfig,\n options?: CSFDOptions\n ): Promise<CSFDUserReviews[]> {\n let allReviews: CSFDUserReviews[] = [];\n const pageToFetch = config?.page || 1;\n const url = userReviewsUrl(user, pageToFetch > 1 ? pageToFetch : undefined, {\n language: options?.language\n });\n const response = await fetchPage(url, { ...options?.request });\n const items = parse(response);\n const reviews = items.querySelectorAll('.user-tab-reviews .article');\n\n // Get number of pages\n const pagesNode = items.querySelector('.pagination');\n const pages = +pagesNode?.childNodes[pagesNode.childNodes.length - 4].rawText || 1;\n\n allReviews = this.getPage(config, reviews);\n\n if (config?.allPages) {\n console.log('User', user, url);\n console.log('Fetching all pages', pages);\n for (let i = 2; i <= pages; i++) {\n console.log('Fetching page', i, 'out of', pages, '...');\n const url = userReviewsUrl(user, i, { language: options?.language });\n const response = await fetchPage(url, { ...options?.request });\n\n const items = parse(response);\n const reviews = items.querySelectorAll('.user-tab-reviews .article');\n allReviews = [...allReviews, ...this.getPage(config, reviews)];\n\n // Sleep\n if (config.allPagesDelay) {\n await sleep(config.allPagesDelay);\n }\n }\n return allReviews;\n }\n\n return allReviews;\n }\n\n private getPage(config: CSFDUserReviewsConfig, reviews: HTMLElement[]) {\n const films: CSFDUserReviews[] = [];\n if (config) {\n if (config.includesOnly?.length && config.excludes?.length) {\n console.warn(\n `node-csfd-api:\n You can not use both parameters 'includesOnly' and 'excludes'.\n Parameter 'includesOnly' will be used now:`,\n config.includesOnly\n );\n }\n }\n\n for (const el of reviews) {\n const type = getUserReviewType(el);\n\n // Filtering includesOnly\n if (config?.includesOnly?.length) {\n if (config.includesOnly.some((include) => type === include)) {\n films.push(this.buildUserReviews(el, type));\n }\n // Filter excludes\n } else if (config?.excludes?.length) {\n if (!config.excludes.some((exclude) => type === exclude)) {\n films.push(this.buildUserReviews(el, type));\n }\n } else {\n // Without filtering\n films.push(this.buildUserReviews(el, type));\n }\n }\n return films;\n }\n\n private buildUserReviews(el: HTMLElement, type: CSFDFilmTypes): CSFDUserReviews {\n return {\n id: getUserReviewId(el),\n title: getUserReviewTitle(el),\n year: getUserReviewYear(el),\n type,\n url: getUserReviewUrl(el),\n colorRating: getUserReviewColorRating(el) as CSFDColorRating,\n userDate: getUserReviewDate(el),\n userRating: getUserReviewRating(el) as CSFDStars,\n text: getUserReviewText(el),\n poster: getUserReviewPoster(el)\n };\n }\n}\n"],"mappings":";;;;;;;AAoBA,IAAa,qBAAb,MAAgC;CAC9B,MAAa,YACX,MACA,QACA,SAC4B;EAC5B,IAAI,aAAgC,EAAE;EACtC,MAAM,cAAc,QAAQ,QAAQ;EACpC,MAAM,MAAMA,4BAAe,MAAM,cAAc,IAAI,cAAc,QAAW,EAC1E,UAAU,SAAS,UACpB,CAAC;EAEF,MAAM,oCADW,MAAMC,wBAAU,KAAK,EAAE,GAAG,SAAS,SAAS,CAAC,CACjC;EAC7B,MAAM,UAAU,MAAM,iBAAiB,6BAA6B;EAGpE,MAAM,YAAY,MAAM,cAAc,cAAc;EACpD,MAAM,QAAQ,CAAC,WAAW,WAAW,UAAU,WAAW,SAAS,GAAG,WAAW;AAEjF,eAAa,KAAK,QAAQ,QAAQ,QAAQ;AAE1C,MAAI,QAAQ,UAAU;AACpB,WAAQ,IAAI,QAAQ,MAAM,IAAI;AAC9B,WAAQ,IAAI,sBAAsB,MAAM;AACxC,QAAK,IAAI,IAAI,GAAG,KAAK,OAAO,KAAK;AAC/B,YAAQ,IAAI,iBAAiB,GAAG,UAAU,OAAO,MAAM;IAKvD,MAAM,sCAHW,MAAMA,wBADXD,4BAAe,MAAM,GAAG,EAAE,UAAU,SAAS,UAAU,CAAC,EAC9B,EAAE,GAAG,SAAS,SAAS,CAAC,CAEjC,CACP,iBAAiB,6BAA6B;AACpE,iBAAa,CAAC,GAAG,YAAY,GAAG,KAAK,QAAQ,QAAQ,QAAQ,CAAC;AAG9D,QAAI,OAAO,cACT,OAAME,4BAAM,OAAO,cAAc;;AAGrC,UAAO;;AAGT,SAAO;;CAGT,AAAQ,QAAQ,QAA+B,SAAwB;EACrE,MAAM,QAA2B,EAAE;AACnC,MAAI,QACF;OAAI,OAAO,cAAc,UAAU,OAAO,UAAU,OAClD,SAAQ,KACN;;qDAGA,OAAO,aACR;;AAIL,OAAK,MAAM,MAAM,SAAS;GACxB,MAAM,OAAOC,8CAAkB,GAAG;AAGlC,OAAI,QAAQ,cAAc,QACxB;QAAI,OAAO,aAAa,MAAM,YAAY,SAAS,QAAQ,CACzD,OAAM,KAAK,KAAK,iBAAiB,IAAI,KAAK,CAAC;cAGpC,QAAQ,UAAU,QAC3B;QAAI,CAAC,OAAO,SAAS,MAAM,YAAY,SAAS,QAAQ,CACtD,OAAM,KAAK,KAAK,iBAAiB,IAAI,KAAK,CAAC;SAI7C,OAAM,KAAK,KAAK,iBAAiB,IAAI,KAAK,CAAC;;AAG/C,SAAO;;CAGT,AAAQ,iBAAiB,IAAiB,MAAsC;AAC9E,SAAO;GACL,IAAIC,4CAAgB,GAAG;GACvB,OAAOC,+CAAmB,GAAG;GAC7B,MAAMC,8CAAkB,GAAG;GAC3B;GACA,KAAKC,6CAAiB,GAAG;GACzB,aAAaC,qDAAyB,GAAG;GACzC,UAAUC,8CAAkB,GAAG;GAC/B,YAAYC,gDAAoB,GAAG;GACnC,MAAMC,8CAAkB,GAAG;GAC3B,QAAQC,gDAAoB,GAAG;GAChC"}
1
+ {"version":3,"file":"user-reviews.service.js","names":["userReviewsUrl","fetchPage","sleep","getUserReviewType","getUserReviewId","getUserReviewTitle","getUserReviewYear","getUserReviewUrl","getUserReviewColorRating","getUserReviewDate","getUserReviewRating","getUserReviewText","getUserReviewPoster"],"sources":["../../src/services/user-reviews.service.ts"],"sourcesContent":["import { HTMLElement, parse } from 'node-html-parser';\nimport { CSFDColorRating, CSFDFilmTypes, CSFDStars } from '../dto/global';\nimport { CSFDUserReviews, CSFDUserReviewsConfig } from '../dto/user-reviews';\nimport { fetchPage } from '../fetchers';\nimport { sleep } from '../helpers/global.helper';\nimport {\n getUserReviewColorRating,\n getUserReviewDate,\n getUserReviewId,\n getUserReviewPoster,\n getUserReviewRating,\n getUserReviewText,\n getUserReviewTitle,\n getUserReviewType,\n getUserReviewUrl,\n getUserReviewYear\n} from '../helpers/user-reviews.helper';\nimport { CSFDOptions } from '../types';\nimport { userReviewsUrl } from '../vars';\n\nexport class UserReviewsScraper {\n public async userReviews(\n user: string | number,\n config?: CSFDUserReviewsConfig,\n options?: CSFDOptions\n ): Promise<CSFDUserReviews[]> {\n let allReviews: CSFDUserReviews[] = [];\n const pageToFetch = config?.page || 1;\n const url = userReviewsUrl(user, pageToFetch > 1 ? pageToFetch : undefined, {\n language: options?.language\n });\n const response = await fetchPage(url, { ...options?.request });\n const items = parse(response);\n const reviews = items.querySelectorAll('.user-tab-reviews .article');\n\n // Get number of pages\n const pagesNode = items.querySelector('.pagination');\n const pages = +pagesNode?.childNodes[pagesNode.childNodes.length - 4].rawText || 1;\n\n allReviews = this.getPage(config, reviews);\n\n if (config?.allPages) {\n console.log('User', user, url);\n console.log('Fetching all pages', pages);\n for (let i = 2; i <= pages; i++) {\n console.log('Fetching page', i, 'out of', pages, '...');\n const url = userReviewsUrl(user, i, { language: options?.language });\n const response = await fetchPage(url, { ...options?.request });\n\n const items = parse(response);\n const reviews = items.querySelectorAll('.user-tab-reviews .article');\n allReviews = [...allReviews, ...this.getPage(config, reviews)];\n\n // Sleep\n if (config.allPagesDelay) {\n await sleep(config.allPagesDelay);\n }\n }\n return allReviews;\n }\n\n return allReviews;\n }\n\n private getPage(config: CSFDUserReviewsConfig, reviews: HTMLElement[]) {\n const films: CSFDUserReviews[] = [];\n if (config) {\n if (config.includesOnly?.length && config.excludes?.length) {\n console.warn(\n `node-csfd-api:\n You can not use both parameters 'includesOnly' and 'excludes'.\n Parameter 'includesOnly' will be used now:`,\n config.includesOnly\n );\n }\n }\n\n const includesSet = config?.includesOnly?.length ? new Set(config.includesOnly) : null;\n const excludesSet = config?.excludes?.length ? new Set(config.excludes) : null;\n\n for (const el of reviews) {\n const type = getUserReviewType(el);\n\n // Filtering includesOnly\n if (includesSet) {\n if (includesSet.has(type)) {\n films.push(this.buildUserReviews(el, type));\n }\n // Filter excludes\n } else if (excludesSet) {\n if (!excludesSet.has(type)) {\n films.push(this.buildUserReviews(el, type));\n }\n } else {\n // Without filtering\n films.push(this.buildUserReviews(el, type));\n }\n }\n return films;\n }\n\n private buildUserReviews(el: HTMLElement, type: CSFDFilmTypes): CSFDUserReviews {\n return {\n id: getUserReviewId(el),\n title: getUserReviewTitle(el),\n year: getUserReviewYear(el),\n type,\n url: getUserReviewUrl(el),\n colorRating: getUserReviewColorRating(el) as CSFDColorRating,\n userDate: getUserReviewDate(el),\n userRating: getUserReviewRating(el) as CSFDStars,\n text: getUserReviewText(el),\n poster: getUserReviewPoster(el)\n };\n }\n}\n"],"mappings":";;;;;;;AAoBA,IAAa,qBAAb,MAAgC;CAC9B,MAAa,YACX,MACA,QACA,SAC4B;EAC5B,IAAI,aAAgC,EAAE;EACtC,MAAM,cAAc,QAAQ,QAAQ;EACpC,MAAM,MAAMA,4BAAe,MAAM,cAAc,IAAI,cAAc,QAAW,EAC1E,UAAU,SAAS,UACpB,CAAC;EAEF,MAAM,oCADW,MAAMC,wBAAU,KAAK,EAAE,GAAG,SAAS,SAAS,CAAC,CACjC;EAC7B,MAAM,UAAU,MAAM,iBAAiB,6BAA6B;EAGpE,MAAM,YAAY,MAAM,cAAc,cAAc;EACpD,MAAM,QAAQ,CAAC,WAAW,WAAW,UAAU,WAAW,SAAS,GAAG,WAAW;AAEjF,eAAa,KAAK,QAAQ,QAAQ,QAAQ;AAE1C,MAAI,QAAQ,UAAU;AACpB,WAAQ,IAAI,QAAQ,MAAM,IAAI;AAC9B,WAAQ,IAAI,sBAAsB,MAAM;AACxC,QAAK,IAAI,IAAI,GAAG,KAAK,OAAO,KAAK;AAC/B,YAAQ,IAAI,iBAAiB,GAAG,UAAU,OAAO,MAAM;IAKvD,MAAM,sCAHW,MAAMA,wBADXD,4BAAe,MAAM,GAAG,EAAE,UAAU,SAAS,UAAU,CAAC,EAC9B,EAAE,GAAG,SAAS,SAAS,CAAC,CAEjC,CACP,iBAAiB,6BAA6B;AACpE,iBAAa,CAAC,GAAG,YAAY,GAAG,KAAK,QAAQ,QAAQ,QAAQ,CAAC;AAG9D,QAAI,OAAO,cACT,OAAME,4BAAM,OAAO,cAAc;;AAGrC,UAAO;;AAGT,SAAO;;CAGT,AAAQ,QAAQ,QAA+B,SAAwB;EACrE,MAAM,QAA2B,EAAE;AACnC,MAAI,QACF;OAAI,OAAO,cAAc,UAAU,OAAO,UAAU,OAClD,SAAQ,KACN;;qDAGA,OAAO,aACR;;EAIL,MAAM,cAAc,QAAQ,cAAc,SAAS,IAAI,IAAI,OAAO,aAAa,GAAG;EAClF,MAAM,cAAc,QAAQ,UAAU,SAAS,IAAI,IAAI,OAAO,SAAS,GAAG;AAE1E,OAAK,MAAM,MAAM,SAAS;GACxB,MAAM,OAAOC,8CAAkB,GAAG;AAGlC,OAAI,aACF;QAAI,YAAY,IAAI,KAAK,CACvB,OAAM,KAAK,KAAK,iBAAiB,IAAI,KAAK,CAAC;cAGpC,aACT;QAAI,CAAC,YAAY,IAAI,KAAK,CACxB,OAAM,KAAK,KAAK,iBAAiB,IAAI,KAAK,CAAC;SAI7C,OAAM,KAAK,KAAK,iBAAiB,IAAI,KAAK,CAAC;;AAG/C,SAAO;;CAGT,AAAQ,iBAAiB,IAAiB,MAAsC;AAC9E,SAAO;GACL,IAAIC,4CAAgB,GAAG;GACvB,OAAOC,+CAAmB,GAAG;GAC7B,MAAMC,8CAAkB,GAAG;GAC3B;GACA,KAAKC,6CAAiB,GAAG;GACzB,aAAaC,qDAAyB,GAAG;GACzC,UAAUC,8CAAkB,GAAG;GAC/B,YAAYC,gDAAoB,GAAG;GACnC,MAAMC,8CAAkB,GAAG;GAC3B,QAAQC,gDAAoB,GAAG;GAChC"}
@@ -35,12 +35,14 @@ var UserReviewsScraper = class {
35
35
  You can not use both parameters 'includesOnly' and 'excludes'.
36
36
  Parameter 'includesOnly' will be used now:`, config.includesOnly);
37
37
  }
38
+ const includesSet = config?.includesOnly?.length ? new Set(config.includesOnly) : null;
39
+ const excludesSet = config?.excludes?.length ? new Set(config.excludes) : null;
38
40
  for (const el of reviews) {
39
41
  const type = getUserReviewType(el);
40
- if (config?.includesOnly?.length) {
41
- if (config.includesOnly.some((include) => type === include)) films.push(this.buildUserReviews(el, type));
42
- } else if (config?.excludes?.length) {
43
- if (!config.excludes.some((exclude) => type === exclude)) films.push(this.buildUserReviews(el, type));
42
+ if (includesSet) {
43
+ if (includesSet.has(type)) films.push(this.buildUserReviews(el, type));
44
+ } else if (excludesSet) {
45
+ if (!excludesSet.has(type)) films.push(this.buildUserReviews(el, type));
44
46
  } else films.push(this.buildUserReviews(el, type));
45
47
  }
46
48
  return films;
@@ -1 +1 @@
1
- {"version":3,"file":"user-reviews.service.mjs","names":[],"sources":["../../src/services/user-reviews.service.ts"],"sourcesContent":["import { HTMLElement, parse } from 'node-html-parser';\nimport { CSFDColorRating, CSFDFilmTypes, CSFDStars } from '../dto/global';\nimport { CSFDUserReviews, CSFDUserReviewsConfig } from '../dto/user-reviews';\nimport { fetchPage } from '../fetchers';\nimport { sleep } from '../helpers/global.helper';\nimport {\n getUserReviewColorRating,\n getUserReviewDate,\n getUserReviewId,\n getUserReviewPoster,\n getUserReviewRating,\n getUserReviewText,\n getUserReviewTitle,\n getUserReviewType,\n getUserReviewUrl,\n getUserReviewYear\n} from '../helpers/user-reviews.helper';\nimport { CSFDOptions } from '../types';\nimport { userReviewsUrl } from '../vars';\n\nexport class UserReviewsScraper {\n public async userReviews(\n user: string | number,\n config?: CSFDUserReviewsConfig,\n options?: CSFDOptions\n ): Promise<CSFDUserReviews[]> {\n let allReviews: CSFDUserReviews[] = [];\n const pageToFetch = config?.page || 1;\n const url = userReviewsUrl(user, pageToFetch > 1 ? pageToFetch : undefined, {\n language: options?.language\n });\n const response = await fetchPage(url, { ...options?.request });\n const items = parse(response);\n const reviews = items.querySelectorAll('.user-tab-reviews .article');\n\n // Get number of pages\n const pagesNode = items.querySelector('.pagination');\n const pages = +pagesNode?.childNodes[pagesNode.childNodes.length - 4].rawText || 1;\n\n allReviews = this.getPage(config, reviews);\n\n if (config?.allPages) {\n console.log('User', user, url);\n console.log('Fetching all pages', pages);\n for (let i = 2; i <= pages; i++) {\n console.log('Fetching page', i, 'out of', pages, '...');\n const url = userReviewsUrl(user, i, { language: options?.language });\n const response = await fetchPage(url, { ...options?.request });\n\n const items = parse(response);\n const reviews = items.querySelectorAll('.user-tab-reviews .article');\n allReviews = [...allReviews, ...this.getPage(config, reviews)];\n\n // Sleep\n if (config.allPagesDelay) {\n await sleep(config.allPagesDelay);\n }\n }\n return allReviews;\n }\n\n return allReviews;\n }\n\n private getPage(config: CSFDUserReviewsConfig, reviews: HTMLElement[]) {\n const films: CSFDUserReviews[] = [];\n if (config) {\n if (config.includesOnly?.length && config.excludes?.length) {\n console.warn(\n `node-csfd-api:\n You can not use both parameters 'includesOnly' and 'excludes'.\n Parameter 'includesOnly' will be used now:`,\n config.includesOnly\n );\n }\n }\n\n for (const el of reviews) {\n const type = getUserReviewType(el);\n\n // Filtering includesOnly\n if (config?.includesOnly?.length) {\n if (config.includesOnly.some((include) => type === include)) {\n films.push(this.buildUserReviews(el, type));\n }\n // Filter excludes\n } else if (config?.excludes?.length) {\n if (!config.excludes.some((exclude) => type === exclude)) {\n films.push(this.buildUserReviews(el, type));\n }\n } else {\n // Without filtering\n films.push(this.buildUserReviews(el, type));\n }\n }\n return films;\n }\n\n private buildUserReviews(el: HTMLElement, type: CSFDFilmTypes): CSFDUserReviews {\n return {\n id: getUserReviewId(el),\n title: getUserReviewTitle(el),\n year: getUserReviewYear(el),\n type,\n url: getUserReviewUrl(el),\n colorRating: getUserReviewColorRating(el) as CSFDColorRating,\n userDate: getUserReviewDate(el),\n userRating: getUserReviewRating(el) as CSFDStars,\n text: getUserReviewText(el),\n poster: getUserReviewPoster(el)\n };\n }\n}\n"],"mappings":";;;;;;;AAoBA,IAAa,qBAAb,MAAgC;CAC9B,MAAa,YACX,MACA,QACA,SAC4B;EAC5B,IAAI,aAAgC,EAAE;EACtC,MAAM,cAAc,QAAQ,QAAQ;EACpC,MAAM,MAAM,eAAe,MAAM,cAAc,IAAI,cAAc,QAAW,EAC1E,UAAU,SAAS,UACpB,CAAC;EAEF,MAAM,QAAQ,MADG,MAAM,UAAU,KAAK,EAAE,GAAG,SAAS,SAAS,CAAC,CACjC;EAC7B,MAAM,UAAU,MAAM,iBAAiB,6BAA6B;EAGpE,MAAM,YAAY,MAAM,cAAc,cAAc;EACpD,MAAM,QAAQ,CAAC,WAAW,WAAW,UAAU,WAAW,SAAS,GAAG,WAAW;AAEjF,eAAa,KAAK,QAAQ,QAAQ,QAAQ;AAE1C,MAAI,QAAQ,UAAU;AACpB,WAAQ,IAAI,QAAQ,MAAM,IAAI;AAC9B,WAAQ,IAAI,sBAAsB,MAAM;AACxC,QAAK,IAAI,IAAI,GAAG,KAAK,OAAO,KAAK;AAC/B,YAAQ,IAAI,iBAAiB,GAAG,UAAU,OAAO,MAAM;IAKvD,MAAM,UADQ,MAFG,MAAM,UADX,eAAe,MAAM,GAAG,EAAE,UAAU,SAAS,UAAU,CAAC,EAC9B,EAAE,GAAG,SAAS,SAAS,CAAC,CAEjC,CACP,iBAAiB,6BAA6B;AACpE,iBAAa,CAAC,GAAG,YAAY,GAAG,KAAK,QAAQ,QAAQ,QAAQ,CAAC;AAG9D,QAAI,OAAO,cACT,OAAM,MAAM,OAAO,cAAc;;AAGrC,UAAO;;AAGT,SAAO;;CAGT,AAAQ,QAAQ,QAA+B,SAAwB;EACrE,MAAM,QAA2B,EAAE;AACnC,MAAI,QACF;OAAI,OAAO,cAAc,UAAU,OAAO,UAAU,OAClD,SAAQ,KACN;;qDAGA,OAAO,aACR;;AAIL,OAAK,MAAM,MAAM,SAAS;GACxB,MAAM,OAAO,kBAAkB,GAAG;AAGlC,OAAI,QAAQ,cAAc,QACxB;QAAI,OAAO,aAAa,MAAM,YAAY,SAAS,QAAQ,CACzD,OAAM,KAAK,KAAK,iBAAiB,IAAI,KAAK,CAAC;cAGpC,QAAQ,UAAU,QAC3B;QAAI,CAAC,OAAO,SAAS,MAAM,YAAY,SAAS,QAAQ,CACtD,OAAM,KAAK,KAAK,iBAAiB,IAAI,KAAK,CAAC;SAI7C,OAAM,KAAK,KAAK,iBAAiB,IAAI,KAAK,CAAC;;AAG/C,SAAO;;CAGT,AAAQ,iBAAiB,IAAiB,MAAsC;AAC9E,SAAO;GACL,IAAI,gBAAgB,GAAG;GACvB,OAAO,mBAAmB,GAAG;GAC7B,MAAM,kBAAkB,GAAG;GAC3B;GACA,KAAK,iBAAiB,GAAG;GACzB,aAAa,yBAAyB,GAAG;GACzC,UAAU,kBAAkB,GAAG;GAC/B,YAAY,oBAAoB,GAAG;GACnC,MAAM,kBAAkB,GAAG;GAC3B,QAAQ,oBAAoB,GAAG;GAChC"}
1
+ {"version":3,"file":"user-reviews.service.mjs","names":[],"sources":["../../src/services/user-reviews.service.ts"],"sourcesContent":["import { HTMLElement, parse } from 'node-html-parser';\nimport { CSFDColorRating, CSFDFilmTypes, CSFDStars } from '../dto/global';\nimport { CSFDUserReviews, CSFDUserReviewsConfig } from '../dto/user-reviews';\nimport { fetchPage } from '../fetchers';\nimport { sleep } from '../helpers/global.helper';\nimport {\n getUserReviewColorRating,\n getUserReviewDate,\n getUserReviewId,\n getUserReviewPoster,\n getUserReviewRating,\n getUserReviewText,\n getUserReviewTitle,\n getUserReviewType,\n getUserReviewUrl,\n getUserReviewYear\n} from '../helpers/user-reviews.helper';\nimport { CSFDOptions } from '../types';\nimport { userReviewsUrl } from '../vars';\n\nexport class UserReviewsScraper {\n public async userReviews(\n user: string | number,\n config?: CSFDUserReviewsConfig,\n options?: CSFDOptions\n ): Promise<CSFDUserReviews[]> {\n let allReviews: CSFDUserReviews[] = [];\n const pageToFetch = config?.page || 1;\n const url = userReviewsUrl(user, pageToFetch > 1 ? pageToFetch : undefined, {\n language: options?.language\n });\n const response = await fetchPage(url, { ...options?.request });\n const items = parse(response);\n const reviews = items.querySelectorAll('.user-tab-reviews .article');\n\n // Get number of pages\n const pagesNode = items.querySelector('.pagination');\n const pages = +pagesNode?.childNodes[pagesNode.childNodes.length - 4].rawText || 1;\n\n allReviews = this.getPage(config, reviews);\n\n if (config?.allPages) {\n console.log('User', user, url);\n console.log('Fetching all pages', pages);\n for (let i = 2; i <= pages; i++) {\n console.log('Fetching page', i, 'out of', pages, '...');\n const url = userReviewsUrl(user, i, { language: options?.language });\n const response = await fetchPage(url, { ...options?.request });\n\n const items = parse(response);\n const reviews = items.querySelectorAll('.user-tab-reviews .article');\n allReviews = [...allReviews, ...this.getPage(config, reviews)];\n\n // Sleep\n if (config.allPagesDelay) {\n await sleep(config.allPagesDelay);\n }\n }\n return allReviews;\n }\n\n return allReviews;\n }\n\n private getPage(config: CSFDUserReviewsConfig, reviews: HTMLElement[]) {\n const films: CSFDUserReviews[] = [];\n if (config) {\n if (config.includesOnly?.length && config.excludes?.length) {\n console.warn(\n `node-csfd-api:\n You can not use both parameters 'includesOnly' and 'excludes'.\n Parameter 'includesOnly' will be used now:`,\n config.includesOnly\n );\n }\n }\n\n const includesSet = config?.includesOnly?.length ? new Set(config.includesOnly) : null;\n const excludesSet = config?.excludes?.length ? new Set(config.excludes) : null;\n\n for (const el of reviews) {\n const type = getUserReviewType(el);\n\n // Filtering includesOnly\n if (includesSet) {\n if (includesSet.has(type)) {\n films.push(this.buildUserReviews(el, type));\n }\n // Filter excludes\n } else if (excludesSet) {\n if (!excludesSet.has(type)) {\n films.push(this.buildUserReviews(el, type));\n }\n } else {\n // Without filtering\n films.push(this.buildUserReviews(el, type));\n }\n }\n return films;\n }\n\n private buildUserReviews(el: HTMLElement, type: CSFDFilmTypes): CSFDUserReviews {\n return {\n id: getUserReviewId(el),\n title: getUserReviewTitle(el),\n year: getUserReviewYear(el),\n type,\n url: getUserReviewUrl(el),\n colorRating: getUserReviewColorRating(el) as CSFDColorRating,\n userDate: getUserReviewDate(el),\n userRating: getUserReviewRating(el) as CSFDStars,\n text: getUserReviewText(el),\n poster: getUserReviewPoster(el)\n };\n }\n}\n"],"mappings":";;;;;;;AAoBA,IAAa,qBAAb,MAAgC;CAC9B,MAAa,YACX,MACA,QACA,SAC4B;EAC5B,IAAI,aAAgC,EAAE;EACtC,MAAM,cAAc,QAAQ,QAAQ;EACpC,MAAM,MAAM,eAAe,MAAM,cAAc,IAAI,cAAc,QAAW,EAC1E,UAAU,SAAS,UACpB,CAAC;EAEF,MAAM,QAAQ,MADG,MAAM,UAAU,KAAK,EAAE,GAAG,SAAS,SAAS,CAAC,CACjC;EAC7B,MAAM,UAAU,MAAM,iBAAiB,6BAA6B;EAGpE,MAAM,YAAY,MAAM,cAAc,cAAc;EACpD,MAAM,QAAQ,CAAC,WAAW,WAAW,UAAU,WAAW,SAAS,GAAG,WAAW;AAEjF,eAAa,KAAK,QAAQ,QAAQ,QAAQ;AAE1C,MAAI,QAAQ,UAAU;AACpB,WAAQ,IAAI,QAAQ,MAAM,IAAI;AAC9B,WAAQ,IAAI,sBAAsB,MAAM;AACxC,QAAK,IAAI,IAAI,GAAG,KAAK,OAAO,KAAK;AAC/B,YAAQ,IAAI,iBAAiB,GAAG,UAAU,OAAO,MAAM;IAKvD,MAAM,UADQ,MAFG,MAAM,UADX,eAAe,MAAM,GAAG,EAAE,UAAU,SAAS,UAAU,CAAC,EAC9B,EAAE,GAAG,SAAS,SAAS,CAAC,CAEjC,CACP,iBAAiB,6BAA6B;AACpE,iBAAa,CAAC,GAAG,YAAY,GAAG,KAAK,QAAQ,QAAQ,QAAQ,CAAC;AAG9D,QAAI,OAAO,cACT,OAAM,MAAM,OAAO,cAAc;;AAGrC,UAAO;;AAGT,SAAO;;CAGT,AAAQ,QAAQ,QAA+B,SAAwB;EACrE,MAAM,QAA2B,EAAE;AACnC,MAAI,QACF;OAAI,OAAO,cAAc,UAAU,OAAO,UAAU,OAClD,SAAQ,KACN;;qDAGA,OAAO,aACR;;EAIL,MAAM,cAAc,QAAQ,cAAc,SAAS,IAAI,IAAI,OAAO,aAAa,GAAG;EAClF,MAAM,cAAc,QAAQ,UAAU,SAAS,IAAI,IAAI,OAAO,SAAS,GAAG;AAE1E,OAAK,MAAM,MAAM,SAAS;GACxB,MAAM,OAAO,kBAAkB,GAAG;AAGlC,OAAI,aACF;QAAI,YAAY,IAAI,KAAK,CACvB,OAAM,KAAK,KAAK,iBAAiB,IAAI,KAAK,CAAC;cAGpC,aACT;QAAI,CAAC,YAAY,IAAI,KAAK,CACxB,OAAM,KAAK,KAAK,iBAAiB,IAAI,KAAK,CAAC;SAI7C,OAAM,KAAK,KAAK,iBAAiB,IAAI,KAAK,CAAC;;AAG/C,SAAO;;CAGT,AAAQ,iBAAiB,IAAiB,MAAsC;AAC9E,SAAO;GACL,IAAI,gBAAgB,GAAG;GACvB,OAAO,mBAAmB,GAAG;GAC7B,MAAM,kBAAkB,GAAG;GAC3B;GACA,KAAK,iBAAiB,GAAG;GACzB,aAAa,yBAAyB,GAAG;GACzC,UAAU,kBAAkB,GAAG;GAC/B,YAAY,oBAAoB,GAAG;GACnC,MAAM,kBAAkB,GAAG;GAC3B,QAAQ,oBAAoB,GAAG;GAChC"}