node-csfd-api 3.1.1 → 3.3.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 (58) hide show
  1. package/README.md +116 -3
  2. package/dto/user-ratings.d.mts +4 -0
  3. package/dto/user-ratings.d.ts +4 -0
  4. package/dto/user-reviews.d.mts +28 -0
  5. package/dto/user-reviews.d.ts +28 -0
  6. package/helpers/global.helper.js +2 -0
  7. package/helpers/global.helper.js.map +1 -1
  8. package/helpers/global.helper.mjs +2 -1
  9. package/helpers/global.helper.mjs.map +1 -1
  10. package/helpers/user-ratings.helper.js +1 -12
  11. package/helpers/user-ratings.helper.js.map +1 -1
  12. package/helpers/user-ratings.helper.mjs +2 -12
  13. package/helpers/user-ratings.helper.mjs.map +1 -1
  14. package/helpers/user-reviews.helper.js +57 -0
  15. package/helpers/user-reviews.helper.js.map +1 -0
  16. package/helpers/user-reviews.helper.mjs +48 -0
  17. package/helpers/user-reviews.helper.mjs.map +1 -0
  18. package/index.d.mts +5 -1
  19. package/index.d.ts +5 -1
  20. package/index.js +9 -2
  21. package/index.js.map +1 -1
  22. package/index.mjs +9 -2
  23. package/index.mjs.map +1 -1
  24. package/package.json +1 -1
  25. package/services/cinema.service.d.mts +0 -1
  26. package/services/cinema.service.d.ts +0 -1
  27. package/services/cinema.service.js +2 -3
  28. package/services/cinema.service.js.map +1 -1
  29. package/services/cinema.service.mjs +2 -3
  30. package/services/cinema.service.mjs.map +1 -1
  31. package/services/creator.service.d.mts +0 -1
  32. package/services/creator.service.d.ts +0 -1
  33. package/services/creator.service.js +2 -3
  34. package/services/creator.service.js.map +1 -1
  35. package/services/creator.service.mjs +2 -3
  36. package/services/creator.service.mjs.map +1 -1
  37. package/services/movie.service.d.mts +0 -1
  38. package/services/movie.service.d.ts +0 -1
  39. package/services/movie.service.js +2 -3
  40. package/services/movie.service.js.map +1 -1
  41. package/services/movie.service.mjs +2 -3
  42. package/services/movie.service.mjs.map +1 -1
  43. package/services/user-ratings.service.d.mts +0 -1
  44. package/services/user-ratings.service.d.ts +0 -1
  45. package/services/user-ratings.service.js +12 -12
  46. package/services/user-ratings.service.js.map +1 -1
  47. package/services/user-ratings.service.mjs +12 -12
  48. package/services/user-ratings.service.mjs.map +1 -1
  49. package/services/user-reviews.service.d.mts +11 -0
  50. package/services/user-reviews.service.d.ts +11 -0
  51. package/services/user-reviews.service.js +68 -0
  52. package/services/user-reviews.service.js.map +1 -0
  53. package/services/user-reviews.service.mjs +66 -0
  54. package/services/user-reviews.service.mjs.map +1 -0
  55. package/vars.js +2 -0
  56. package/vars.js.map +1 -1
  57. package/vars.mjs +2 -1
  58. package/vars.mjs.map +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"user-ratings.service.mjs","names":["allMovies: CSFDUserRatings[]","movies"],"sources":["../../src/services/user-ratings.service.ts"],"sourcesContent":["import { HTMLElement, parse } from 'node-html-parser';\nimport { CSFDColorRating, CSFDStars } from '../dto/global';\nimport { CSFDUserRatingConfig, CSFDUserRatings } from '../dto/user-ratings';\nimport { fetchPage } from '../fetchers';\nimport {\n getUserRating,\n getUserRatingColorRating,\n getUserRatingDate,\n getUserRatingId,\n getUserRatingTitle,\n getUserRatingType,\n getUserRatingUrl,\n getUserRatingYear,\n sleep\n} from '../helpers/user-ratings.helper';\nimport { userRatingsUrl } from '../vars';\n\nexport class UserRatingsScraper {\n private films: CSFDUserRatings[] = [];\n\n public async userRatings(\n user: string | number,\n config?: CSFDUserRatingConfig,\n optionsRequest?: RequestInit\n ): Promise<CSFDUserRatings[]> {\n let allMovies: CSFDUserRatings[] = [];\n const url = userRatingsUrl(user);\n const response = await fetchPage(url, { ...optionsRequest });\n const items = parse(response);\n const movies = items.querySelectorAll('.box-user-rating .table-container tbody 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);\n const response = await fetchPage(url, { ...optionsRequest });\n\n const items = parse(response);\n const movies = items.querySelectorAll('.box-user-rating .table-container tbody tr');\n 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 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 this.buildUserRatings(el);\n }\n // Filter exludes\n } else if (config?.excludes?.length) {\n if (!config.excludes.some((exclude) => type === exclude)) {\n this.buildUserRatings(el);\n }\n } else {\n // Without filtering\n this.buildUserRatings(el);\n }\n }\n return this.films;\n }\n\n private buildUserRatings(el: HTMLElement) {\n this.films.push({\n id: getUserRatingId(el),\n title: getUserRatingTitle(el),\n year: getUserRatingYear(el),\n type: getUserRatingType(el),\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":";;;;;;AAiBA,IAAa,qBAAb,MAAgC;;eACK,EAAE;;CAErC,MAAa,YACX,MACA,QACA,gBAC4B;EAC5B,IAAIA,YAA+B,EAAE;EACrC,MAAM,MAAM,eAAe,KAAK;EAEhC,MAAM,QAAQ,MADG,MAAM,UAAU,KAAK,EAAE,GAAG,gBAAgB,CAAC,CAC/B;EAC7B,MAAM,SAAS,MAAM,iBAAiB,6CAA6C;EAGnF,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,MAAMC,WADQ,MAFG,MAAM,UADX,eAAe,MAAM,EAAE,EACG,EAAE,GAAG,gBAAgB,CAAC,CAE/B,CACR,iBAAiB,6CAA6C;AACnF,gBAAY,CAAC,GAAG,KAAK,QAAQ,QAAQA,SAAO,CAAC;AAG7C,QAAI,OAAO,cACT,OAAM,MAAM,OAAO,cAAc;;AAGrC,UAAO;;AAGT,SAAO;;CAGT,AAAQ,QAAQ,QAA8B,QAAuB;AACnE,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,MAAK,iBAAiB,GAAG;cAGlB,QAAQ,UAAU,QAC3B;QAAI,CAAC,OAAO,SAAS,MAAM,YAAY,SAAS,QAAQ,CACtD,MAAK,iBAAiB,GAAG;SAI3B,MAAK,iBAAiB,GAAG;;AAG7B,SAAO,KAAK;;CAGd,AAAQ,iBAAiB,IAAiB;AACxC,OAAK,MAAM,KAAK;GACd,IAAI,gBAAgB,GAAG;GACvB,OAAO,mBAAmB,GAAG;GAC7B,MAAM,kBAAkB,GAAG;GAC3B,MAAM,kBAAkB,GAAG;GAC3B,KAAK,iBAAiB,GAAG;GACzB,aAAa,yBAAyB,GAAG;GACzC,UAAU,kBAAkB,GAAG;GAC/B,YAAY,cAAc,GAAG;GAC9B,CAAC"}
