node-csfd-api 3.0.0-next.2 → 3.0.0-next.20

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
@@ -3,29 +3,30 @@
3
3
  [![Build & Publish](https://github.com/bartholomej/node-csfd-api/workflows/Publish/badge.svg)](https://github.com/bartholomej/node-csfd-api/actions)
4
4
  [![codecov](https://codecov.io/gh/bartholomej/node-csfd-api/branch/master/graph/badge.svg?token=YQH9UoVrGP)](https://codecov.io/gh/bartholomej/node-csfd-api)
5
5
 
6
- # CSFD API 2022
6
+ # CSFD API 🎥 2024
7
7
 
8
8
  > JavaScript NPM library for scraping **Czech Movie Database (csfd.cz)**
9
9
  >
10
- > - Browser + Node.js (SSR)
11
10
  > - JavaScript / TypeScript
11
+ > - Browser + Node.js (SSR)
12
12
  > - Tested (~100% Code coverage)
13
- > - ✅ Ready for new ČSFD 2021!
13
+ > - ✅ Ready for new ČSFD 2024!
14
14
  > - You can use in:
15
15
  > - Firebase function
16
16
  > - AWS λ (lambda function)
17
+ > - CloudFlare Worker
17
18
  > - Chrome extension
18
19
  > - React native app
19
- > - ...
20
+ > - Browsers (Pay attention to CORS)
20
21
 
21
- ## Install
22
+ ## 🗜️ Install
22
23
 
23
24
  ```bash
24
- npm install node-csfd-api --save
25
+ npm install node-csfd-api
25
26
  # yarn add node-csfd-api
26
27
  ```
27
28
 
28
- ## Usage and examples
29
+ ## 🛠️ Usage and examples
29
30
 
30
31
  - [Movies and TV Series](#Movie)
31
32
  - [User Ratings](#User-Ratings)
@@ -34,7 +35,7 @@ npm install node-csfd-api --save
34
35
 
35
36
  ### Movie
36
37
 
37
- Get info about [this movie](https://www.csfd.cz/film/535121-na-spatne-strane/komentare/) _(id: 535121)_
38
+ > Get info about [this movie](https://www.csfd.cz/film/535121-na-spatne-strane/komentare/) _(id: 535121)_
38
39
 
39
40
  ```javascript
40
41
  import { csfd } from 'node-csfd-api';
@@ -42,8 +43,9 @@ import { csfd } from 'node-csfd-api';
42
43
  csfd.movie(535121).then((movie) => console.log(movie));
43
44
  ```
44
45
 
45
- #### Results
46
-
46
+ <details>
47
+ <summary>Click here to see full result example</summary>
48
+
47
49
  ```javascript
48
50
  {
49
51
  id: 535121,
@@ -135,10 +137,11 @@ csfd.movie(535121).then((movie) => console.log(movie));
135
137
  ]
136
138
  }
137
139
  ```
140
+ </details>
138
141
 
139
142
  ### Search
140
143
 
141
- > Search movies and users
144
+ > Search movies, users and TV series
142
145
 
143
146
  ```javascript
144
147
  import { csfd } from 'node-csfd-api';
@@ -146,10 +149,11 @@ import { csfd } from 'node-csfd-api';
146
149
  csfd.search('bart').then((search) => console.log(search));
147
150
  ```
148
151
 
149
- #### Results
150
-
152
+ <details>
153
+ <summary>Click here to see full result example</summary>
154
+
151
155
  ```javascript
152
- movies: [
156
+ [
153
157
  {
154
158
  id: 19653,
155
159
  title: 'Black Bart',
@@ -173,6 +177,19 @@ movies: [
173
177
  }
174
178
  }
175
179
  ],
180
+ tvSeries: [
181
+ {
182
+ id: 71924,
183
+ title: 'Království',
184
+ year: 1994,
185
+ url: 'https://www.csfd.cz/film/71924-kralovstvi/',
186
+ type: 'seriál',
187
+ colorRating: 'good',
188
+ poster: 'https://image.pmgstatic.com/cache/resized/w60h85/files/images/film/posters/166/708/166708064_2da697.jpg',
189
+ origins: ['Dánsko'],
190
+ creators: []
191
+ }
192
+ ],
176
193
  users: [
177
194
  {
178
195
  id: 912,
@@ -184,9 +201,11 @@ users: [
184
201
  ]
185
202
  ```
186
203
 
204
+ </details>
205
+
187
206
  ### Creators
188
207
 
189
- > Search creators and filmography
208
+ > Get creator info + filmography
190
209
 
191
210
  ```javascript
192
211
  import { csfd } from 'node-csfd-api';
@@ -194,7 +213,8 @@ import { csfd } from 'node-csfd-api';
194
213
  csfd.creator(2120).then((creator) => console.log(creator));
195
214
  ```
196
215
 
197
- #### Results
216
+ <details>
217
+ <summary>Click here to see full result example</summary>
198
218
 
199
219
  ```javascript
200
220
  {
@@ -271,6 +291,8 @@ csfd.creator(2120).then((creator) => console.log(creator));
271
291
  }
272
292
  ```
273
293
 
294
+ </details>
295
+
274
296
  ### User Ratings
275
297
 
276
298
  #### Last ratings (last page)
@@ -300,7 +322,8 @@ csfd
300
322
  .then((ratings) => console.log(ratings));
301
323
  ```
302
324
 
303
- #### Results
325
+ <details>
326
+ <summary>Click here to see full result example</summary>
304
327
 
305
328
  ```javascript
306
329
  [
@@ -325,7 +348,9 @@ csfd
325
348
  ];
326
349
  ```
327
350
 
328
- #### Options
351
+ </details>
352
+
353
+ #### Options for user ratings
329
354
 
330
355
  | Option | Type | Default | Description |
331
356
  | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | ------- | ------------------------------------------------------ |
@@ -334,15 +359,25 @@ csfd
334
359
  | **allPages** | boolean | false | Get all pages |
335
360
  | **allPagesDelay** | number | 0 | Delay on each page request. In milliseconds |
336
361
 
337
- _Note: You can not use both parameters 'includesOnly' and 'excludes'. Parameter 'includesOnly' has a priority._
362
+ _Note: You can not use both parameters `includesOnly` and `excludes`. Parameter `includesOnly` has a priority._
338
363
 
339
- ## Used by
364
+ ## 🧑‍💻 Used by
340
365
 
341
- - [Dafilms web extension](https://chrome.google.com/webstore/detail/dafilms/hgcgneddmgflnbmhkjnefiobjgobbmdm?hl=en) ([code](https://github.com/bartholomej/dafilms-ext)) – Parser for film ratings (web extension)
342
- - [bartweb.cz](https://bartweb.cz) – **Last seen** section (Firebase function)
343
- - KinoKlub – Mobile application for AeroFilms (native Android + iOS application)
366
+ ### Web extensions
344
367
 
345
- ## Roadmap
368
+ - [Netflix: chrome extension](https://chrome.google.com/webstore/detail/netflix-csfd/eomgekccbddnlpmehgdjmlphndjgnlni) ([code](https://github.com/bartholomej/netflix-csfd-ext))
369
+ - [Dafilms: chrome extension](https://chrome.google.com/webstore/detail/dafilms/hgcgneddmgflnbmhkjnefiobjgobbmdm) ([code](https://github.com/bartholomej/dafilms-ext))
370
+ - [Kviff.tv: chrome extension](https://chrome.google.com/webstore/detail/kvifftv-%20-csfd/ihpngekoejodiligajlppbeedofhnmfm) ([code](https://github.com/bartholomej/kviff-ext))
371
+
372
+ ### Web applications
373
+
374
+ - [bartweb.cz](https://bartweb.cz) – **Last seen** section (**Firebase function**)
375
+
376
+ ### Mobile applications
377
+
378
+ - [KinoKlub](https://play.google.com/store/apps/details?id=com.aquasoup) – Mobile application for AeroFilms (React Native: Android + iOS application)
379
+
380
+ ## 🔮 Roadmap
346
381
 
347
382
  ### Scraping more pages
348
383
 
@@ -381,8 +416,8 @@ _Note: You can not use both parameters 'includesOnly' and 'excludes'. Parameter
381
416
  - [ ] Search
382
417
  - [x] Movies
383
418
  - [x] Users
419
+ - [x] TV Series
384
420
  - [ ] Creators
385
- - [ ] TV Series
386
421
  - [x] Creators
387
422
  - [x] Bio
388
423
  - [x] Movies (TODO categories)
@@ -390,7 +425,7 @@ _Note: You can not use both parameters 'includesOnly' and 'excludes'. Parameter
390
425
  - [x] Last ratings
391
426
  - [x] All pages
392
427
 
393
- ## Development
428
+ ## 🛠️ Development
394
429
 
395
430
  ### Developing and debugging library
396
431
 
@@ -406,22 +441,7 @@ You can find and modify it in [`./demo.ts`](https://github.com/bartholomej/node-
406
441
  yarn demo
407
442
  ```
408
443
 
409
- ## Development (notes for me)
410
-
411
- ### Publish Stable
412
-
413
- ```shell
414
- yarn release:patch
415
- # yarn release:minor
416
- # yarn release:major
417
- ```
418
-
419
- ### Publish next channel
420
-
421
- 1. Bump version `-beta.0` in `package.json`
422
- 2. `yarn release:beta`
423
-
424
- ## Contribution
444
+ ## 🤝 Contribution
425
445
 
426
446
  I welcome you to customize this according to your needs ;)
427
447
 
@@ -433,7 +453,7 @@ Give a ⭐️ if this project helped you!
433
453
 
434
454
  Or if you are brave enough consider [making a donation](https://github.com/sponsors/bartholomej) for some 🍺 or 🍵 ;)
435
455
 
436
- ## Privacy Policy
456
+ ## 🕵️‍♀️ Privacy Policy
437
457
 
438
458
  I DO NOT STORE ANY DATA. PERIOD.
439
459
 
@@ -441,9 +461,9 @@ I physically can't. I have nowhere to store it. I don't even have a server datab
441
461
 
442
462
  That's why, with node-csfd-api, what happens on your device stays on your device till disappear.
443
463
 
444
- ## License
464
+ ## 📝 License
445
465
 
446
- Copyright &copy; 2022 [Lukas Bartak](http://bartweb.cz)
466
+ Copyright &copy; 2020 – 2024 [Lukas Bartak](http://bartweb.cz)
447
467
 
448
468
  Proudly powered by nature 🗻, wind 💨, tea 🍵 and beer 🍺 ;)
449
469
 
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.fetchSafe = void 0;
4
+ // Check if `fetch` is available in global scope (nodejs 18+) or in window (browser). If not, use cross-fetch polyfill.
5
+ const cross_fetch_1 = require("cross-fetch");
6
+ exports.fetchSafe = (typeof fetch === 'function' && fetch) || // ServiceWorker fetch (Cloud Functions + Chrome extension)
7
+ (typeof global === 'object' && global.fetch) || // Node.js 18+ fetch
8
+ (typeof window !== 'undefined' && window.fetch) || // Browser fetch
9
+ cross_fetch_1.fetch; // Polyfill fetch
@@ -1,5 +1,4 @@
1
1
  "use strict";
2
- // import fetch from 'cross-fetch';
3
2
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
4
3
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
5
4
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -11,6 +10,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
11
10
  };
12
11
  Object.defineProperty(exports, "__esModule", { value: true });
13
12
  exports.fetchPage = void 0;
13
+ const fetch_polyfill_1 = require("./fetch.polyfill");
14
14
  const USER_AGENTS = [
15
15
  'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36',
16
16
  'Mozilla/5.0 (iPhone; CPU iPhone OS 14_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/87.0.4280.77 Mobile/15E148 Safari/604.1',
@@ -22,7 +22,7 @@ const headers = {
22
22
  };
23
23
  const fetchPage = (url) => __awaiter(void 0, void 0, void 0, function* () {
24
24
  try {
25
- const response = yield fetch(url, { headers });
25
+ const response = yield (0, fetch_polyfill_1.fetchSafe)(url, { headers });
26
26
  if (response.status >= 400 && response.status < 600) {
27
27
  throw new Error(`node-csfd-api: Bad response ${response.status} for url: ${url}`);
28
28
  }
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseMeta = exports.getFilms = exports.getGroupedFilmsByDate = exports.parseCinema = exports.getCinemaUrl = exports.getCoords = exports.getName = exports.getId = exports.getCinemaId = exports.getColorRating = void 0;
4
+ const global_helper_1 = require("./global.helper");
5
+ const getColorRating = (el) => {
6
+ return (0, global_helper_1.parseColor)(el === null || el === void 0 ? void 0 : el.classNames.split(' ').pop());
7
+ };
8
+ exports.getColorRating = getColorRating;
9
+ const getCinemaId = (el) => {
10
+ var _a;
11
+ const id = (_a = el === null || el === void 0 ? void 0 : el.id) === null || _a === void 0 ? void 0 : _a.split('-')[1];
12
+ return +id;
13
+ };
14
+ exports.getCinemaId = getCinemaId;
15
+ const getId = (url) => {
16
+ if (url) {
17
+ return (0, global_helper_1.parseIdFromUrl)(url);
18
+ }
19
+ return null;
20
+ };
21
+ exports.getId = getId;
22
+ const getName = (el) => {
23
+ return el.querySelector('h1').innerText.trim();
24
+ };
25
+ exports.getName = getName;
26
+ const getCoords = (el) => {
27
+ const link = el === null || el === void 0 ? void 0 : el.querySelector('.box-header img[alt="Google Maps"]').closest('a').getAttribute('href');
28
+ const coords = link.split('q=')[1].split(',');
29
+ const [lat, lng] = coords;
30
+ return { lat: +lat, lng: +lng };
31
+ };
32
+ exports.getCoords = getCoords;
33
+ const getCinemaUrl = (el) => {
34
+ var _a;
35
+ return (_a = el.querySelector('.box-header .cinema-logo a')) === null || _a === void 0 ? void 0 : _a.attributes.href;
36
+ };
37
+ exports.getCinemaUrl = getCinemaUrl;
38
+ const parseCinema = (el) => {
39
+ const title = el.querySelector('.box-header h2').innerText.trim();
40
+ const [city, name] = title.split(' - ');
41
+ return { city, name };
42
+ };
43
+ exports.parseCinema = parseCinema;
44
+ const getGroupedFilmsByDate = (el) => {
45
+ const divs = el.querySelectorAll(':scope > div');
46
+ const getDatesAndFilms = divs
47
+ .map((_, index) => index)
48
+ .filter((index) => index % 2 === 0)
49
+ .map((index) => {
50
+ const [date, films] = divs.slice(index, index + 2);
51
+ const dateText = date === null || date === void 0 ? void 0 : date.innerText.trim();
52
+ return { date: dateText, films: (0, exports.getFilms)('', films) };
53
+ });
54
+ return getDatesAndFilms;
55
+ };
56
+ exports.getGroupedFilmsByDate = getGroupedFilmsByDate;
57
+ const getFilms = (date, el) => {
58
+ const filmNodes = el.querySelectorAll('.cinema-table tr');
59
+ const films = filmNodes.map((filmNode) => {
60
+ var _a, _b, _c, _d;
61
+ const url = (_a = filmNode.querySelector('td.name h3 a')) === null || _a === void 0 ? void 0 : _a.attributes.href;
62
+ const id = (0, exports.getId)(url);
63
+ const title = (_b = filmNode.querySelector('.name h3')) === null || _b === void 0 ? void 0 : _b.text.trim();
64
+ const colorRating = (0, exports.getColorRating)(filmNode.querySelector('.name .icon'));
65
+ const showTimes = (_c = filmNode.querySelectorAll('.td-time')) === null || _c === void 0 ? void 0 : _c.map((x) => x.textContent.trim());
66
+ const meta = (_d = filmNode.querySelectorAll('.td-title span')) === null || _d === void 0 ? void 0 : _d.map((x) => x.text.trim());
67
+ return {
68
+ id,
69
+ title,
70
+ url,
71
+ colorRating,
72
+ showTimes,
73
+ meta: (0, exports.parseMeta)(meta)
74
+ };
75
+ });
76
+ return films;
77
+ };
78
+ exports.getFilms = getFilms;
79
+ const parseMeta = (meta) => {
80
+ const metaConvert = [];
81
+ for (const element of meta) {
82
+ if (element === 'T') {
83
+ metaConvert.push('subtitles');
84
+ }
85
+ else if (element === 'D') {
86
+ metaConvert.push('dubbing');
87
+ }
88
+ else {
89
+ metaConvert.push(element);
90
+ }
91
+ }
92
+ return metaConvert;
93
+ };
94
+ exports.parseMeta = parseMeta;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.parseISO8601Duration = exports.addProtocol = exports.parseColor = exports.getColor = exports.parseIdFromUrl = void 0;
3
+ exports.parseISO8601Duration = exports.getDuration = exports.addProtocol = exports.parseColor = exports.getColor = exports.parseIdFromUrl = void 0;
4
4
  const parseIdFromUrl = (url) => {
5
5
  if (url) {
6
6
  const idSlug = url === null || url === void 0 ? void 0 : url.split('/')[2];
@@ -46,10 +46,8 @@ const addProtocol = (url) => {
46
46
  return url.startsWith('//') ? 'https:' + url : url;
47
47
  };
48
48
  exports.addProtocol = addProtocol;
49
- const parseISO8601Duration = (iso) => {
50
- const iso8601DurationRegex = /(-)?P(?:([.,\d]+)Y)?(?:([.,\d]+)M)?(?:([.,\d]+)W)?(?:([.,\d]+)D)?T(?:([.,\d]+)H)?(?:([.,\d]+)M)?(?:([.,\d]+)S)?/;
51
- const matches = iso.match(iso8601DurationRegex);
52
- const duration = {
49
+ const getDuration = (matches) => {
50
+ return {
53
51
  sign: matches[1] === undefined ? '+' : '-',
54
52
  years: matches[2] === undefined ? 0 : matches[2],
55
53
  months: matches[3] === undefined ? 0 : matches[3],
@@ -59,6 +57,12 @@ const parseISO8601Duration = (iso) => {
59
57
  minutes: matches[7] === undefined ? 0 : matches[7],
60
58
  seconds: matches[8] === undefined ? 0 : matches[8]
61
59
  };
60
+ };
61
+ exports.getDuration = getDuration;
62
+ const parseISO8601Duration = (iso) => {
63
+ const iso8601DurationRegex = /(-)?P(?:([.,\d]+)Y)?(?:([.,\d]+)M)?(?:([.,\d]+)W)?(?:([.,\d]+)D)?T(?:([.,\d]+)H)?(?:([.,\d]+)M)?(?:([.,\d]+)S)?/;
64
+ const matches = iso.match(iso8601DurationRegex);
65
+ const duration = (0, exports.getDuration)(matches);
62
66
  return +duration.minutes;
63
67
  };
64
68
  exports.parseISO8601Duration = parseISO8601Duration;
@@ -28,9 +28,10 @@ const getColorRating = (bodyClasses) => {
28
28
  exports.getColorRating = getColorRating;
29
29
  const getRating = (el) => {
30
30
  const ratingRaw = el.querySelector('.film-rating-average').textContent;
31
- const rating = +(ratingRaw === null || ratingRaw === void 0 ? void 0 : ratingRaw.replace(/%/g, '').trim());
32
- if (Number.isInteger(rating)) {
33
- return rating;
31
+ const rating = ratingRaw === null || ratingRaw === void 0 ? void 0 : ratingRaw.replace(/%/g, '').trim();
32
+ const ratingInt = parseInt(rating);
33
+ if (Number.isInteger(ratingInt)) {
34
+ return ratingInt;
34
35
  }
35
36
  else {
36
37
  return null;
@@ -90,7 +91,10 @@ const getDuration = (jsonLdRaw, el) => {
90
91
  exports.getDuration = getDuration;
91
92
  const getTitlesOther = (el) => {
92
93
  const namesNode = el.querySelectorAll('.film-names li');
93
- return namesNode.map((el) => {
94
+ if (!namesNode.length) {
95
+ return [];
96
+ }
97
+ const titlesOther = namesNode.map((el) => {
94
98
  const country = el.querySelector('img.flag').attributes.alt;
95
99
  const title = el.textContent.trim().split('\n')[0];
96
100
  if (country && title) {
@@ -103,6 +107,7 @@ const getTitlesOther = (el) => {
103
107
  return null;
104
108
  }
105
109
  });
110
+ return titlesOther.filter((x) => x);
106
111
  };
107
112
  exports.getTitlesOther = getTitlesOther;
108
113
  const getPoster = (el) => {
package/cjs/index.js CHANGED
@@ -10,16 +10,18 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.csfd = exports.Csfd = void 0;
13
+ const cinema_service_1 = require("./services/cinema.service");
13
14
  const creator_service_1 = require("./services/creator.service");
14
15
  const movie_service_1 = require("./services/movie.service");
15
16
  const search_service_1 = require("./services/search.service");
16
17
  const user_ratings_service_1 = require("./services/user-ratings.service");
17
18
  class Csfd {
18
- constructor(userRatingsService, movieService, creatorService, searchService) {
19
+ constructor(userRatingsService, movieService, creatorService, searchService, cinemaService) {
19
20
  this.userRatingsService = userRatingsService;
20
21
  this.movieService = movieService;
21
22
  this.creatorService = creatorService;
22
23
  this.searchService = searchService;
24
+ this.cinemaService = cinemaService;
23
25
  }
24
26
  userRatings(user, config) {
25
27
  return __awaiter(this, void 0, void 0, function* () {
@@ -41,10 +43,16 @@ class Csfd {
41
43
  return this.searchService.search(text);
42
44
  });
43
45
  }
46
+ cinema(district, period) {
47
+ return __awaiter(this, void 0, void 0, function* () {
48
+ return this.cinemaService.cinemas(+district, period);
49
+ });
50
+ }
44
51
  }
45
52
  exports.Csfd = Csfd;
46
53
  const movieScraper = new movie_service_1.MovieScraper();
47
54
  const userRatingsScraper = new user_ratings_service_1.UserRatingsScraper();
55
+ const cinemaScraper = new cinema_service_1.CinemaScraper();
48
56
  const creatorScraper = new creator_service_1.CreatorScraper();
49
57
  const searchScraper = new search_service_1.SearchScraper();
50
- exports.csfd = new Csfd(userRatingsScraper, movieScraper, creatorScraper, searchScraper);
58
+ exports.csfd = new Csfd(userRatingsScraper, movieScraper, creatorScraper, searchScraper, cinemaScraper);
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.CinemaScraper = void 0;
13
+ const node_html_parser_1 = require("node-html-parser");
14
+ const fetchers_1 = require("../fetchers");
15
+ const vars_1 = require("../vars");
16
+ const cinema_helper_1 = require("./../helpers/cinema.helper");
17
+ class CinemaScraper {
18
+ cinemas(district = 1, period = 'today') {
19
+ return __awaiter(this, void 0, void 0, function* () {
20
+ const url = (0, vars_1.cinemasUrl)(district, period);
21
+ const response = yield (0, fetchers_1.fetchPage)(url);
22
+ const cinemasHtml = (0, node_html_parser_1.parse)(response);
23
+ const contentNode = cinemasHtml.querySelectorAll('#snippet--cinemas section.box');
24
+ this.buildCinemas(contentNode);
25
+ return this.cinema;
26
+ });
27
+ }
28
+ buildCinemas(contentNode) {
29
+ const cinemas = [];
30
+ contentNode.map((x) => {
31
+ var _a, _b;
32
+ const cinema = {
33
+ id: (0, cinema_helper_1.getCinemaId)(x),
34
+ name: (_a = (0, cinema_helper_1.parseCinema)(x)) === null || _a === void 0 ? void 0 : _a.name,
35
+ city: (_b = (0, cinema_helper_1.parseCinema)(x)) === null || _b === void 0 ? void 0 : _b.city,
36
+ url: (0, cinema_helper_1.getCinemaUrl)(x),
37
+ coords: (0, cinema_helper_1.getCoords)(x),
38
+ screenings: (0, cinema_helper_1.getGroupedFilmsByDate)(x)
39
+ };
40
+ cinemas.push(cinema);
41
+ });
42
+ this.cinema = cinemas;
43
+ }
44
+ }
45
+ exports.CinemaScraper = CinemaScraper;
@@ -24,12 +24,14 @@ class SearchScraper {
24
24
  const html = (0, node_html_parser_1.parse)(response);
25
25
  const moviesNode = html.querySelectorAll('.main-movies article');
26
26
  const usersNode = html.querySelectorAll('.main-users article');
27
- return this.parseSearch(moviesNode, usersNode);
27
+ const tvSeriesNode = html.querySelectorAll('.main-series article');
28
+ return this.parseSearch(moviesNode, usersNode, tvSeriesNode);
28
29
  });
29
30
  }
30
- parseSearch(moviesNode, usersNode) {
31
+ parseSearch(moviesNode, usersNode, tvSeriesNode) {
31
32
  const movies = [];
32
33
  const users = [];
34
+ const tvSeries = [];
33
35
  moviesNode.map((m) => {
34
36
  const url = (0, search_helper_1.getUrl)(m);
35
37
  const movie = {
@@ -59,10 +61,28 @@ class SearchScraper {
59
61
  };
60
62
  users.push(user);
61
63
  });
64
+ tvSeriesNode.map((m) => {
65
+ const url = (0, search_helper_1.getUrl)(m);
66
+ const user = {
67
+ id: (0, global_helper_1.parseIdFromUrl)(url),
68
+ title: (0, search_helper_1.getTitle)(m),
69
+ year: (0, search_helper_1.getYear)(m),
70
+ url: `https://www.csfd.cz${url}`,
71
+ type: (0, search_helper_1.getType)(m),
72
+ colorRating: (0, search_helper_1.getColorRating)(m),
73
+ poster: (0, search_helper_1.getPoster)(m),
74
+ origins: (0, search_helper_1.getOrigins)(m),
75
+ creators: {
76
+ directors: (0, search_helper_1.parsePeople)(m, 'directors'),
77
+ actors: (0, search_helper_1.parsePeople)(m, 'actors')
78
+ }
79
+ };
80
+ tvSeries.push(user);
81
+ });
62
82
  const search = {
63
83
  movies: movies,
64
84
  users: users,
65
- tvSeries: [],
85
+ tvSeries: tvSeries,
66
86
  creators: []
67
87
  };
68
88
  return search;
package/cjs/vars.js CHANGED
@@ -1,11 +1,15 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.searchUrl = exports.creatorUrl = exports.movieUrl = exports.userRatingsUrl = void 0;
3
+ exports.searchUrl = exports.cinemasUrl = exports.creatorUrl = exports.movieUrl = exports.userRatingsUrl = void 0;
4
4
  const userRatingsUrl = (user, page) => `https://www.csfd.cz/uzivatel/${encodeURIComponent(user)}/hodnoceni/${page ? '?page=' + page : ''}`;
5
5
  exports.userRatingsUrl = userRatingsUrl;
6
6
  const movieUrl = (movie) => `https://www.csfd.cz/film/${encodeURIComponent(movie)}/prehled/`;
7
7
  exports.movieUrl = movieUrl;
8
8
  const creatorUrl = (creator) => `https://www.csfd.cz/tvurce/${encodeURIComponent(creator)}`;
9
9
  exports.creatorUrl = creatorUrl;
10
+ const cinemasUrl = (district, period) => {
11
+ return `https://www.csfd.cz/kino/?period=${period}&district=${district}`;
12
+ };
13
+ exports.cinemasUrl = cinemasUrl;
10
14
  const searchUrl = (text) => `https://www.csfd.cz/hledat/?q=${encodeURIComponent(text)}`;
11
15
  exports.searchUrl = searchUrl;
@@ -0,0 +1,6 @@
1
+ // Check if `fetch` is available in global scope (nodejs 18+) or in window (browser). If not, use cross-fetch polyfill.
2
+ import { fetch as crossFetch } from 'cross-fetch';
3
+ export const fetchSafe = (typeof fetch === 'function' && fetch) || // ServiceWorker fetch (Cloud Functions + Chrome extension)
4
+ (typeof global === 'object' && global.fetch) || // Node.js 18+ fetch
5
+ (typeof window !== 'undefined' && window.fetch) || // Browser fetch
6
+ crossFetch; // Polyfill fetch
@@ -1,4 +1,3 @@
1
- // import fetch from 'cross-fetch';
2
1
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
2
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
3
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -8,6 +7,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
7
  step((generator = generator.apply(thisArg, _arguments || [])).next());
9
8
  });
10
9
  };
10
+ import { fetchSafe } from './fetch.polyfill';
11
11
  const USER_AGENTS = [
12
12
  'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36',
13
13
  'Mozilla/5.0 (iPhone; CPU iPhone OS 14_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/87.0.4280.77 Mobile/15E148 Safari/604.1',
@@ -19,7 +19,7 @@ const headers = {
19
19
  };
20
20
  export const fetchPage = (url) => __awaiter(void 0, void 0, void 0, function* () {
21
21
  try {
22
- const response = yield fetch(url, { headers });
22
+ const response = yield fetchSafe(url, { headers });
23
23
  if (response.status >= 400 && response.status < 600) {
24
24
  throw new Error(`node-csfd-api: Bad response ${response.status} for url: ${url}`);
25
25
  }
@@ -0,0 +1,81 @@
1
+ import { parseColor, parseIdFromUrl } from './global.helper';
2
+ export const getColorRating = (el) => {
3
+ return parseColor(el === null || el === void 0 ? void 0 : el.classNames.split(' ').pop());
4
+ };
5
+ export const getCinemaId = (el) => {
6
+ var _a;
7
+ const id = (_a = el === null || el === void 0 ? void 0 : el.id) === null || _a === void 0 ? void 0 : _a.split('-')[1];
8
+ return +id;
9
+ };
10
+ export const getId = (url) => {
11
+ if (url) {
12
+ return parseIdFromUrl(url);
13
+ }
14
+ return null;
15
+ };
16
+ export const getName = (el) => {
17
+ return el.querySelector('h1').innerText.trim();
18
+ };
19
+ export const getCoords = (el) => {
20
+ const link = el === null || el === void 0 ? void 0 : el.querySelector('.box-header img[alt="Google Maps"]').closest('a').getAttribute('href');
21
+ const coords = link.split('q=')[1].split(',');
22
+ const [lat, lng] = coords;
23
+ return { lat: +lat, lng: +lng };
24
+ };
25
+ export const getCinemaUrl = (el) => {
26
+ var _a;
27
+ return (_a = el.querySelector('.box-header .cinema-logo a')) === null || _a === void 0 ? void 0 : _a.attributes.href;
28
+ };
29
+ export const parseCinema = (el) => {
30
+ const title = el.querySelector('.box-header h2').innerText.trim();
31
+ const [city, name] = title.split(' - ');
32
+ return { city, name };
33
+ };
34
+ export const getGroupedFilmsByDate = (el) => {
35
+ const divs = el.querySelectorAll(':scope > div');
36
+ const getDatesAndFilms = divs
37
+ .map((_, index) => index)
38
+ .filter((index) => index % 2 === 0)
39
+ .map((index) => {
40
+ const [date, films] = divs.slice(index, index + 2);
41
+ const dateText = date === null || date === void 0 ? void 0 : date.innerText.trim();
42
+ return { date: dateText, films: getFilms('', films) };
43
+ });
44
+ return getDatesAndFilms;
45
+ };
46
+ export const getFilms = (date, el) => {
47
+ const filmNodes = el.querySelectorAll('.cinema-table tr');
48
+ const films = filmNodes.map((filmNode) => {
49
+ var _a, _b, _c, _d;
50
+ const url = (_a = filmNode.querySelector('td.name h3 a')) === null || _a === void 0 ? void 0 : _a.attributes.href;
51
+ const id = getId(url);
52
+ const title = (_b = filmNode.querySelector('.name h3')) === null || _b === void 0 ? void 0 : _b.text.trim();
53
+ const colorRating = getColorRating(filmNode.querySelector('.name .icon'));
54
+ const showTimes = (_c = filmNode.querySelectorAll('.td-time')) === null || _c === void 0 ? void 0 : _c.map((x) => x.textContent.trim());
55
+ const meta = (_d = filmNode.querySelectorAll('.td-title span')) === null || _d === void 0 ? void 0 : _d.map((x) => x.text.trim());
56
+ return {
57
+ id,
58
+ title,
59
+ url,
60
+ colorRating,
61
+ showTimes,
62
+ meta: parseMeta(meta)
63
+ };
64
+ });
65
+ return films;
66
+ };
67
+ export const parseMeta = (meta) => {
68
+ const metaConvert = [];
69
+ for (const element of meta) {
70
+ if (element === 'T') {
71
+ metaConvert.push('subtitles');
72
+ }
73
+ else if (element === 'D') {
74
+ metaConvert.push('dubbing');
75
+ }
76
+ else {
77
+ metaConvert.push(element);
78
+ }
79
+ }
80
+ return metaConvert;
81
+ };
@@ -39,10 +39,8 @@ export const parseColor = (quality) => {
39
39
  export const addProtocol = (url) => {
40
40
  return url.startsWith('//') ? 'https:' + url : url;
41
41
  };
42
- export const parseISO8601Duration = (iso) => {
43
- const iso8601DurationRegex = /(-)?P(?:([.,\d]+)Y)?(?:([.,\d]+)M)?(?:([.,\d]+)W)?(?:([.,\d]+)D)?T(?:([.,\d]+)H)?(?:([.,\d]+)M)?(?:([.,\d]+)S)?/;
44
- const matches = iso.match(iso8601DurationRegex);
45
- const duration = {
42
+ export const getDuration = (matches) => {
43
+ return {
46
44
  sign: matches[1] === undefined ? '+' : '-',
47
45
  years: matches[2] === undefined ? 0 : matches[2],
48
46
  months: matches[3] === undefined ? 0 : matches[3],
@@ -52,5 +50,10 @@ export const parseISO8601Duration = (iso) => {
52
50
  minutes: matches[7] === undefined ? 0 : matches[7],
53
51
  seconds: matches[8] === undefined ? 0 : matches[8]
54
52
  };
53
+ };
54
+ export const parseISO8601Duration = (iso) => {
55
+ const iso8601DurationRegex = /(-)?P(?:([.,\d]+)Y)?(?:([.,\d]+)M)?(?:([.,\d]+)W)?(?:([.,\d]+)D)?T(?:([.,\d]+)H)?(?:([.,\d]+)M)?(?:([.,\d]+)S)?/;
56
+ const matches = iso.match(iso8601DurationRegex);
57
+ const duration = getDuration(matches);
55
58
  return +duration.minutes;
56
59
  };
@@ -1,4 +1,4 @@
1
- import { addProtocol, getColor, parseIdFromUrl, parseISO8601Duration } from './global.helper';
1
+ import { addProtocol, getColor, parseISO8601Duration, parseIdFromUrl } from './global.helper';
2
2
  export const getId = (el) => {
3
3
  const url = el.querySelector('.tabs .tab-nav-list a').attributes.href;
4
4
  return parseIdFromUrl(url);
@@ -20,9 +20,10 @@ export const getColorRating = (bodyClasses) => {
20
20
  };
21
21
  export const getRating = (el) => {
22
22
  const ratingRaw = el.querySelector('.film-rating-average').textContent;
23
- const rating = +(ratingRaw === null || ratingRaw === void 0 ? void 0 : ratingRaw.replace(/%/g, '').trim());
24
- if (Number.isInteger(rating)) {
25
- return rating;
23
+ const rating = ratingRaw === null || ratingRaw === void 0 ? void 0 : ratingRaw.replace(/%/g, '').trim();
24
+ const ratingInt = parseInt(rating);
25
+ if (Number.isInteger(ratingInt)) {
26
+ return ratingInt;
26
27
  }
27
28
  else {
28
29
  return null;
@@ -78,7 +79,10 @@ export const getDuration = (jsonLdRaw, el) => {
78
79
  };
79
80
  export const getTitlesOther = (el) => {
80
81
  const namesNode = el.querySelectorAll('.film-names li');
81
- return namesNode.map((el) => {
82
+ if (!namesNode.length) {
83
+ return [];
84
+ }
85
+ const titlesOther = namesNode.map((el) => {
82
86
  const country = el.querySelector('img.flag').attributes.alt;
83
87
  const title = el.textContent.trim().split('\n')[0];
84
88
  if (country && title) {
@@ -91,6 +95,7 @@ export const getTitlesOther = (el) => {
91
95
  return null;
92
96
  }
93
97
  });
98
+ return titlesOther.filter((x) => x);
94
99
  };
95
100
  export const getPoster = (el) => {
96
101
  var _a;
package/esm/index.js CHANGED
@@ -7,16 +7,18 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
7
7
  step((generator = generator.apply(thisArg, _arguments || [])).next());
8
8
  });
9
9
  };
10
+ import { CinemaScraper } from './services/cinema.service';
10
11
  import { CreatorScraper } from './services/creator.service';
11
12
  import { MovieScraper } from './services/movie.service';
12
13
  import { SearchScraper } from './services/search.service';
13
14
  import { UserRatingsScraper } from './services/user-ratings.service';
14
15
  export class Csfd {
15
- constructor(userRatingsService, movieService, creatorService, searchService) {
16
+ constructor(userRatingsService, movieService, creatorService, searchService, cinemaService) {
16
17
  this.userRatingsService = userRatingsService;
17
18
  this.movieService = movieService;
18
19
  this.creatorService = creatorService;
19
20
  this.searchService = searchService;
21
+ this.cinemaService = cinemaService;
20
22
  }
21
23
  userRatings(user, config) {
22
24
  return __awaiter(this, void 0, void 0, function* () {
@@ -38,9 +40,15 @@ export class Csfd {
38
40
  return this.searchService.search(text);
39
41
  });
40
42
  }
43
+ cinema(district, period) {
44
+ return __awaiter(this, void 0, void 0, function* () {
45
+ return this.cinemaService.cinemas(+district, period);
46
+ });
47
+ }
41
48
  }
42
49
  const movieScraper = new MovieScraper();
43
50
  const userRatingsScraper = new UserRatingsScraper();
51
+ const cinemaScraper = new CinemaScraper();
44
52
  const creatorScraper = new CreatorScraper();
45
53
  const searchScraper = new SearchScraper();
46
- export const csfd = new Csfd(userRatingsScraper, movieScraper, creatorScraper, searchScraper);
54
+ export const csfd = new Csfd(userRatingsScraper, movieScraper, creatorScraper, searchScraper, cinemaScraper);
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,41 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { parse } from 'node-html-parser';
11
+ import { fetchPage } from '../fetchers';
12
+ import { cinemasUrl } from '../vars';
13
+ import { getCinemaId, getCinemaUrl, getCoords, getGroupedFilmsByDate, parseCinema } from './../helpers/cinema.helper';
14
+ export class CinemaScraper {
15
+ cinemas(district = 1, period = 'today') {
16
+ return __awaiter(this, void 0, void 0, function* () {
17
+ const url = cinemasUrl(district, period);
18
+ const response = yield fetchPage(url);
19
+ const cinemasHtml = parse(response);
20
+ const contentNode = cinemasHtml.querySelectorAll('#snippet--cinemas section.box');
21
+ this.buildCinemas(contentNode);
22
+ return this.cinema;
23
+ });
24
+ }
25
+ buildCinemas(contentNode) {
26
+ const cinemas = [];
27
+ contentNode.map((x) => {
28
+ var _a, _b;
29
+ const cinema = {
30
+ id: getCinemaId(x),
31
+ name: (_a = parseCinema(x)) === null || _a === void 0 ? void 0 : _a.name,
32
+ city: (_b = parseCinema(x)) === null || _b === void 0 ? void 0 : _b.city,
33
+ url: getCinemaUrl(x),
34
+ coords: getCoords(x),
35
+ screenings: getGroupedFilmsByDate(x)
36
+ };
37
+ cinemas.push(cinema);
38
+ });
39
+ this.cinema = cinemas;
40
+ }
41
+ }
@@ -21,12 +21,14 @@ export class SearchScraper {
21
21
  const html = parse(response);
22
22
  const moviesNode = html.querySelectorAll('.main-movies article');
23
23
  const usersNode = html.querySelectorAll('.main-users article');
24
- return this.parseSearch(moviesNode, usersNode);
24
+ const tvSeriesNode = html.querySelectorAll('.main-series article');
25
+ return this.parseSearch(moviesNode, usersNode, tvSeriesNode);
25
26
  });
26
27
  }
27
- parseSearch(moviesNode, usersNode) {
28
+ parseSearch(moviesNode, usersNode, tvSeriesNode) {
28
29
  const movies = [];
29
30
  const users = [];
31
+ const tvSeries = [];
30
32
  moviesNode.map((m) => {
31
33
  const url = getUrl(m);
32
34
  const movie = {
@@ -56,10 +58,28 @@ export class SearchScraper {
56
58
  };
57
59
  users.push(user);
58
60
  });
61
+ tvSeriesNode.map((m) => {
62
+ const url = getUrl(m);
63
+ const user = {
64
+ id: parseIdFromUrl(url),
65
+ title: getTitle(m),
66
+ year: getYear(m),
67
+ url: `https://www.csfd.cz${url}`,
68
+ type: getType(m),
69
+ colorRating: getColorRating(m),
70
+ poster: getPoster(m),
71
+ origins: getOrigins(m),
72
+ creators: {
73
+ directors: parsePeople(m, 'directors'),
74
+ actors: parsePeople(m, 'actors')
75
+ }
76
+ };
77
+ tvSeries.push(user);
78
+ });
59
79
  const search = {
60
80
  movies: movies,
61
81
  users: users,
62
- tvSeries: [],
82
+ tvSeries: tvSeries,
63
83
  creators: []
64
84
  };
65
85
  return search;
package/esm/vars.js CHANGED
@@ -1,4 +1,7 @@
1
1
  export const userRatingsUrl = (user, page) => `https://www.csfd.cz/uzivatel/${encodeURIComponent(user)}/hodnoceni/${page ? '?page=' + page : ''}`;
2
2
  export const movieUrl = (movie) => `https://www.csfd.cz/film/${encodeURIComponent(movie)}/prehled/`;
3
3
  export const creatorUrl = (creator) => `https://www.csfd.cz/tvurce/${encodeURIComponent(creator)}`;
4
+ export const cinemasUrl = (district, period) => {
5
+ return `https://www.csfd.cz/kino/?period=${period}&district=${district}`;
6
+ };
4
7
  export const searchUrl = (text) => `https://www.csfd.cz/hledat/?q=${encodeURIComponent(text)}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-csfd-api",
3
- "version": "3.0.0-next.2",
3
+ "version": "3.0.0-next.20",
4
4
  "description": "ČSFD API in JavaScript. Amazing NPM library for scrapping csfd.cz :)",
5
5
  "main": "./cjs/index.js",
6
6
  "author": "BART! <bart@bartweb.cz>",
@@ -11,11 +11,12 @@
11
11
  "barrels": "barrelsby --delete -c barrels.json",
12
12
  "postbuild": "npm-prepare-dist -s postinstall -s prepare && yarn fix-paths",
13
13
  "tsc": "tsc",
14
- "demo": "ts-node demo",
14
+ "demo": "tsx demo",
15
15
  "lint": "eslint ./src/**/**/* --fix",
16
- "test": "jest",
16
+ "test": "vitest",
17
+ "test:coverage": "yarn test run --coverage",
17
18
  "fix-paths": "yarn json -I -f ./dist/package.json -e \"this.module='./esm/index.js';this.main='./cjs/index.js';this.types='./types/index.d.ts'\"",
18
- "publish:next": "yarn && yarn build && yarn test --coverage true && npm publish --folder dist --tag beta",
19
+ "publish:next": "yarn && yarn build && yarn test:coverage && cd dist && npm publish --tag next",
19
20
  "postversion": "git push && git push --follow-tags",
20
21
  "release:beta": "npm version preminor --preid=beta -m \"chore(update): prelease %s β\"",
21
22
  "prerelease:beta": "npm version prerelease --preid=beta -m \"chore(update): prelease %s β\"",
@@ -23,8 +24,13 @@
23
24
  "release:minor": "git checkout master && npm version minor -m \"chore(update): release %s 🚀\"",
24
25
  "release:major": "git checkout master && npm version major -m \"chore(update): major release %s 💥\""
25
26
  },
27
+ "publishConfig": {
28
+ "access": "public",
29
+ "registry": "https://registry.npmjs.org"
30
+ },
26
31
  "dependencies": {
27
- "node-html-parser": "^6.1.4"
32
+ "cross-fetch": "^4.0.0",
33
+ "node-html-parser": "^6.1.12"
28
34
  },
29
35
  "repository": {
30
36
  "url": "git+https://github.com/bartholomej/node-csfd-api.git",
@@ -48,9 +54,12 @@
48
54
  "api"
49
55
  ],
50
56
  "engines": {
51
- "node": ">= 12"
57
+ "node": ">= 14"
52
58
  },
53
59
  "license": "MIT",
60
+ "lint-staged": {
61
+ "*.ts": "eslint --cache --fix"
62
+ },
54
63
  "module": "./esm/index.js",
55
64
  "types": "./types/index.d.ts"
56
65
  }
@@ -0,0 +1 @@
1
+ export declare const fetchSafe: typeof fetch;
@@ -0,0 +1,19 @@
1
+ import { CSFDCinemaGroupedFilmsByDate, CSFDCinemaMeta, CSFDCinemaMovie } from 'interfaces/cinema.interface';
2
+ import { HTMLElement } from 'node-html-parser';
3
+ import { CSFDColorRating } from '../interfaces/global';
4
+ export declare const getColorRating: (el: HTMLElement) => CSFDColorRating;
5
+ export declare const getCinemaId: (el: HTMLElement | null) => number;
6
+ export declare const getId: (url: string) => number;
7
+ export declare const getName: (el: HTMLElement | null) => string;
8
+ export declare const getCoords: (el: HTMLElement | null) => {
9
+ lat: number;
10
+ lng: number;
11
+ };
12
+ export declare const getCinemaUrl: (el: HTMLElement | null) => string;
13
+ export declare const parseCinema: (el: HTMLElement | null) => {
14
+ city: string;
15
+ name: string;
16
+ };
17
+ export declare const getGroupedFilmsByDate: (el: HTMLElement | null) => CSFDCinemaGroupedFilmsByDate[];
18
+ export declare const getFilms: (date: string, el: HTMLElement | null) => CSFDCinemaMovie[];
19
+ export declare const parseMeta: (meta: string[]) => CSFDCinemaMeta[];
@@ -4,4 +4,14 @@ export declare const parseIdFromUrl: (url: string) => number;
4
4
  export declare const getColor: (cls: string) => CSFDColorRating;
5
5
  export declare const parseColor: (quality: Colors) => CSFDColorRating;
6
6
  export declare const addProtocol: (url: string) => string;
7
+ export declare const getDuration: (matches: any[]) => {
8
+ sign: string;
9
+ years: any;
10
+ months: any;
11
+ weeks: any;
12
+ days: any;
13
+ hours: any;
14
+ minutes: any;
15
+ seconds: any;
16
+ };
7
17
  export declare const parseISO8601Duration: (iso: string) => number;
package/types/index.d.ts CHANGED
@@ -1,7 +1,9 @@
1
+ import { CSFDCinema, CSFDCinemaPeriod } from 'interfaces/cinema.interface';
1
2
  import { CSFDCreator } from './interfaces/creator.interface';
2
3
  import { CSFDMovie } from './interfaces/movie.interface';
3
4
  import { CSFDSearch } from './interfaces/search.interface';
4
5
  import { CSFDUserRatingConfig, CSFDUserRatings } from './interfaces/user-ratings.interface';
6
+ import { CinemaScraper } from './services/cinema.service';
5
7
  import { CreatorScraper } from './services/creator.service';
6
8
  import { MovieScraper } from './services/movie.service';
7
9
  import { SearchScraper } from './services/search.service';
@@ -11,10 +13,12 @@ export declare class Csfd {
11
13
  private movieService;
12
14
  private creatorService;
13
15
  private searchService;
14
- constructor(userRatingsService: UserRatingsScraper, movieService: MovieScraper, creatorService: CreatorScraper, searchService: SearchScraper);
16
+ private cinemaService;
17
+ constructor(userRatingsService: UserRatingsScraper, movieService: MovieScraper, creatorService: CreatorScraper, searchService: SearchScraper, cinemaService: CinemaScraper);
15
18
  userRatings(user: string | number, config?: CSFDUserRatingConfig): Promise<CSFDUserRatings[]>;
16
19
  movie(movie: number): Promise<CSFDMovie>;
17
20
  creator(creator: number): Promise<CSFDCreator>;
18
21
  search(text: string): Promise<CSFDSearch>;
22
+ cinema(district: number, period: CSFDCinemaPeriod): Promise<CSFDCinema[]>;
19
23
  }
20
24
  export declare const csfd: Csfd;
package/types/index.ts CHANGED
@@ -4,18 +4,22 @@
4
4
 
5
5
  export * from "./index";
6
6
  export * from "./vars";
7
+ export * from "./fetchers/fetch.polyfill";
7
8
  export * from "./fetchers/index";
9
+ export * from "./helpers/cinema.helper";
8
10
  export * from "./helpers/creator.helper";
9
11
  export * from "./helpers/global.helper";
10
12
  export * from "./helpers/movie.helper";
11
13
  export * from "./helpers/search-user.helper";
12
14
  export * from "./helpers/search.helper";
13
15
  export * from "./helpers/user-ratings.helper";
16
+ export * from "./interfaces/cinema.interface";
14
17
  export * from "./interfaces/creator.interface";
15
18
  export * from "./interfaces/global";
16
19
  export * from "./interfaces/movie.interface";
17
20
  export * from "./interfaces/search.interface";
18
21
  export * from "./interfaces/user-ratings.interface";
22
+ export * from "./services/cinema.service";
19
23
  export * from "./services/creator.service";
20
24
  export * from "./services/movie.service";
21
25
  export * from "./services/search.service";
@@ -0,0 +1,23 @@
1
+ import { CSFDMovieListItem } from './movie.interface';
2
+ export interface CSFDCinema {
3
+ id: number;
4
+ name: string;
5
+ city: string;
6
+ url: string;
7
+ coords: {
8
+ lat: number;
9
+ lng: number;
10
+ };
11
+ region?: string;
12
+ screenings: CSFDCinemaGroupedFilmsByDate[];
13
+ }
14
+ export interface CSFDCinemaGroupedFilmsByDate {
15
+ date: string;
16
+ films: CSFDCinemaMovie[];
17
+ }
18
+ export interface CSFDCinemaMovie extends CSFDMovieListItem {
19
+ meta: CSFDCinemaMeta[];
20
+ showTimes: string[];
21
+ }
22
+ export type CSFDCinemaMeta = 'dubbing' | '3D' | 'subtitles' | string;
23
+ export type CSFDCinemaPeriod = 'today' | 'weekend' | 'week' | 'tomorrow' | 'month';
@@ -0,0 +1,6 @@
1
+ import { CSFDCinema, CSFDCinemaPeriod } from '../interfaces/cinema.interface';
2
+ export declare class CinemaScraper {
3
+ private cinema;
4
+ cinemas(district?: number, period?: CSFDCinemaPeriod): Promise<CSFDCinema[]>;
5
+ private buildCinemas;
6
+ }
package/types/vars.d.ts CHANGED
@@ -1,4 +1,6 @@
1
+ import { CSFDCinemaPeriod } from 'interfaces/cinema.interface';
1
2
  export declare const userRatingsUrl: (user: string | number, page?: number) => string;
2
3
  export declare const movieUrl: (movie: number) => string;
3
4
  export declare const creatorUrl: (creator: number | string) => string;
5
+ export declare const cinemasUrl: (district: number | string, period: CSFDCinemaPeriod) => string;
4
6
  export declare const searchUrl: (text: string) => string;