node-csfd-api 4.3.3 → 5.0.0-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -223,7 +223,7 @@ console.log(results.users); // Array of users
223
223
  title: 'Black Bart',
224
224
  year: '1975',
225
225
  url: 'https://www.csfd.cz/film/19653-black-bart/',
226
- type: 'TV film',
226
+ type: 'tv-film',
227
227
  colorRating: 'bad',
228
228
  poster: 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7',
229
229
  origins: ['USA'],
@@ -247,7 +247,7 @@ tvSeries: [
247
247
  title: 'Království',
248
248
  year: 1994,
249
249
  url: 'https://www.csfd.cz/film/71924-kralovstvi/',
250
- type: 'seriál',
250
+ type: 'series',
251
251
  colorRating: 'good',
252
252
  poster: 'https://image.pmgstatic.com/cache/resized/w60h85/files/images/film/posters/166/708/166708064_2da697.jpg',
253
253
  origins: ['Dánsko'],
@@ -398,7 +398,7 @@ const onlyMovies = await csfd.userRatings('912-bart', {
398
398
  });
399
399
 
400
400
  const excludeEpisodes = await csfd.userRatings('912-bart', {
401
- exclude: ['epizoda', 'série']
401
+ exclude: ['episode', 'season']
402
402
  });
403
403
  ```
404
404
 
@@ -434,13 +434,13 @@ const excludeEpisodes = await csfd.userRatings('912-bart', {
434
434
 
435
435
  #### UserRatingsOptions
436
436
 
437
- | Option | Type | Default | Description |
438
- | --------------- | ----------------- | ------- | ---------------------------------------------------------------- |
439
- | `includesOnly` | `CSFDFilmTypes[]` | `null` | Include only specific content types (e.g., `['film', 'seriál']`) |
440
- | `exclude` | `CSFDFilmTypes[]` | `null` | Exclude specific content types (e.g., `['epizoda']`) |
441
- | `allPages` | `boolean` | `false` | Fetch all pages of ratings |
442
- | `allPagesDelay` | `number` | `0` | Delay between page requests in milliseconds |
443
- | `page` | `number` | `1` | Fetch specific page number |
437
+ | Option | Type | Default | Description |
438
+ | --------------- | ----------------- | ------- | -------------------------------------------------------------------- |
439
+ | `includesOnly` | `CSFDFilmTypes[]` | `null` | Include only specific content types (e.g., `['film', 'series']`) |
440
+ | `exclude` | `CSFDFilmTypes[]` | `null` | Exclude specific content types (e.g., `['episode']`) |
441
+ | `allPages` | `boolean` | `false` | Fetch all pages of ratings |
442
+ | `allPagesDelay` | `number` | `0` | Delay between page requests in milliseconds |
443
+ | `page` | `number` | `1` | Fetch specific page number |
444
444
 
445
445
  > 📝 **Note**: `includesOnly` and `exclude` are mutually exclusive. If both are provided, `includesOnly` takes precedence.
446
446
  >
@@ -476,7 +476,7 @@ const allReviews = await csfd.userReviews(195357, {
476
476
  // Filter by content type
477
477
  const filtered = await csfd.userReviews(195357, {
478
478
  includesOnly: ['film'],
479
- exclude: ['epizoda']
479
+ exclude: ['episode']
480
480
  });
481
481
  ```
482
482
 
@@ -1,61 +1,65 @@
1
+ #!/usr/bin/env node
2
+ import { csfd } from "../index.mjs";
1
3
  import { writeFile } from "node:fs/promises";
2
- import { csfd } from "./../index.mjs";
4
+
5
+ //#region src/tools/export-ratings.ts
6
+ /**
7
+ * Runs the ratings export process for a given user.
8
+ * @param userId - The CSFD user ID.
9
+ * @param options - Export options (format, filters, language).
10
+ */
3
11
  async function runRatingsExport(userId, options) {
4
- try {
5
- if (options.format === "letterboxd") {
6
- csfd.setOptions({ language: "en" });
7
- }
8
- console.log(`Fetching ratings for user ${userId} (${options.format.toUpperCase()})...`);
9
- const ratings = await csfd.userRatings(userId, options.userRatingsOptions);
10
- console.log(`Fetched ${ratings.length} ratings.`);
11
- let content = "";
12
- let fileName = "";
13
- const escapeCsvField = (value) => {
14
- const needsQuotes = /[",\n\r]/.test(value);
15
- const escaped = value.replaceAll('"', '""');
16
- return needsQuotes ? `"${escaped}"` : escaped;
17
- };
18
- if (options.format === "letterboxd") {
19
- content = [
20
- "Title,Year,Rating,WatchedDate",
21
- ...ratings.map((r) => {
22
- const title = escapeCsvField(r.title ?? "");
23
- const year = r.year ?? "";
24
- const rating = r.userRating ?? "";
25
- const watchedDate = escapeCsvField(r.userDate ?? "");
26
- return `${title},${year},${rating},${watchedDate}`;
27
- })
28
- ].join("\n");
29
- fileName = `${userId}-for-letterboxd.csv`;
30
- } else if (options.format === "json") {
31
- content = JSON.stringify(ratings, null, 2);
32
- fileName = `${userId}-ratings.json`;
33
- } else {
34
- const headers = ["id", "title", "year", "rating", "date", "type", "url", "colorRating"];
35
- content = [
36
- headers.join(","),
37
- ...ratings.map((r) => {
38
- return [
39
- r.id,
40
- escapeCsvField(r.title ?? ""),
41
- r.year ?? "",
42
- r.userRating ?? "",
43
- escapeCsvField(r.userDate ?? ""),
44
- escapeCsvField(r.type ?? ""),
45
- escapeCsvField(r.url ?? ""),
46
- escapeCsvField(r.colorRating ?? "")
47
- ].join(",");
48
- })
49
- ].join("\n");
50
- fileName = `${userId}-ratings.csv`;
51
- }
52
- await writeFile(fileName, content);
53
- console.log("Saved in file:", `./${fileName}`);
54
- } catch (error) {
55
- console.error("Error exporting ratings:", error);
56
- throw error;
57
- }
12
+ try {
13
+ if (options.format === "letterboxd") csfd.setOptions({ language: "en" });
14
+ console.log(`Fetching ratings for user ${userId} (${options.format.toUpperCase()})...`);
15
+ const ratings = await csfd.userRatings(userId, options.userRatingsOptions);
16
+ console.log(`Fetched ${ratings.length} ratings.`);
17
+ let content = "";
18
+ let fileName = "";
19
+ const escapeCsvField = (value) => {
20
+ const needsQuotes = /[",\n\r]/.test(value);
21
+ const escaped = value.replaceAll("\"", "\"\"");
22
+ return needsQuotes ? `"${escaped}"` : escaped;
23
+ };
24
+ if (options.format === "letterboxd") {
25
+ content = ["Title,Year,Rating,WatchedDate", ...ratings.map((r) => {
26
+ return `${escapeCsvField(r.title ?? "")},${r.year ?? ""},${r.userRating ?? ""},${escapeCsvField(r.userDate ?? "")}`;
27
+ })].join("\n");
28
+ fileName = `${userId}-for-letterboxd.csv`;
29
+ } else if (options.format === "json") {
30
+ content = JSON.stringify(ratings, null, 2);
31
+ fileName = `${userId}-ratings.json`;
32
+ } else {
33
+ content = [[
34
+ "id",
35
+ "title",
36
+ "year",
37
+ "rating",
38
+ "date",
39
+ "type",
40
+ "url",
41
+ "colorRating"
42
+ ].join(","), ...ratings.map((r) => {
43
+ return [
44
+ r.id,
45
+ escapeCsvField(r.title ?? ""),
46
+ r.year ?? "",
47
+ r.userRating ?? "",
48
+ escapeCsvField(r.userDate ?? ""),
49
+ escapeCsvField(r.type ?? ""),
50
+ escapeCsvField(r.url ?? ""),
51
+ escapeCsvField(r.colorRating ?? "")
52
+ ].join(",");
53
+ })].join("\n");
54
+ fileName = `${userId}-ratings.csv`;
55
+ }
56
+ await writeFile(fileName, content);
57
+ console.log("Saved in file:", `./${fileName}`);
58
+ } catch (error) {
59
+ console.error("Error exporting ratings:", error);
60
+ throw error;
61
+ }
58
62
  }
59
- export {
60
- runRatingsExport
61
- };
63
+
64
+ //#endregion
65
+ export { runRatingsExport };
@@ -1,192 +1,185 @@
1
1
  #!/usr/bin/env node
2
+ import { csfd } from "../index.mjs";
3
+ import { name, version } from "../package.mjs";
2
4
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
5
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
6
  import { z } from "zod";
5
- import packageJson from "./../package.json" with { type: "json" };
6
- import { csfd } from "./../index.mjs";
7
+
8
+ //#region src/mcp-server/index.ts
7
9
  const server = new McpServer({
8
- name: packageJson.name,
9
- version: packageJson.version
10
+ name,
11
+ version
12
+ });
13
+ /**
14
+ * TOOL 1: Search
15
+ * Description: Essential first step to get IDs for movies or creators.
16
+ */
17
+ server.tool("search", "Searches for a movie, TV series, or person on CSFD.cz. Returns a list of results with IDs. Use this tool FIRST to find the ID needed for other tools.", { query: z.string().describe("Search query (movie title, series, or actor name)") }, async ({ query }) => {
18
+ try {
19
+ const results = await csfd.search(query);
20
+ return { content: [{
21
+ type: "text",
22
+ text: JSON.stringify(results, null, 2)
23
+ }] };
24
+ } catch (error) {
25
+ return {
26
+ content: [{
27
+ type: "text",
28
+ text: `Error during search: ${error}`
29
+ }],
30
+ isError: true
31
+ };
32
+ }
33
+ });
34
+ /**
35
+ * TOOL 2: Movie Details
36
+ * Description: Returns detailed info about a specific movie/series.
37
+ */
38
+ server.tool("get_movie", "Retrieves detailed information about a specific movie or series, including rating, plot, genres, and actors. Requires a numeric CSFD ID.", { id: z.number().describe("CSFD Movie ID (found using the 'search' tool)") }, async ({ id }) => {
39
+ try {
40
+ const movie = await csfd.movie(id);
41
+ return { content: [{
42
+ type: "text",
43
+ text: JSON.stringify(movie, null, 2)
44
+ }] };
45
+ } catch (error) {
46
+ return {
47
+ content: [{
48
+ type: "text",
49
+ text: `Error retrieving movie details: ${error}`
50
+ }],
51
+ isError: true
52
+ };
53
+ }
54
+ });
55
+ /**
56
+ * TOOL 3: Creator Details
57
+ * Description: Returns detailed info about a person (actor, director).
58
+ */
59
+ server.tool("get_creator", "Retrieves information about a specific creator (actor, director, etc.), including their biography and filmography. Requires a numeric CSFD ID.", { id: z.number().describe("CSFD Creator ID (found using the 'search' tool)") }, async ({ id }) => {
60
+ try {
61
+ const creator = await csfd.creator(id);
62
+ return { content: [{
63
+ type: "text",
64
+ text: JSON.stringify(creator, null, 2)
65
+ }] };
66
+ } catch (error) {
67
+ return {
68
+ content: [{
69
+ type: "text",
70
+ text: `Error retrieving creator details: ${error}`
71
+ }],
72
+ isError: true
73
+ };
74
+ }
75
+ });
76
+ /**
77
+ * TOOL 4: User Ratings
78
+ * Description: Returns ratings from a specific CSFD user.
79
+ */
80
+ server.tool("get_user_ratings", "Retrieves movie ratings from a specific CSFD user. Returns a list of movies with their user rating (0-5 stars). Supports pagination and filtering by film type.", {
81
+ user: z.union([z.string(), z.number()]).describe("CSFD User ID (numeric) or username"),
82
+ page: z.number().optional().describe("Page number to fetch (default: 1)"),
83
+ allPages: z.boolean().optional().describe("Fetch all pages at once (use wisely, may be slow)"),
84
+ allPagesDelay: z.number().optional().describe("Delay in ms between page requests when using allPages"),
85
+ excludes: z.array(z.string()).optional().describe("Film types to exclude (e.g. \"series\", \"tv-film\")"),
86
+ includesOnly: z.array(z.string()).optional().describe("Only include these film types (e.g. \"film\")")
87
+ }, async ({ user, page, allPages, allPagesDelay, excludes, includesOnly }) => {
88
+ try {
89
+ const results = await csfd.userRatings(user, {
90
+ page,
91
+ allPages,
92
+ allPagesDelay,
93
+ excludes,
94
+ includesOnly
95
+ });
96
+ return { content: [{
97
+ type: "text",
98
+ text: JSON.stringify(results, null, 2)
99
+ }] };
100
+ } catch (error) {
101
+ return {
102
+ content: [{
103
+ type: "text",
104
+ text: `Error retrieving user ratings: ${error}`
105
+ }],
106
+ isError: true
107
+ };
108
+ }
109
+ });
110
+ /**
111
+ * TOOL 5: User Reviews
112
+ * Description: Returns reviews written by a specific CSFD user.
113
+ */
114
+ server.tool("get_user_reviews", "Retrieves movie reviews written by a specific CSFD user. Returns a list of movies with their review text and rating. Supports pagination and filtering by film type.", {
115
+ user: z.union([z.string(), z.number()]).describe("CSFD User ID (numeric) or username"),
116
+ page: z.number().optional().describe("Page number to fetch (default: 1)"),
117
+ allPages: z.boolean().optional().describe("Fetch all pages at once (use wisely, may be slow)"),
118
+ allPagesDelay: z.number().optional().describe("Delay in ms between page requests when using allPages"),
119
+ excludes: z.array(z.string()).optional().describe("Film types to exclude (e.g. \"series\", \"tv-film\")"),
120
+ includesOnly: z.array(z.string()).optional().describe("Only include these film types (e.g. \"film\")")
121
+ }, async ({ user, page, allPages, allPagesDelay, excludes, includesOnly }) => {
122
+ try {
123
+ const results = await csfd.userReviews(user, {
124
+ page,
125
+ allPages,
126
+ allPagesDelay,
127
+ excludes,
128
+ includesOnly
129
+ });
130
+ return { content: [{
131
+ type: "text",
132
+ text: JSON.stringify(results, null, 2)
133
+ }] };
134
+ } catch (error) {
135
+ return {
136
+ content: [{
137
+ type: "text",
138
+ text: `Error retrieving user reviews: ${error}`
139
+ }],
140
+ isError: true
141
+ };
142
+ }
143
+ });
144
+ /**
145
+ * TOOL 6: Cinemas
146
+ * Description: Returns cinema screenings for a given district and time period.
147
+ */
148
+ server.tool("get_cinemas", "Retrieves cinema screenings for a given district in Czech Republic. Returns a list of cinemas with their current screenings, showtimes, and movie details.", {
149
+ district: z.union([z.number(), z.string()]).describe("District ID (numeric) or name"),
150
+ period: z.enum([
151
+ "today",
152
+ "tomorrow",
153
+ "weekend",
154
+ "week",
155
+ "month"
156
+ ]).describe("Time period for screenings")
157
+ }, async ({ district, period }) => {
158
+ try {
159
+ const results = await csfd.cinema(district, period);
160
+ return { content: [{
161
+ type: "text",
162
+ text: JSON.stringify(results, null, 2)
163
+ }] };
164
+ } catch (error) {
165
+ return {
166
+ content: [{
167
+ type: "text",
168
+ text: `Error retrieving cinema data: ${error}`
169
+ }],
170
+ isError: true
171
+ };
172
+ }
10
173
  });
11
- server.tool(
12
- "search",
13
- "Searches for a movie, TV series, or person on CSFD.cz. Returns a list of results with IDs. Use this tool FIRST to find the ID needed for other tools.",
14
- {
15
- query: z.string().describe("Search query (movie title, series, or actor name)")
16
- },
17
- async ({ query }) => {
18
- try {
19
- const results = await csfd.search(query);
20
- return {
21
- content: [
22
- {
23
- type: "text",
24
- text: JSON.stringify(results, null, 2)
25
- }
26
- ]
27
- };
28
- } catch (error) {
29
- return {
30
- content: [{ type: "text", text: `Error during search: ${error}` }],
31
- isError: true
32
- };
33
- }
34
- }
35
- );
36
- server.tool(
37
- "get_movie",
38
- "Retrieves detailed information about a specific movie or series, including rating, plot, genres, and actors. Requires a numeric CSFD ID.",
39
- {
40
- id: z.number().describe("CSFD Movie ID (found using the 'search' tool)")
41
- },
42
- async ({ id }) => {
43
- try {
44
- const movie = await csfd.movie(id);
45
- return {
46
- content: [
47
- {
48
- type: "text",
49
- text: JSON.stringify(movie, null, 2)
50
- }
51
- ]
52
- };
53
- } catch (error) {
54
- return {
55
- content: [{ type: "text", text: `Error retrieving movie details: ${error}` }],
56
- isError: true
57
- };
58
- }
59
- }
60
- );
61
- server.tool(
62
- "get_creator",
63
- "Retrieves information about a specific creator (actor, director, etc.), including their biography and filmography. Requires a numeric CSFD ID.",
64
- {
65
- id: z.number().describe("CSFD Creator ID (found using the 'search' tool)")
66
- },
67
- async ({ id }) => {
68
- try {
69
- const creator = await csfd.creator(id);
70
- return {
71
- content: [
72
- {
73
- type: "text",
74
- text: JSON.stringify(creator, null, 2)
75
- }
76
- ]
77
- };
78
- } catch (error) {
79
- return {
80
- content: [{ type: "text", text: `Error retrieving creator details: ${error}` }],
81
- isError: true
82
- };
83
- }
84
- }
85
- );
86
- server.tool(
87
- "get_user_ratings",
88
- "Retrieves movie ratings from a specific CSFD user. Returns a list of movies with their user rating (0-5 stars). Supports pagination and filtering by film type.",
89
- {
90
- user: z.union([z.string(), z.number()]).describe("CSFD User ID (numeric) or username"),
91
- page: z.number().optional().describe("Page number to fetch (default: 1)"),
92
- allPages: z.boolean().optional().describe("Fetch all pages at once (use wisely, may be slow)"),
93
- allPagesDelay: z.number().optional().describe("Delay in ms between page requests when using allPages"),
94
- excludes: z.array(z.string()).optional().describe('Film types to exclude (e.g. "seri\xE1l", "TV film")'),
95
- includesOnly: z.array(z.string()).optional().describe('Only include these film types (e.g. "film")')
96
- },
97
- async ({ user, page, allPages, allPagesDelay, excludes, includesOnly }) => {
98
- try {
99
- const results = await csfd.userRatings(user, {
100
- page,
101
- allPages,
102
- allPagesDelay,
103
- excludes,
104
- includesOnly
105
- });
106
- return {
107
- content: [
108
- {
109
- type: "text",
110
- text: JSON.stringify(results, null, 2)
111
- }
112
- ]
113
- };
114
- } catch (error) {
115
- return {
116
- content: [{ type: "text", text: `Error retrieving user ratings: ${error}` }],
117
- isError: true
118
- };
119
- }
120
- }
121
- );
122
- server.tool(
123
- "get_user_reviews",
124
- "Retrieves movie reviews written by a specific CSFD user. Returns a list of movies with their review text and rating. Supports pagination and filtering by film type.",
125
- {
126
- user: z.union([z.string(), z.number()]).describe("CSFD User ID (numeric) or username"),
127
- page: z.number().optional().describe("Page number to fetch (default: 1)"),
128
- allPages: z.boolean().optional().describe("Fetch all pages at once (use wisely, may be slow)"),
129
- allPagesDelay: z.number().optional().describe("Delay in ms between page requests when using allPages"),
130
- excludes: z.array(z.string()).optional().describe('Film types to exclude (e.g. "seri\xE1l", "TV film")'),
131
- includesOnly: z.array(z.string()).optional().describe('Only include these film types (e.g. "film")')
132
- },
133
- async ({ user, page, allPages, allPagesDelay, excludes, includesOnly }) => {
134
- try {
135
- const results = await csfd.userReviews(user, {
136
- page,
137
- allPages,
138
- allPagesDelay,
139
- excludes,
140
- includesOnly
141
- });
142
- return {
143
- content: [
144
- {
145
- type: "text",
146
- text: JSON.stringify(results, null, 2)
147
- }
148
- ]
149
- };
150
- } catch (error) {
151
- return {
152
- content: [{ type: "text", text: `Error retrieving user reviews: ${error}` }],
153
- isError: true
154
- };
155
- }
156
- }
157
- );
158
- server.tool(
159
- "get_cinemas",
160
- "Retrieves cinema screenings for a given district in Czech Republic. Returns a list of cinemas with their current screenings, showtimes, and movie details.",
161
- {
162
- district: z.union([z.number(), z.string()]).describe("District ID (numeric) or name"),
163
- period: z.enum(["today", "tomorrow", "weekend", "week", "month"]).describe("Time period for screenings")
164
- },
165
- async ({ district, period }) => {
166
- try {
167
- const results = await csfd.cinema(district, period);
168
- return {
169
- content: [
170
- {
171
- type: "text",
172
- text: JSON.stringify(results, null, 2)
173
- }
174
- ]
175
- };
176
- } catch (error) {
177
- return {
178
- content: [{ type: "text", text: `Error retrieving cinema data: ${error}` }],
179
- isError: true
180
- };
181
- }
182
- }
183
- );
184
174
  async function main() {
185
- const transport = new StdioServerTransport();
186
- await server.connect(transport);
187
- console.error("CSFD MCP Server running on stdio...");
175
+ const transport = new StdioServerTransport();
176
+ await server.connect(transport);
177
+ console.error("CSFD MCP Server running on stdio...");
188
178
  }
189
179
  main().catch((error) => {
190
- console.error("Fatal error in MCP server:", error);
191
- process.exit(1);
180
+ console.error("Fatal error in MCP server:", error);
181
+ process.exit(1);
192
182
  });
183
+
184
+ //#endregion
185
+ export { };