1
+ {"version":3,"file":"user-ratings.service.mjs","names":["allMovies: CSFDUserRatings[]","movies","films: CSFDUserRatings[]"],"sources":["../../src/services/user-ratings.service.ts"],"sourcesContent":["import { HTMLElement, parse } from 'node-html-parser';\nimport { CSFDColorRating, 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 { userRatingsUrl } from '../vars';\n\nexport class UserRatingsScraper {\n public async userRatings(\n user: string | number,\n config?: CSFDUserRatingConfig,\n optionsRequest?: RequestInit\n ): Promise<CSFDUserRatings[]> {\n let allMovies: CSFDUserRatings[] = [];\n const pageToFetch = config?.page || 1;\n const url = userRatingsUrl(user, pageToFetch > 1 ? pageToFetch : undefined);\n const response = await fetchPage(url, { ...optionsRequest });\n const items = parse(response);\n const movies = items.querySelectorAll('.box-user-rating .table-container tbody 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);\n const response = await fetchPage(url, { ...optionsRequest });\n\n const items = parse(response);\n const movies = items.querySelectorAll('.box-user-rating .table-container tbody 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));\n }\n // Filter excludes\n } else if (config?.excludes?.length) {\n if (!config.excludes.some((exclude) => type === exclude)) {\n films.push(this.buildUserRatings(el));\n }\n } else {\n // Without filtering\n films.push(this.buildUserRatings(el));\n }\n }\n return films;\n }\n\n private buildUserRatings(el: HTMLElement): CSFDUserRatings {\n return {\n id: getUserRatingId(el),\n title: getUserRatingTitle(el),\n year: getUserRatingYear(el),\n type: getUserRatingType(el),\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":";;;;;;;AAiBA,IAAa,qBAAb,MAAgC;CAC9B,MAAa,YACX,MACA,QACA,gBAC4B;EAC5B,IAAIA,YAA+B,EAAE;EACrC,MAAM,cAAc,QAAQ,QAAQ;EACpC,MAAM,MAAM,eAAe,MAAM,cAAc,IAAI,cAAc,OAAU;EAE3E,MAAM,QAAQ,MADG,MAAM,UAAU,KAAK,EAAE,GAAG,gBAAgB,CAAC,CAC/B;EAC7B,MAAM,SAAS,MAAM,iBAAiB,6CAA6C;EAGnF,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,MAAMC,WADQ,MAFG,MAAM,UADX,eAAe,MAAM,EAAE,EACG,EAAE,GAAG,gBAAgB,CAAC,CAE/B,CACR,iBAAiB,6CAA6C;AACnF,gBAAY,CAAC,GAAG,WAAW,GAAG,KAAK,QAAQ,QAAQA,SAAO,CAAC;AAG3D,QAAI,OAAO,cACT,OAAM,MAAM,OAAO,cAAc;;AAGrC,UAAO;;AAGT,SAAO;;CAGT,AAAQ,QAAQ,QAA8B,QAAuB;EACnE,MAAMC,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,GAAG,CAAC;cAG9B,QAAQ,UAAU,QAC3B;QAAI,CAAC,OAAO,SAAS,MAAM,YAAY,SAAS,QAAQ,CACtD,OAAM,KAAK,KAAK,iBAAiB,GAAG,CAAC;SAIvC,OAAM,KAAK,KAAK,iBAAiB,GAAG,CAAC;;AAGzC,SAAO;;CAGT,AAAQ,iBAAiB,IAAkC;AACzD,SAAO;GACL,IAAI,gBAAgB,GAAG;GACvB,OAAO,mBAAmB,GAAG;GAC7B,MAAM,kBAAkB,GAAG;GAC3B,MAAM,kBAAkB,GAAG;GAC3B,KAAK,iBAAiB,GAAG;GACzB,aAAa,yBAAyB,GAAG;GACzC,UAAU,kBAAkB,GAAG;GAC/B,YAAY,cAAc,GAAG;GAC9B"}
@@ -0,0 +1,11 @@
1
+ import { CSFDUserReviews, CSFDUserReviewsConfig } from "../dto/user-reviews.mjs";
2
+
3
+ //#region src/services/user-reviews.service.d.ts
4
+ declare class UserReviewsScraper {
5
+ userReviews(user: string | number, config?: CSFDUserReviewsConfig, optionsRequest?: RequestInit): Promise<CSFDUserReviews[]>;
6
+ private getPage;
7
+ private buildUserReviews;
8
+ }
9
+ //#endregion
10
+ export { UserReviewsScraper };
11
+ //# sourceMappingURL=user-reviews.service.d.mts.map
@@ -0,0 +1,11 @@
1
+ import { CSFDUserReviews, CSFDUserReviewsConfig } from "../dto/user-reviews.js";
2
+
3
+ //#region src/services/user-reviews.service.d.ts
4
+ declare class UserReviewsScraper {
5
+ userReviews(user: string | number, config?: CSFDUserReviewsConfig, optionsRequest?: RequestInit): Promise<CSFDUserReviews[]>;
6
+ private getPage;
7
+ private buildUserReviews;
8
+ }
9
+ //#endregion
10
+ export { UserReviewsScraper };
11
+ //# sourceMappingURL=user-reviews.service.d.ts.map
@@ -0,0 +1,68 @@
1
+ const require_rolldown_runtime = require('../_virtual/rolldown_runtime.js');
2
+ const require_index = require('../fetchers/index.js');
3
+ const require_vars = require('../vars.js');
4
+ const require_global_helper = require('../helpers/global.helper.js');
5
+ const require_user_reviews_helper = require('../helpers/user-reviews.helper.js');
6
+ let node_html_parser = require("node-html-parser");
7
+ node_html_parser = require_rolldown_runtime.__toESM(node_html_parser);
8
+
9
+ //#region src/services/user-reviews.service.ts
10
+ var UserReviewsScraper = class {
11
+ async userReviews(user, config, optionsRequest) {
12
+ let allReviews = [];
13
+ const pageToFetch = config?.page || 1;
14
+ const url = require_vars.userReviewsUrl(user, pageToFetch > 1 ? pageToFetch : void 0);
15
+ const items = (0, node_html_parser.parse)(await require_index.fetchPage(url, { ...optionsRequest }));
16
+ const reviews = items.querySelectorAll(".user-reviews .article");
17
+ const pagesNode = items.querySelector(".pagination");
18
+ const pages = +pagesNode?.childNodes[pagesNode.childNodes.length - 4].rawText || 1;
19
+ allReviews = this.getPage(config, reviews);
20
+ if (config?.allPages) {
21
+ console.log("User", user, url);
22
+ console.log("Fetching all pages", pages);
23
+ for (let i = 2; i <= pages; i++) {
24
+ console.log("Fetching page", i, "out of", pages, "...");
25
+ const reviews$1 = (0, node_html_parser.parse)(await require_index.fetchPage(require_vars.userReviewsUrl(user, i), { ...optionsRequest })).querySelectorAll(".user-reviews .article");
26
+ allReviews = [...allReviews, ...this.getPage(config, reviews$1)];
27
+ if (config.allPagesDelay) await require_global_helper.sleep(config.allPagesDelay);
28
+ }
29
+ return allReviews;
30
+ }
31
+ return allReviews;
32
+ }
33
+ getPage(config, reviews) {
34
+ const films = [];
35
+ if (config) {
36
+ if (config.includesOnly?.length && config.excludes?.length) console.warn(`node-csfd-api:
37
+ You can not use both parameters 'includesOnly' and 'excludes'.
38
+ Parameter 'includesOnly' will be used now:`, config.includesOnly);
39
+ }
40
+ for (const el of reviews) {
41
+ const type = require_user_reviews_helper.getUserReviewType(el);
42
+ if (config?.includesOnly?.length) {
43
+ if (config.includesOnly.some((include) => type === include)) films.push(this.buildUserReviews(el));
44
+ } else if (config?.excludes?.length) {
45
+ if (!config.excludes.some((exclude) => type === exclude)) films.push(this.buildUserReviews(el));
46
+ } else films.push(this.buildUserReviews(el));
47
+ }
48
+ return films;
49
+ }
50
+ buildUserReviews(el) {
51
+ return {
52
+ id: require_user_reviews_helper.getUserReviewId(el),
53
+ title: require_user_reviews_helper.getUserReviewTitle(el),
54
+ year: require_user_reviews_helper.getUserReviewYear(el),
55
+ type: require_user_reviews_helper.getUserReviewType(el),
56
+ url: require_user_reviews_helper.getUserReviewUrl(el),
57
+ colorRating: require_user_reviews_helper.getUserReviewColorRating(el),
58
+ userDate: require_user_reviews_helper.getUserReviewDate(el),
59
+ userRating: require_user_reviews_helper.getUserReviewRating(el),
60
+ text: require_user_reviews_helper.getUserReviewText(el),
61
+ poster: require_user_reviews_helper.getUserReviewPoster(el)
62
+ };
63
+ }
64
+ };
65
+
66
+ //#endregion
67
+ exports.UserReviewsScraper = UserReviewsScraper;
68
+ //# sourceMappingURL=user-reviews.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user-reviews.service.js","names":["allReviews: CSFDUserReviews[]","userReviewsUrl","fetchPage","reviews","sleep","films: CSFDUserReviews[]","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, 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 { userReviewsUrl } from '../vars';\n\nexport class UserReviewsScraper {\n public async userReviews(\n user: string | number,\n config?: CSFDUserReviewsConfig,\n optionsRequest?: RequestInit\n ): Promise<CSFDUserReviews[]> {\n let allReviews: CSFDUserReviews[] = [];\n const pageToFetch = config?.page || 1;\n const url = userReviewsUrl(user, pageToFetch > 1 ? pageToFetch : undefined);\n const response = await fetchPage(url, { ...optionsRequest });\n const items = parse(response);\n const reviews = items.querySelectorAll('.user-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);\n const response = await fetchPage(url, { ...optionsRequest });\n\n const items = parse(response);\n const reviews = items.querySelectorAll('.user-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));\n }\n // Filter excludes\n } else if (config?.excludes?.length) {\n if (!config.excludes.some((exclude) => type === exclude)) {\n films.push(this.buildUserReviews(el));\n }\n } else {\n // Without filtering\n films.push(this.buildUserReviews(el));\n }\n }\n return films;\n }\n\n private buildUserReviews(el: HTMLElement): CSFDUserReviews {\n return {\n id: getUserReviewId(el),\n title: getUserReviewTitle(el),\n year: getUserReviewYear(el),\n type: getUserReviewType(el),\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":";;;;;;;;;AAmBA,IAAa,qBAAb,MAAgC;CAC9B,MAAa,YACX,MACA,QACA,gBAC4B;EAC5B,IAAIA,aAAgC,EAAE;EACtC,MAAM,cAAc,QAAQ,QAAQ;EACpC,MAAM,MAAMC,4BAAe,MAAM,cAAc,IAAI,cAAc,OAAU;EAE3E,MAAM,oCADW,MAAMC,wBAAU,KAAK,EAAE,GAAG,gBAAgB,CAAC,CAC/B;EAC7B,MAAM,UAAU,MAAM,iBAAiB,yBAAyB;EAGhE,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,MAAMC,wCAHW,MAAMD,wBADXD,4BAAe,MAAM,EAAE,EACG,EAAE,GAAG,gBAAgB,CAAC,CAE/B,CACP,iBAAiB,yBAAyB;AAChE,iBAAa,CAAC,GAAG,YAAY,GAAG,KAAK,QAAQ,QAAQE,UAAQ,CAAC;AAG9D,QAAI,OAAO,cACT,OAAMC,4BAAM,OAAO,cAAc;;AAGrC,UAAO;;AAGT,SAAO;;CAGT,AAAQ,QAAQ,QAA+B,SAAwB;EACrE,MAAMC,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,GAAG,CAAC;cAG9B,QAAQ,UAAU,QAC3B;QAAI,CAAC,OAAO,SAAS,MAAM,YAAY,SAAS,QAAQ,CACtD,OAAM,KAAK,KAAK,iBAAiB,GAAG,CAAC;SAIvC,OAAM,KAAK,KAAK,iBAAiB,GAAG,CAAC;;AAGzC,SAAO;;CAGT,AAAQ,iBAAiB,IAAkC;AACzD,SAAO;GACL,IAAIC,4CAAgB,GAAG;GACvB,OAAOC,+CAAmB,GAAG;GAC7B,MAAMC,8CAAkB,GAAG;GAC3B,MAAMH,8CAAkB,GAAG;GAC3B,KAAKI,6CAAiB,GAAG;GACzB,aAAaC,qDAAyB,GAAG;GACzC,UAAUC,8CAAkB,GAAG;GAC/B,YAAYC,gDAAoB,GAAG;GACnC,MAAMC,8CAAkB,GAAG;GAC3B,QAAQC,gDAAoB,GAAG;GAChC"}
@@ -0,0 +1,66 @@
1
+ import { fetchPage } from "../fetchers/index.mjs";
2
+ import { userReviewsUrl } from "../vars.mjs";
3
+ import { sleep } from "../helpers/global.helper.mjs";
4
+ import { getUserReviewColorRating, getUserReviewDate, getUserReviewId, getUserReviewPoster, getUserReviewRating, getUserReviewText, getUserReviewTitle, getUserReviewType, getUserReviewUrl, getUserReviewYear } from "../helpers/user-reviews.helper.mjs";
5
+ import { parse } from "node-html-parser";
6
+
7
+ //#region src/services/user-reviews.service.ts
8
+ var UserReviewsScraper = class {
9
+ async userReviews(user, config, optionsRequest) {
10
+ let allReviews = [];
11
+ const pageToFetch = config?.page || 1;
12
+ const url = userReviewsUrl(user, pageToFetch > 1 ? pageToFetch : void 0);
13
+ const items = parse(await fetchPage(url, { ...optionsRequest }));
14
+ const reviews = items.querySelectorAll(".user-reviews .article");
15
+ const pagesNode = items.querySelector(".pagination");
16
+ const pages = +pagesNode?.childNodes[pagesNode.childNodes.length - 4].rawText || 1;
17
+ allReviews = this.getPage(config, reviews);
18
+ if (config?.allPages) {
19
+ console.log("User", user, url);
20
+ console.log("Fetching all pages", pages);
21
+ for (let i = 2; i <= pages; i++) {
22
+ console.log("Fetching page", i, "out of", pages, "...");
23
+ const reviews$1 = parse(await fetchPage(userReviewsUrl(user, i), { ...optionsRequest })).querySelectorAll(".user-reviews .article");
24
+ allReviews = [...allReviews, ...this.getPage(config, reviews$1)];
25
+ if (config.allPagesDelay) await sleep(config.allPagesDelay);
26
+ }
27
+ return allReviews;
28
+ }
29
+ return allReviews;
30
+ }
31
+ getPage(config, reviews) {
32
+ const films = [];
33
+ if (config) {
34
+ if (config.includesOnly?.length && config.excludes?.length) console.warn(`node-csfd-api:
35
+ You can not use both parameters 'includesOnly' and 'excludes'.
36
+ Parameter 'includesOnly' will be used now:`, config.includesOnly);
37
+ }
38
+ for (const el of reviews) {
39
+ const type = getUserReviewType(el);
40
+ if (config?.includesOnly?.length) {
41
+ if (config.includesOnly.some((include) => type === include)) films.push(this.buildUserReviews(el));
42
+ } else if (config?.excludes?.length) {
43
+ if (!config.excludes.some((exclude) => type === exclude)) films.push(this.buildUserReviews(el));
44
+ } else films.push(this.buildUserReviews(el));
45
+ }
46
+ return films;
47
+ }
48
+ buildUserReviews(el) {
49
+ return {
50
+ id: getUserReviewId(el),
51
+ title: getUserReviewTitle(el),
52
+ year: getUserReviewYear(el),
53
+ type: getUserReviewType(el),
54
+ url: getUserReviewUrl(el),
55
+ colorRating: getUserReviewColorRating(el),
56
+ userDate: getUserReviewDate(el),
57
+ userRating: getUserReviewRating(el),
58
+ text: getUserReviewText(el),
59
+ poster: getUserReviewPoster(el)
60
+ };
61
+ }
62
+ };
63
+
64
+ //#endregion
65
+ export { UserReviewsScraper };
66
+ //# sourceMappingURL=user-reviews.service.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user-reviews.service.mjs","names":["allReviews: CSFDUserReviews[]","reviews","films: CSFDUserReviews[]"],"sources":["../../src/services/user-reviews.service.ts"],"sourcesContent":["import { HTMLElement, parse } from 'node-html-parser';\nimport { CSFDColorRating, 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 { userReviewsUrl } from '../vars';\n\nexport class UserReviewsScraper {\n public async userReviews(\n user: string | number,\n config?: CSFDUserReviewsConfig,\n optionsRequest?: RequestInit\n ): Promise<CSFDUserReviews[]> {\n let allReviews: CSFDUserReviews[] = [];\n const pageToFetch = config?.page || 1;\n const url = userReviewsUrl(user, pageToFetch > 1 ? pageToFetch : undefined);\n const response = await fetchPage(url, { ...optionsRequest });\n const items = parse(response);\n const reviews = items.querySelectorAll('.user-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);\n const response = await fetchPage(url, { ...optionsRequest });\n\n const items = parse(response);\n const reviews = items.querySelectorAll('.user-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));\n }\n // Filter excludes\n } else if (config?.excludes?.length) {\n if (!config.excludes.some((exclude) => type === exclude)) {\n films.push(this.buildUserReviews(el));\n }\n } else {\n // Without filtering\n films.push(this.buildUserReviews(el));\n }\n }\n return films;\n }\n\n private buildUserReviews(el: HTMLElement): CSFDUserReviews {\n return {\n id: getUserReviewId(el),\n title: getUserReviewTitle(el),\n year: getUserReviewYear(el),\n type: getUserReviewType(el),\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":";;;;;;;AAmBA,IAAa,qBAAb,MAAgC;CAC9B,MAAa,YACX,MACA,QACA,gBAC4B;EAC5B,IAAIA,aAAgC,EAAE;EACtC,MAAM,cAAc,QAAQ,QAAQ;EACpC,MAAM,MAAM,eAAe,MAAM,cAAc,IAAI,cAAc,OAAU;EAE3E,MAAM,QAAQ,MADG,MAAM,UAAU,KAAK,EAAE,GAAG,gBAAgB,CAAC,CAC/B;EAC7B,MAAM,UAAU,MAAM,iBAAiB,yBAAyB;EAGhE,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,MAAMC,YADQ,MAFG,MAAM,UADX,eAAe,MAAM,EAAE,EACG,EAAE,GAAG,gBAAgB,CAAC,CAE/B,CACP,iBAAiB,yBAAyB;AAChE,iBAAa,CAAC,GAAG,YAAY,GAAG,KAAK,QAAQ,QAAQA,UAAQ,CAAC;AAG9D,QAAI,OAAO,cACT,OAAM,MAAM,OAAO,cAAc;;AAGrC,UAAO;;AAGT,SAAO;;CAGT,AAAQ,QAAQ,QAA+B,SAAwB;EACrE,MAAMC,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,GAAG,CAAC;cAG9B,QAAQ,UAAU,QAC3B;QAAI,CAAC,OAAO,SAAS,MAAM,YAAY,SAAS,QAAQ,CACtD,OAAM,KAAK,KAAK,iBAAiB,GAAG,CAAC;SAIvC,OAAM,KAAK,KAAK,iBAAiB,GAAG,CAAC;;AAGzC,SAAO;;CAGT,AAAQ,iBAAiB,IAAkC;AACzD,SAAO;GACL,IAAI,gBAAgB,GAAG;GACvB,OAAO,mBAAmB,GAAG;GAC7B,MAAM,kBAAkB,GAAG;GAC3B,MAAM,kBAAkB,GAAG;GAC3B,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"}
package/vars.js CHANGED
@@ -7,6 +7,7 @@ const cinemasUrl = (district, period) => {
7
7
  return `https://www.csfd.cz/kino/?period=${period}&district=${district}`;
8
8
  };
9
9
  const searchUrl = (text) => `https://www.csfd.cz/hledat/?q=${encodeURIComponent(text)}`;
10
+ const userReviewsUrl = (user, page) => `https://www.csfd.cz/uzivatel/${encodeURIComponent(user)}/recenze/${page ? "?page=" + page : ""}`;
10
11
 
11
12
  //#endregion
12
13
  exports.cinemasUrl = cinemasUrl;
@@ -14,4 +15,5 @@ exports.creatorUrl = creatorUrl;
14
15
  exports.movieUrl = movieUrl;
15
16
  exports.searchUrl = searchUrl;
16
17
  exports.userRatingsUrl = userRatingsUrl;
18
+ exports.userReviewsUrl = userReviewsUrl;
17
19
  //# sourceMappingURL=vars.js.map
package/vars.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"vars.js","names":[],"sources":["../src/vars.ts"],"sourcesContent":["import { CSFDCinemaPeriod } from './dto/cinema';\n\nexport const userRatingsUrl = (user: string | number, page?: number): string =>\n `https://www.csfd.cz/uzivatel/${encodeURIComponent(user)}/hodnoceni/${page ? '?page=' + page : ''\n }`;\n\nexport const movieUrl = (movie: number): string =>\n `https://www.csfd.cz/film/${encodeURIComponent(movie)}/prehled/`;\n\nexport const creatorUrl = (creator: number | string): string =>\n `https://www.csfd.cz/tvurce/${encodeURIComponent(creator)}`;\n\nexport const cinemasUrl = (district: number | string, period: CSFDCinemaPeriod): string => {\n return `https://www.csfd.cz/kino/?period=${period}&district=${district}`;\n};\n\nexport const searchUrl = (text: string): string =>\n `https://www.csfd.cz/hledat/?q=${encodeURIComponent(text)}`;\n"],"mappings":";;AAEA,MAAa,kBAAkB,MAAuB,SACpD,gCAAgC,mBAAmB,KAAK,CAAC,aAAa,OAAO,WAAW,OAAO;AAGjG,MAAa,YAAY,UACvB,4BAA4B,mBAAmB,MAAM,CAAC;AAExD,MAAa,cAAc,YACzB,8BAA8B,mBAAmB,QAAQ;AAE3D,MAAa,cAAc,UAA2B,WAAqC;AACzF,QAAO,oCAAoC,OAAO,YAAY;;AAGhE,MAAa,aAAa,SACxB,iCAAiC,mBAAmB,KAAK"}
1
+ {"version":3,"file":"vars.js","names":[],"sources":["../src/vars.ts"],"sourcesContent":["import { CSFDCinemaPeriod } from './dto/cinema';\n\nexport const userRatingsUrl = (user: string | number, page?: number): string =>\n `https://www.csfd.cz/uzivatel/${encodeURIComponent(user)}/hodnoceni/${\n page ? '?page=' + page : ''\n }`;\n\nexport const movieUrl = (movie: number): string =>\n `https://www.csfd.cz/film/${encodeURIComponent(movie)}/prehled/`;\n\nexport const creatorUrl = (creator: number | string): string =>\n `https://www.csfd.cz/tvurce/${encodeURIComponent(creator)}`;\n\nexport const cinemasUrl = (district: number | string, period: CSFDCinemaPeriod): string => {\n return `https://www.csfd.cz/kino/?period=${period}&district=${district}`;\n};\n\nexport const searchUrl = (text: string): string =>\n `https://www.csfd.cz/hledat/?q=${encodeURIComponent(text)}`;\n\nexport const userReviewsUrl = (user: string | number, page?: number): string =>\n `https://www.csfd.cz/uzivatel/${encodeURIComponent(user)}/recenze/${page ? '?page=' + page : ''}`;\n"],"mappings":";;AAEA,MAAa,kBAAkB,MAAuB,SACpD,gCAAgC,mBAAmB,KAAK,CAAC,aACvD,OAAO,WAAW,OAAO;AAG7B,MAAa,YAAY,UACvB,4BAA4B,mBAAmB,MAAM,CAAC;AAExD,MAAa,cAAc,YACzB,8BAA8B,mBAAmB,QAAQ;AAE3D,MAAa,cAAc,UAA2B,WAAqC;AACzF,QAAO,oCAAoC,OAAO,YAAY;;AAGhE,MAAa,aAAa,SACxB,iCAAiC,mBAAmB,KAAK;AAE3D,MAAa,kBAAkB,MAAuB,SACpD,gCAAgC,mBAAmB,KAAK,CAAC,WAAW,OAAO,WAAW,OAAO"}
package/vars.mjs CHANGED
@@ -6,7 +6,8 @@ const cinemasUrl = (district, period) => {
6
6
  return `https://www.csfd.cz/kino/?period=${period}&district=${district}`;
7
7
  };
