node-csfd-api-racintom 1.6.0 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/.editorconfig +13 -0
  2. package/.eslintrc.json +33 -0
  3. package/.gitattributes +2 -0
  4. package/.github/FUNDING.yml +8 -0
  5. package/.github/pull_request_template.md +19 -0
  6. package/.github/workflows/main.yml +40 -0
  7. package/.github/workflows/publish.yml +73 -0
  8. package/.github/workflows/test.yml +43 -0
  9. package/.husky/pre-commit +1 -0
  10. package/.idea/codeStyles/Project.xml +72 -0
  11. package/.idea/codeStyles/codeStyleConfig.xml +5 -0
  12. package/.idea/inspectionProfiles/Project_Default.xml +6 -0
  13. package/.idea/misc.xml +6 -0
  14. package/.idea/modules.xml +8 -0
  15. package/.idea/node-csfd-api.iml +9 -0
  16. package/.idea/prettier.xml +6 -0
  17. package/.idea/vcs.xml +7 -0
  18. package/.nvmrc +1 -0
  19. package/.prettierignore +8 -0
  20. package/.prettierrc +10 -0
  21. package/.vscode/settings.json +16 -0
  22. package/Dockerfile +19 -0
  23. package/demo.ts +35 -0
  24. package/eslint.config.mjs +55 -0
  25. package/package.json +86 -62
  26. package/server.ts +66 -0
  27. package/src/fetchers/fetch.polyfill.ts +7 -0
  28. package/src/fetchers/index.ts +25 -0
  29. package/src/helpers/creator.helper.ts +95 -0
  30. package/src/helpers/global.helper.ts +70 -0
  31. package/src/helpers/movie.helper.ts +276 -0
  32. package/src/helpers/search-user.helper.ts +19 -0
  33. package/src/helpers/search.helper.ts +66 -0
  34. package/src/helpers/user-ratings.helper.ts +62 -0
  35. package/src/index.ts +50 -0
  36. package/src/interfaces/creator.interface.ts +14 -0
  37. package/src/interfaces/global.ts +36 -0
  38. package/src/interfaces/movie.interface.ts +157 -0
  39. package/src/interfaces/search.interface.ts +32 -0
  40. package/src/interfaces/season.interface.ts +12 -0
  41. package/src/interfaces/user-ratings.interface.ts +21 -0
  42. package/src/services/creator.service.ts +34 -0
  43. package/src/services/movie.service.ts +89 -0
  44. package/src/services/search.service.ts +101 -0
  45. package/src/services/season.service.ts +55 -0
  46. package/src/services/user-ratings.service.ts +106 -0
  47. package/src/vars.ts +16 -0
  48. package/tests/creator.test.ts +182 -0
  49. package/tests/fetchers.test.ts +109 -0
  50. package/tests/global.test.ts +35 -0
  51. package/tests/helpers.test.ts +59 -0
  52. package/tests/mocks/creator-actor.html.ts +2244 -0
  53. package/tests/mocks/creator-composer-empty.html.ts +683 -0
  54. package/tests/mocks/creator-director.html.ts +3407 -0
  55. package/tests/mocks/movie1.html.ts +1430 -0
  56. package/tests/mocks/movie2.html.ts +740 -0
  57. package/tests/mocks/movie3.html.ts +1843 -0
  58. package/tests/mocks/movie4.html.ts +1568 -0
  59. package/tests/mocks/search.html.ts +838 -0
  60. package/tests/mocks/series1.html.ts +1540 -0
  61. package/tests/mocks/userRatings.html.ts +1354 -0
  62. package/tests/movie.test.ts +606 -0
  63. package/tests/search.test.ts +379 -0
  64. package/tests/season.test.ts +115 -0
  65. package/tests/services.test.ts +106 -0
  66. package/tests/user-ratings.test.ts +142 -0
  67. package/tests/vars.test.ts +34 -0
  68. package/tsconfig.json +23 -0
  69. package/vitest.config.mts +10 -0
  70. package/fetchers/fetch.polyfill.d.ts +0 -1
  71. package/fetchers/fetch.polyfill.js +0 -9
  72. package/fetchers/index.d.ts +0 -1
  73. package/fetchers/index.js +0 -27
  74. package/helpers/creator.helper.d.ts +0 -17
  75. package/helpers/creator.helper.js +0 -87
  76. package/helpers/global.helper.d.ts +0 -17
  77. package/helpers/global.helper.js +0 -68
  78. package/helpers/movie.helper.d.ts +0 -26
  79. package/helpers/movie.helper.js +0 -270
  80. package/helpers/search-user.helper.d.ts +0 -5
  81. package/helpers/search-user.helper.js +0 -22
  82. package/helpers/search.helper.d.ts +0 -11
  83. package/helpers/search.helper.js +0 -62
  84. package/helpers/user-ratings.helper.d.ts +0 -13
  85. package/helpers/user-ratings.helper.js +0 -61
  86. package/index.d.ts +0 -24
  87. package/index.js +0 -39
  88. package/interfaces/creator.interface.d.ts +0 -12
  89. package/interfaces/creator.interface.js +0 -2
  90. package/interfaces/global.d.ts +0 -22
  91. package/interfaces/global.js +0 -2
  92. package/interfaces/movie.interface.d.ts +0 -73
  93. package/interfaces/movie.interface.js +0 -2
  94. package/interfaces/search.interface.d.ts +0 -27
  95. package/interfaces/search.interface.js +0 -2
  96. package/interfaces/season.interface.d.ts +0 -11
  97. package/interfaces/season.interface.js +0 -2
  98. package/interfaces/user-ratings.interface.d.ts +0 -18
  99. package/interfaces/user-ratings.interface.js +0 -2
  100. package/services/creator.service.d.ts +0 -6
  101. package/services/creator.service.js +0 -32
  102. package/services/movie.service.d.ts +0 -6
  103. package/services/movie.service.js +0 -59
  104. package/services/search.service.d.ts +0 -5
  105. package/services/search.service.js +0 -80
  106. package/services/season.service.d.ts +0 -9
  107. package/services/season.service.js +0 -42
  108. package/services/user-ratings.service.d.ts +0 -7
  109. package/services/user-ratings.service.js +0 -84
  110. package/vars.d.ts +0 -5
  111. package/vars.js +0 -13