8
8
  const searchUrl = (text) => `https://www.csfd.cz/hledat/?q=${encodeURIComponent(text)}`;
9
+ const userReviewsUrl = (user, page) => `https://www.csfd.cz/uzivatel/${encodeURIComponent(user)}/recenze/${page ? "?page=" + page : ""}`;
9
10
 
10
11
  //#endregion
11
- export { cinemasUrl, creatorUrl, movieUrl, searchUrl, userRatingsUrl };
12
+ export { cinemasUrl, creatorUrl, movieUrl, searchUrl, userRatingsUrl, userReviewsUrl };
12
13
  //# sourceMappingURL=vars.mjs.map
package/vars.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"vars.mjs","names":[],"sources":["../src/vars.ts"],"sourcesContent":["import { CSFDCinemaPeriod } from './dto/cinema';\n\nexport const userRatingsUrl = (user: string | number, page?: number): string =>\n `https://www.csfd.cz/uzivatel/${encodeURIComponent(user)}/hodnoceni/${page ? '?page=' + page : ''\n }`;\n\nexport const movieUrl = (movie: number): string =>\n `https://www.csfd.cz/film/${encodeURIComponent(movie)}/prehled/`;\n\nexport const creatorUrl = (creator: number | string): string =>\n `https://www.csfd.cz/tvurce/${encodeURIComponent(creator)}`;\n\nexport const cinemasUrl = (district: number | string, period: CSFDCinemaPeriod): string => {\n return `https://www.csfd.cz/kino/?period=${period}&district=${district}`;\n};\n\nexport const searchUrl = (text: string): string =>\n `https://www.csfd.cz/hledat/?q=${encodeURIComponent(text)}`;\n"],"mappings":";AAEA,MAAa,kBAAkB,MAAuB,SACpD,gCAAgC,mBAAmB,KAAK,CAAC,aAAa,OAAO,WAAW,OAAO;AAGjG,MAAa,YAAY,UACvB,4BAA4B,mBAAmB,MAAM,CAAC;AAExD,MAAa,cAAc,YACzB,8BAA8B,mBAAmB,QAAQ;AAE3D,MAAa,cAAc,UAA2B,WAAqC;AACzF,QAAO,oCAAoC,OAAO,YAAY;;AAGhE,MAAa,aAAa,SACxB,iCAAiC,mBAAmB,KAAK"}
1
+ {"version":3,"file":"vars.mjs","names":[],"sources":["../src/vars.ts"],"sourcesContent":["import { CSFDCinemaPeriod } from './dto/cinema';\n\nexport const userRatingsUrl = (user: string | number, page?: number): string =>\n `https://www.csfd.cz/uzivatel/${encodeURIComponent(user)}/hodnoceni/${\n page ? '?page=' + page : ''\n }`;\n\nexport const movieUrl = (movie: number): string =>\n `https://www.csfd.cz/film/${encodeURIComponent(movie)}/prehled/`;\n\nexport const creatorUrl = (creator: number | string): string =>\n `https://www.csfd.cz/tvurce/${encodeURIComponent(creator)}`;\n\nexport const cinemasUrl = (district: number | string, period: CSFDCinemaPeriod): string => {\n return `https://www.csfd.cz/kino/?period=${period}&district=${district}`;\n};\n\nexport const searchUrl = (text: string): string =>\n `https://www.csfd.cz/hledat/?q=${encodeURIComponent(text)}`;\n\nexport const userReviewsUrl = (user: string | number, page?: number): string =>\n `https://www.csfd.cz/uzivatel/${encodeURIComponent(user)}/recenze/${page ? '?page=' + page : ''}`;\n"],"mappings":";AAEA,MAAa,kBAAkB,MAAuB,SACpD,gCAAgC,mBAAmB,KAAK,CAAC,aACvD,OAAO,WAAW,OAAO;AAG7B,MAAa,YAAY,UACvB,4BAA4B,mBAAmB,MAAM,CAAC;AAExD,MAAa,cAAc,YACzB,8BAA8B,mBAAmB,QAAQ;AAE3D,MAAa,cAAc,UAA2B,WAAqC;AACzF,QAAO,oCAAoC,OAAO,YAAY;;AAGhE,MAAa,aAAa,SACxB,iCAAiC,mBAAmB,KAAK;AAE3D,MAAa,kBAAkB,MAAuB,SACpD,gCAAgC,mBAAmB,KAAK,CAAC,WAAW,OAAO,WAAW,OAAO"}