@@ -0,0 +1,276 @@
1
+ import { HTMLElement } from 'node-html-parser';
2
+ import { CSFDColorRating } from '../interfaces/global';
3
+ import {
4
+ CSFDBoxContent,
5
+ CSFDCreator,
6
+ CSFDCreatorGroups,
7
+ CSFDGenres,
8
+ CSFDMovieListItem,
9
+ CSFDPremiere, CSFDSeasons,
10
+ CSFDTitlesOther,
11
+ CSFDVod,
12
+ CSFDVodService
13
+ } from '../interfaces/movie.interface';
14
+ import { addProtocol, getColor, parseISO8601Duration, parseIdFromUrl } from './global.helper';
15
+
16
+ export const getId = (el: HTMLElement): number => {
17
+ const url = el.querySelector('.tabs .tab-nav-list a').attributes.href;
18
+ return parseIdFromUrl(url);
19
+ };
20
+
21
+ export const getTitle = (el: HTMLElement): string => {
22
+ return el.querySelector('h1').innerText.split(`(`)[0].trim();
23
+ };
24
+
25
+ export const getGenres = (el: HTMLElement): CSFDGenres[] => {
26
+ const genresRaw = el.querySelector('.genres').textContent;
27
+ return genresRaw.split(' / ') as CSFDGenres[];
28
+ };
29
+
30
+ export const getOrigins = (el: HTMLElement): string[] => {
31
+ const originsRaw = el.querySelector('.origin').textContent;
32
+ const origins = originsRaw.split(',')[0];
33
+ return origins.split(' / ');
34
+ };
35
+
36
+ export const getColorRating = (bodyClasses: string[]): CSFDColorRating => {
37
+ return getColor(bodyClasses[1]);
38
+ };
39
+
40
+ export const getRating = (el: HTMLElement): number => {
41
+ const ratingRaw = el.querySelector('.film-rating-average').textContent;
42
+ const rating = ratingRaw?.replace(/%/g, '').trim();
43
+ const ratingInt = parseInt(rating);
44
+
45
+ if (Number.isInteger(ratingInt)) {
46
+ return ratingInt;
47
+ } else {
48
+ return null;
49
+ }
50
+ };
51
+
52
+ export const getRatingCount = (el: HTMLElement): number => {
53
+ const ratingCountRaw = el.querySelector('.box-rating-container .counter')?.textContent;
54
+ const ratingCount = +ratingCountRaw?.replace(/[(\s)]/g, '');
55
+ if (Number.isInteger(ratingCount)) {
56
+ return ratingCount;
57
+ } else {
58
+ return null;
59
+ }
60
+ };
61
+
62
+ export const getYear = (el: string): number => {
63
+ try {
64
+ const jsonLd = JSON.parse(el);
65
+ return +jsonLd.dateCreated;
66
+ } catch (error) {
67
+ console.error('node-csfd-api: Error parsing JSON-LD', error);
68
+ return null;
69
+ }
70
+ };
71
+
72
+ export const getDuration = (jsonLdRaw: string, el: HTMLElement): number => {
73
+ let duration = null;
74
+ try {
75
+ const jsonLd = JSON.parse(jsonLdRaw);
76
+ duration = jsonLd.duration;
77
+ return parseISO8601Duration(duration);
78
+ } catch (error) {
79
+ const origin = el.querySelector('.origin').innerText;
80
+ const timeString = origin.split(',');
81
+ if (timeString.length > 2) {
82
+ // Get last time elelment
83
+ const timeString2 = timeString.pop().trim();
84
+ // Clean it
85
+ const timeRaw = timeString2.split('(')[0].trim();
86
+ // Split by minutes and hours
87
+ const hoursMinsRaw = timeRaw.split('min')[0];
88
+ const hoursMins = hoursMinsRaw.split('h');
89
+ // Resolve hours + minutes format
90
+ duration = hoursMins.length > 1 ? +hoursMins[0] * 60 + +hoursMins[1] : +hoursMins[0];
91
+ return duration;
92
+ } else {
93
+ return null;
94
+ }
95
+ }
96
+ };
97
+
98
+ export const getTitlesOther = (el: HTMLElement): CSFDTitlesOther[] => {
99
+ const namesNode = el.querySelectorAll('.film-names li');
100
+
101
+ if (!namesNode.length) {
102
+ return [];
103
+ }
104
+
105
+ const titlesOther = namesNode.map((el) => {
106
+ const country = el.querySelector('img.flag').attributes.alt;
107
+ const title = el.textContent.trim().split('\n')[0];
108
+
109
+ if (country && title) {
110
+ return {
111
+ country,
112
+ title
113
+ };
114
+ } else {
115
+ return null;
116
+ }
117
+ });
118
+
119
+ return titlesOther.filter((x) => x);
120
+ };
121
+
122
+ export const getPoster = (el: HTMLElement | null): string => {
123
+ const poster = el.querySelector('.film-posters img');
124
+ // Resolve empty image
125
+ if (poster) {
126
+ if (poster.classNames?.includes('empty-image')) {
127
+ return null;
128
+ } else {
129
+ // Full sized image (not thumb)
130
+ const imageThumb = poster.attributes.src.split('?')[0];
131
+ const image = imageThumb.replace(/\/w140\//, '/w1080/');
132
+ return addProtocol(image);
133
+ }
134
+ } else {
135
+ return null;
136
+ }
137
+ };
138
+
139
+ export const getRandomPhoto = (el: HTMLElement | null): string => {
140
+ const imageNode = el.querySelector('.gallery-item picture img');
141
+ const image = imageNode?.attributes?.src;
142
+ if (image) {
143
+ return image.replace(/\/w663\//, '/w1326/');
144
+ } else {
145
+ return null;
146
+ }
147
+ };
148
+
149
+ export const getTrivia = (el: HTMLElement | null): string[] => {
150
+ const triviaNodes = el.querySelectorAll('.article-trivia ul li');
151
+ if (triviaNodes?.length) {
152
+ return triviaNodes.map((node) => node.textContent.trim().replace(/(\r\n|\n|\r|\t)/gm, ''));
153
+ } else {
154
+ return null;
155
+ }
156
+ };
157
+
158
+ export const getDescriptions = (el: HTMLElement): string[] => {
159
+ return el
160
+ .querySelectorAll('.body--plots .plot-full p, .body--plots .plots .plots-item p')
161
+ .map((movie) => movie.textContent?.trim().replace(/(\r\n|\n|\r|\t)/gm, ''));
162
+ };
163
+
164
+ export const parsePeople = (el: HTMLElement): CSFDCreator[] => {
165
+ const people = el.querySelectorAll('a');
166
+ return (
167
+ people
168
+ // Filter out "more" links
169
+ .filter((x) => x.classNames.length === 0)
170
+ .map((person) => {
171
+ return {
172
+ id: parseIdFromUrl(person.attributes.href),
173
+ name: person.innerText.trim(),
174
+ url: `https://www.csfd.cz${person.attributes.href}`
175
+ };
176
+ })
177
+ );
178
+ };
179
+
180
+ export const getGroup = (el: HTMLElement, group: CSFDCreatorGroups): CSFDCreator[] => {
181
+ const creators = el.querySelectorAll('.creators h4');
182
+ const element = creators.filter((elem) => elem.textContent.trim().includes(group))[0];
183
+ if (element?.parentNode) {
184
+ return parsePeople(element.parentNode as HTMLElement);
185
+ } else {
186
+ return [];
187
+ }
188
+ };
189
+
190
+ export const getType = (el: HTMLElement): string => {
191
+ const type = el.querySelector('.film-header-name .type');
192
+ return type?.innerText?.replace(/[{()}]/g, '') || 'film';
193
+ };
194
+
195
+ export const getVods = (el: HTMLElement | null): CSFDVod[] => {
196
+ let vods: CSFDVod[] = [];
197
+ if (el) {
198
+ const buttons = el.querySelectorAll('.box-buttons .button');
199
+ const buttonsVod = buttons.filter((x) => !x.classNames.includes('button-social'));
200
+ vods = buttonsVod.map((btn) => {
201
+ return {
202
+ title: btn.textContent.trim() as CSFDVodService,
203
+ url: btn.attributes.href
204
+ };
205
+ });
206
+ }
207
+ return vods.length ? vods : [];
208
+ };
209
+
210
+ // Get box content
211
+ export const getBoxContent = (el: HTMLElement, box: string): HTMLElement => {
212
+ const headers = el.querySelectorAll('section.box .box-header');
213
+ return headers.find((header) => header.querySelector('h3').textContent.trim().includes(box))
214
+ ?.parentNode;
215
+ };
216
+
217
+ export const getBoxMovies = (el: HTMLElement, boxName: CSFDBoxContent): CSFDMovieListItem[] => {
218
+ const movieListItem: CSFDMovieListItem[] = [];
219
+ const box = getBoxContent(el, boxName);
220
+ const movieTitleNodes = box?.querySelectorAll('.article-header .film-title-name');
221
+ if (movieTitleNodes?.length) {
222
+ for (const item of movieTitleNodes) {
223
+ movieListItem.push({
224
+ id: parseIdFromUrl(item.attributes.href),
225
+ title: item.textContent.trim(),
226
+ url: `https://www.csfd.cz${item.attributes.href}`
227
+ });
228
+ }
229
+ }
230
+ return movieListItem;
231
+ };
232
+
233
+ export const getPremieres = (el: HTMLElement): CSFDPremiere[] => {
234
+ const premiereNodes = el.querySelectorAll('.box-premieres li');
235
+ const premiere: CSFDPremiere[] = [];
236
+ for (const premiereNode of premiereNodes) {
237
+ const title = premiereNode.querySelector('p + span').attributes.title;
238
+
239
+ if (title) {
240
+ const [date, ...company] = title?.split(' ');
241
+
242
+ premiere.push({
243
+ country: premiereNode.querySelector('.flag')?.attributes.title || null,
244
+ format: premiereNode.querySelector('p').textContent.trim()?.split(' od')[0],
245
+ date,
246
+ company: company.join(' ')
247
+ });
248
+ }
249
+ }
250
+ return premiere;
251
+ };
252
+
253
+ export const getTags = (el: HTMLElement): string[] => {
254
+ const tagsRaw = el.querySelectorAll('.box-content a[href*="/podrobne-vyhledavani/?tag="]');
255
+ return tagsRaw.map((tag) => tag.textContent);
256
+ };
257
+
258
+ export const getSeasonsInfo = (el: HTMLElement): CSFDSeasons => {
259
+ const seasonsList = el.querySelector('.film-episodes-list')
260
+ if (seasonsList === null) {
261
+ return null
262
+ }
263
+
264
+ const seasons = seasonsList.querySelectorAll('.film-title')
265
+
266
+ return seasons.map(season => {
267
+ const nameContainer = season.querySelector('.film-title-name')
268
+ const infoContainer = season.querySelector('.film-title-info')
269
+
270
+ return {
271
+ linkToDetail: nameContainer.getAttribute('href'),
272
+ name: nameContainer.textContent,
273
+ additionalInfo: infoContainer.textContent
274
+ }
275
+ })
276
+ }
@@ -0,0 +1,19 @@
1
+ import { HTMLElement } from 'node-html-parser';
2
+ import { addProtocol } from './global.helper';
3
+
4
+ export const getUser = (el: HTMLElement): string => {
5
+ return el.querySelector('.user-title-name').text;
6
+ };
7
+
8
+ export const getUserRealName = (el: HTMLElement): string => {
9
+ return el.querySelector('.user-real-name')?.text.trim() || null;
10
+ };
11
+
12
+ export const getAvatar = (el: HTMLElement): string => {
13
+ const image = el.querySelector('.article-img img').attributes.src;
14
+ return addProtocol(image);
15
+ };
16
+
17
+ export const getUserUrl = (el: HTMLElement): string => {
18
+ return el.querySelector('.user-title-name').attributes.href;
19
+ };
@@ -0,0 +1,66 @@
1
+ import { HTMLElement } from 'node-html-parser';
2
+ import { CSFDColorRating, CSFDFilmTypes } from '../interfaces/global';
3
+ import { CSFDCreator } from '../interfaces/movie.interface';
4
+ import { Colors } from '../interfaces/user-ratings.interface';
5
+ import { addProtocol, parseColor, parseIdFromUrl } from './global.helper';
6
+
7
+ type Creator = 'Režie:' | 'Hrají:';
8
+
9
+ export const getType = (el: HTMLElement): CSFDFilmTypes => {
10
+ const type = el.querySelectorAll('.film-title-info .info')[1];
11
+ return (type?.innerText.replace(/[{()}]/g, '') || 'film') as CSFDFilmTypes;
12
+ };
13
+
14
+ export const getTitle = (el: HTMLElement): string => {
15
+ return el.querySelector('.film-title-name').text;
16
+ };
17
+
18
+ export const getYear = (el: HTMLElement): number => {
19
+ return +el.querySelectorAll('.film-title-info .info')[0]?.innerText.replace(/[{()}]/g, '');
20
+ };
21
+
22
+ export const getUrl = (el: HTMLElement): string => {
23
+ return el.querySelector('.film-title-name').attributes.href;
24
+ };
25
+
26
+ export const getColorRating = (el: HTMLElement): CSFDColorRating => {
27
+ return parseColor(
28
+ el.querySelector('.article-header i.icon').classNames.split(' ').pop() as Colors
29
+ );
30
+ };
31
+
32
+ export const getPoster = (el: HTMLElement): string => {
33
+ const image = el.querySelector('img').attributes.src;
34
+ return addProtocol(image);
35
+ };
36
+
37
+ export const getOrigins = (el: HTMLElement): string[] => {
38
+ const originsRaw = el.querySelector('.article-content p .info')?.text;
39
+ if (!originsRaw) return [];
40
+ const originsAll = originsRaw?.split(', ')?.[0];
41
+ return originsAll?.split('/').map((country) => country.trim());
42
+ };
43
+
44
+ export const parsePeople = (el: HTMLElement, type: 'directors' | 'actors'): CSFDCreator[] => {
45
+ let who: Creator;
46
+ if (type === 'directors') who = 'Režie:';
47
+ if (type === 'actors') who = 'Hrají:';
48
+
49
+ const peopleNode = Array.from(el && el.querySelectorAll('.article-content p')).find((el) =>
50
+ el.textContent.includes(who)
51
+ );
52
+
53
+ if (peopleNode) {
54
+ const people = Array.from(peopleNode.querySelectorAll('a')) as unknown as HTMLElement[];
55
+
56
+ return people.map((person) => {
57
+ return {
58
+ id: parseIdFromUrl(person.attributes.href),
59
+ name: person.innerText.trim(),
60
+ url: `https://www.csfd.cz${person.attributes.href}`
61
+ };
62
+ });
63
+ } else {
64
+ return [];
65
+ }
66
+ };
@@ -0,0 +1,62 @@
1
+ import { HTMLElement } from 'node-html-parser';
2
+ import { CSFDColorRating, CSFDFilmTypes, CSFDStars } from '../interfaces/global';
3
+ import { Colors } from '../interfaces/user-ratings.interface';
4
+ import { parseIdFromUrl } from './global.helper';
5
+
6
+ export const getId = (el: HTMLElement): number => {
7
+ const url = el.querySelector('td.name .film-title-name').attributes.href;
8
+ return parseIdFromUrl(url);
9
+ };
10
+
11
+ export const getUserRating = (el: HTMLElement): CSFDStars => {
12
+ const ratingText = el.querySelector('td.star-rating-only .stars').classNames.split(' ').pop();
13
+
14
+ const rating = ratingText.includes('stars-') ? +ratingText.split('-').pop() : 0;
15
+ return rating as CSFDStars;
16
+ };
17
+
18
+ export const getType = (el: HTMLElement): CSFDFilmTypes => {
19
+ const typeText = el.querySelectorAll('td.name .film-title-info .info');
20
+
21
+ return (typeText.length > 1 ? typeText[1].text.slice(1, -1) : 'film') as CSFDFilmTypes;
22
+ };
23
+
24
+ export const getTitle = (el: HTMLElement): string => {
25
+ return el.querySelector('td.name .film-title-name').text;
26
+ };
27
+
28
+ export const getYear = (el: HTMLElement): number => {
29
+ return +el.querySelectorAll('td.name .film-title-info .info')[0]?.text.slice(1, -1) || null;
30
+ };
31
+
32
+ export const getColorRating = (el: HTMLElement): CSFDColorRating => {
33
+ const color = parseColor(el.querySelector('td.name .icon').classNames.split(' ').pop() as Colors);
34
+ return color;
35
+ };
36
+
37
+ export const getDate = (el: HTMLElement): string => {
38
+ return el.querySelector('td.date-only').text.trim();
39
+ };
40
+
41
+ export const getUrl = (el: HTMLElement): string => {
42
+ const url = el.querySelector('td.name .film-title-name').attributes.href;
43
+ return `https://www.csfd.cz${url}`;
44
+ };
45
+
46
+ export const parseColor = (quality: Colors): CSFDColorRating => {
47
+ switch (quality) {
48
+ case 'lightgrey':
49
+ return 'unknown';
50
+ case 'red':
51
+ return 'good';
52
+ case 'blue':
53
+ return 'average';
54
+ case 'grey':
55
+ return 'bad';
56
+ default:
57
+ return 'unknown';
58
+ }
59
+ };
60
+
61
+ // Sleep in loop
62
+ export const sleep = (ms: number) => new Promise((res) => setTimeout(res, ms));
package/src/index.ts ADDED
@@ -0,0 +1,50 @@
1
+ import { CSFDCreator } from './interfaces/creator.interface';
2
+ import { CSFDMovie } from './interfaces/movie.interface';
3
+ import { CSFDSearch } from './interfaces/search.interface';
4
+ import { CSFDUserRatingConfig, CSFDUserRatings } from './interfaces/user-ratings.interface';
5
+ import { CreatorScraper } from './services/creator.service';
6
+ import { MovieScraper } from './services/movie.service';
7
+ import { SearchScraper } from './services/search.service';
8
+ import { UserRatingsScraper } from './services/user-ratings.service';
9
+ import { SeasonScraper } from './services/season.service';
10
+ import { CSFDSeason } from './interfaces/season.interface';
11
+
12
+ export class Csfd {
13
+ constructor(
14
+ private userRatingsService: UserRatingsScraper,
15
+ private movieService: MovieScraper,
16
+ private creatorService: CreatorScraper,
17
+ private searchService: SearchScraper,
18
+ private seasonService: SeasonScraper,
19
+ ) {}
20
+
21
+ public async userRatings(
22
+ user: string | number,
23
+ config?: CSFDUserRatingConfig
24
+ ): Promise<CSFDUserRatings[]> {
25
+ return this.userRatingsService.userRatings(user, config);
26
+ }
27
+
28
+ public async movie(movie: number): Promise<CSFDMovie> {
29
+ return this.movieService.movie(+movie);
30
+ }
31
+
32
+ public async creator(creator: number): Promise<CSFDCreator> {
33
+ return this.creatorService.creator(+creator);
34
+ }
35
+
36
+ public async search(text: string): Promise<CSFDSearch> {
37
+ return this.searchService.search(text);
38
+ }
39
+
40
+ public async seasonDetails(url: string): Promise<CSFDSeason> {
41
+ return this.seasonService.season(url);
42
+ }
43
+ }
44
+
45
+ const movieScraper = new MovieScraper();
46
+ const userRatingsScraper = new UserRatingsScraper();
47
+ const creatorScraper = new CreatorScraper();
48
+ const searchScraper = new SearchScraper();
49
+ const seasonScraper = new SeasonScraper();
50
+ export const csfd = new Csfd(userRatingsScraper, movieScraper, creatorScraper, searchScraper, seasonScraper);
@@ -0,0 +1,14 @@
1
+ import { CSFDScreening } from './global';
2
+
3
+ export interface CSFDCreator {
4
+ id: number;
5
+ name: string;
6
+ birthday: string;
7
+ birthplace: string;
8
+ photo: string;
9
+ age: number | string;
10
+ bio: string;
11
+ films: CSFDCreatorScreening[];
12
+ }
13
+
14
+ export type CSFDCreatorScreening = Omit<CSFDScreening, 'url' | 'type'>;
@@ -0,0 +1,36 @@
1
+ export interface CSFDScreening {
2
+ id: number;
3
+ title: string;
4
+ year: number;
5
+ url: string;
6
+ type: CSFDFilmTypes;
7
+ /**
8
+ * Overall aggregated rating. (On the web usually represented by colors).
9
+ *
10
+ * 'unknown': unknown (gray color)
11
+ *
12
+ * 'good': 70% – 100 % (red color)
13
+ *
14
+ * 'average': 30% - 69% (blue color)
15
+ *
16
+ * 'bad': 0% - 29% (black color)
17
+ */
18
+ colorRating: CSFDColorRating;
19
+ }
20
+
21
+ export type CSFDColorRating = 'bad' | 'average' | 'good' | 'unknown';
22
+
23
+ export type CSFDStars = 0 | 1 | 2 | 3 | 4 | 5;
24
+
25
+ export type CSFDFilmTypes =
26
+ | 'film'
27
+ | 'TV film'
28
+ | 'pořad'
29
+ | 'seriál'
30
+ | 'divadelní záznam'
31
+ | 'koncert'
32
+ | 'série'
33
+ | 'studentský film'
34
+ | 'amatérský film'
35
+ | 'hudební videoklip'
36
+ | 'epizoda';
@@ -0,0 +1,157 @@
1
+ import { CSFDScreening } from './global';
2
+
3
+ export interface CSFDMovie extends CSFDScreening {
4
+ rating: number | null;
5
+ poster: string;
6
+ photo: string;
7
+ ratingCount: number | null;
8
+ duration: number | string;
9
+ titlesOther: CSFDTitlesOther[];
10
+ origins: string[];
11
+ descriptions: string[];
12
+ trivia: string[];
13
+ genres: CSFDGenres[] | string[];
14
+ creators: CSFDCreators;
15
+ vod: CSFDVod[];
16
+ tags: string[];
17
+ premieres: CSFDPremiere[];
18
+ related: CSFDMovieListItem[];
19
+ similar: CSFDMovieListItem[];
20
+ seasons: CSFDSeasons | null;
21
+ }
22
+
23
+ export type CSFDVodService =
24
+ | 'Netflix'
25
+ | 'hbogo'
26
+ | 'Prime Video'
27
+ | 'Apple TV+'
28
+ | 'iTunes'
29
+ | 'KVIFF.TV'
30
+ | 'Edisonline'
31
+ | 'o2tv'
32
+ | 'SledovaniTV'
33
+ | 'Starmax'
34
+ | 'DAFilms'
35
+ | 'FILMY ČESKY A ZADARMO'
36
+ | 'Youtube Česká filmová klasika'
37
+ | 'VAPET'
38
+ | 'VOREL FILM'
39
+ | 'ivysilani'
40
+ | 'Google Play'
41
+ | 'Voyo'
42
+ | 'DVD';
43
+
44
+ export interface CSFDVod {
45
+ title: CSFDVodService;
46
+ url: string;
47
+ }
48
+
49
+ export interface CSFDCreators {
50
+ directors: CSFDCreator[];
51
+ writers: CSFDCreator[];
52
+ cinematography: CSFDCreator[];
53
+ music: CSFDCreator[];
54
+ actors: CSFDCreator[];
55
+ basedOn: CSFDCreator[];
56
+ producers: CSFDCreator[];
57
+ filmEditing: CSFDCreator[];
58
+ costumeDesign: CSFDCreator[];
59
+ productionDesign: CSFDCreator[];
60
+ }
61
+
62
+ export interface CSFDTitlesOther {
63
+ country: string;
64
+ title: string;
65
+ }
66
+
67
+ export interface CSFDCreator {
68
+ /**
69
+ * CSFD person ID.
70
+ *
71
+ * You can always assemble url from ID like this:
72
+ *
73
+ * `https://www.csfd.cz/tvurce/${id}`
74
+ */
75
+ id: number;
76
+ name: string;
77
+ url: string;
78
+ }
79
+
80
+ export interface CSFDMovieListItem {
81
+ id: number;
82
+ title: string;
83
+ url: string;
84
+ }
85
+
86
+ export type CSFDGenres =
87
+ | 'Akční'
88
+ | 'Animovaný'
89
+ | 'Dobrodružný'
90
+ | 'Dokumentární'
91
+ | 'Drama'
92
+ | 'Experimentální'
93
+ | 'Fantasy'
94
+ | 'Film-Noir'
95
+ | 'Historický'
96
+ | 'Horor'
97
+ | 'Hudební'
98
+ | 'IMAX'
99
+ | 'Katastrofický'
100
+ | 'Komedie'
101
+ | 'Krátkometrážní'
102
+ | 'Krimi'
103
+ | 'Loutkový'
104
+ | 'Muzikál'
105
+ | 'Mysteriózní'
106
+ | 'Naučný'
107
+ | 'Podobenství'
108
+ | 'Poetický'
109
+ | 'Pohádka'
110
+ | 'Povídkový'
111
+ | 'Psychologický'
112
+ | 'Publicistický'
113
+ | 'Reality-TV'
114
+ | 'Road movie'
115
+ | 'Rodinný'
116
+ | 'Romantický'
117
+ | 'Sci-Fi'
118
+ | 'Soutěžní'
119
+ | 'Sportovní'
120
+ | 'Stand-up'
121
+ | 'Talk-show'
122
+ | 'Taneční'
123
+ | 'Telenovela'
124
+ | 'Thriller'
125
+ | 'Válečný'
126
+ | 'Western'
127
+ | 'Zábavný'
128
+ | 'Životopisný';
129
+
130
+ export type CSFDCreatorGroups =
131
+ | 'Režie'
132
+ | 'Scénář'
133
+ | 'Kamera'
134
+ | 'Hudba'
135
+ | 'Hrají'
136
+ | 'Produkce'
137
+ | 'Střih'
138
+ | 'Předloha'
139
+ | 'Scénografie'
140
+ | 'Kostýmy';
141
+
142
+ export interface CSFDPremiere {
143
+ country: string;
144
+ format: string;
145
+ date: string;
146
+ company: string;
147
+ }
148
+
149
+ export type CSFDBoxContent = 'Související' | 'Podobné';
150
+
151
+ export type CSFDSeasons = CSFDSeasonInfo[]
152
+
153
+ export interface CSFDSeasonInfo {
154
+ name: string
155
+ linkToDetail: string
156
+ additionalInfo: string
157
+ }