node-csfd-api 4.2.0 → 4.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.
package/README.md CHANGED
@@ -6,9 +6,9 @@
6
6
 
7
7
  <div align="center">
8
8
 
9
- # CSFD API 🎬 + MCP Server 🤖
9
+ # CSFD API 🎬 + CSFD Export + MCP Server 🤖
10
10
 
11
- #### Modern TypeScript NPM library for scraping **CSFD.CZ**. Scraper, API, and MCP Server in one package. _(unofficial)_
11
+ #### Modern TypeScript NPM library for scraping **CSFD.CZ**. Scraper, API, Export and MCP Server in one package. _(unofficial)_
12
12
 
13
13
  [Features](#-features) • [Installation](#-installation) • [Quick Start](#-quick-start) • [API Reference](#-api-reference) • [Examples](#-usage-examples) • [MCP Server](#-mcp-server-model-context-protocol) • [Docker](#-docker-support)
14
14
 
@@ -520,6 +520,42 @@ const filtered = await csfd.userReviews(195357, {
520
520
 
521
521
  Same options as [UserRatingsOptions](#userrationsoptions).
522
522
 
523
+ ## 💻 CLI Tools
524
+
525
+ This library comes with a powerful CLI that exposes several tools.
526
+
527
+ ### 1. Export Ratings (CSV, JSON & Letterboxd)
528
+
529
+ > Backup your personal user ratings to CSV (default), JSON, or Letterboxd format. Use this tool just to keep a local copy of your data.
530
+
531
+ ```bash
532
+ # Export to CSV (default) -> saves as <userId>-ratings.csv
533
+ npx node-csfd-api export ratings 912
534
+
535
+ # Export to JSON -> saves as <userId>-ratings.json
536
+ npx node-csfd-api export ratings 912 --json
537
+
538
+ # Export to Letterboxd CSV -> saves as <userId>-for-letterboxd.csv
539
+ npx node-csfd-api export ratings 912 --letterboxd
540
+ ```
541
+
542
+ ### 2. REST API Server
543
+
544
+ Run a standalone REST API server.
545
+
546
+ ```bash
547
+ npx node-csfd-api server
548
+ # Server listening on port 3000
549
+ ```
550
+
551
+ ### 3. MCP Server
552
+
553
+ Run the Model Context Protocol server for AI agents.
554
+
555
+ ```bash
556
+ npx node-csfd-api mcp
557
+ ```
558
+
523
559
  ## 🤖 MCP Server (Model Context Protocol)
524
560
 
525
561
  This library includes a built-in [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server. This allows you to use ČSFD data directly within LLMs like **Claude Desktop**.
@@ -539,7 +575,7 @@ Add the following configuration to your `claude_desktop_config.json`:
539
575
  "mcpServers": {
540
576
  "csfd": {
541
577
  "command": "npx",
542
- "args": ["-y", "--package", "node-csfd-api", "csfd-mcp"]
578
+ "args": ["-y", "node-csfd-api", "mcp"]
543
579
  }
544
580
  }
545
581
  }
@@ -0,0 +1,61 @@
1
+ import { writeFile } from "node:fs/promises";
2
+ import { csfd } from "./../index.mjs";
3
+ 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
+ }
58
+ }
59
+ export {
60
+ runRatingsExport
61
+ };
@@ -2,8 +2,8 @@
2
2
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
3
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
4
  import { z } from "zod";
5
- import packageJson from "./package.json" with { type: "json" };
6
- import { csfd } from "./index.mjs";
5
+ import packageJson from "./../package.json" with { type: "json" };
6
+ import { csfd } from "./../index.mjs";
7
7
  const server = new McpServer({
8
8
  name: packageJson.name,
9
9
  version: packageJson.version
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import "dotenv/config";
3
3
  import express from "express";
4
- import packageJson from "./package.json" with { type: "json" };
5
- import { csfd } from "./index.mjs";
4
+ import packageJson from "./../package.json" with { type: "json" };
5
+ import { csfd } from "./../index.mjs";
6
6
  const LOG_COLORS = {
7
7
  info: "\x1B[36m",
8
8
  // cyan
package/cli.mjs ADDED
@@ -0,0 +1,101 @@
1
+ #!/usr/bin/env node
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ const __filename = fileURLToPath(import.meta.url);
5
+ const __dirname = path.dirname(__filename);
6
+ async function main() {
7
+ const args = process.argv.slice(2);
8
+ const command = args[0];
9
+ switch (command) {
10
+ case "server":
11
+ case "api":
12
+ try {
13
+ await import(path.join(__dirname, "bin/server.mjs"));
14
+ } catch (error) {
15
+ console.error("Failed to start server:", error);
16
+ process.exit(1);
17
+ }
18
+ break;
19
+ case "mcp":
20
+ try {
21
+ await import(path.join(__dirname, "bin/mcp-server.mjs"));
22
+ } catch (error) {
23
+ console.error("Failed to start MCP server:", error);
24
+ process.exit(1);
25
+ }
26
+ break;
27
+ case "export":
28
+ if (args[1] === "ratings") {
29
+ const userIdRaw = args[2];
30
+ const userId = Number(userIdRaw);
31
+ if (!userIdRaw || isNaN(userId)) {
32
+ console.error("Error: Please provide a valid numeric User ID.");
33
+ console.log("Usage: npx node-csfd-api export ratings <userId> [options]");
34
+ process.exit(1);
35
+ }
36
+ const isLetterboxd = args.includes("--letterboxd");
37
+ const isJson = args.includes("--json");
38
+ const isCsv = args.includes("--csv");
39
+ let format = "csv";
40
+ if (isLetterboxd) {
41
+ format = "letterboxd";
42
+ } else if (isJson) {
43
+ format = "json";
44
+ } else if (isCsv) {
45
+ format = "csv";
46
+ }
47
+ try {
48
+ const modulePath = path.join(__dirname, "bin/export-ratings.mjs");
49
+ const { runRatingsExport } = await import(modulePath);
50
+ await runRatingsExport(userId, {
51
+ format,
52
+ userRatingsOptions: {
53
+ // Default behavior for Letterboxd (films only) if not overridden
54
+ includesOnly: isLetterboxd ? ["film"] : void 0,
55
+ allPages: true,
56
+ allPagesDelay: 1e3
57
+ }
58
+ });
59
+ } catch (error) {
60
+ console.error("Failed to run export:", error);
61
+ process.exit(1);
62
+ }
63
+ } else if (args[1] === "letterboxd") {
64
+ console.warn(
65
+ 'Deprecation Warning: "export letterboxd" is deprecated. Please use "export ratings <id> --letterboxd" instead.'
66
+ );
67
+ console.log("Usage: npx node-csfd-api export ratings <userId> --letterboxd");
68
+ process.exit(1);
69
+ } else {
70
+ console.error(`Unknown export target: ${args[1]}`);
71
+ printUsage();
72
+ process.exit(1);
73
+ }
74
+ break;
75
+ case "help":
76
+ case "--help":
77
+ case "-h":
78
+ default:
79
+ printUsage();
80
+ break;
81
+ }
82
+ }
83
+ function printUsage() {
84
+ console.log(`
85
+ Usage: npx node-csfd-api <command> [options]
86
+
87
+ Commands:
88
+ server, api Start the REST API server
89
+ mcp Start the MCP (Model Context Protocol) server for AI agents
90
+ export ratings <userId> Export user ratings to CSV (default), JSON, or Letterboxd format
91
+ Options:
92
+ --csv Export to CSV format (default)
93
+ --json Export to JSON format
94
+ --letterboxd Export to Letterboxd-compatible CSV format
95
+ help Show this help message
96
+ `);
97
+ }
98
+ main().catch((error) => {
99
+ console.error("Fatal error:", error);
100
+ process.exit(1);
101
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-csfd-api",
3
- "version": "4.2.0",
3
+ "version": "4.3.0",
4
4
  "description": "ČSFD API in JavaScript. Amazing NPM library for scrapping csfd.cz :)",
5
5
  "author": "BART! <bart@bartweb.cz>",
6
6
  "publishConfig": {
@@ -10,9 +10,9 @@
10
10
  "dependencies": {
11
11
  "@modelcontextprotocol/sdk": "^1.26.0",
12
12
  "cross-fetch": "^4.1.0",
13
- "node-html-parser": "^7.0.2",
13
+ "dotenv": "^17.3.1",
14
14
  "express": "^5.2.1",
15
- "dotenv": "^17.2.4",
15
+ "node-html-parser": "^7.0.2",
16
16
  "zod": "^4.3.6"
17
17
  },
18
18
  "repository": {
@@ -51,8 +51,7 @@
51
51
  "./package.json": "./package.json"
52
52
  },
53
53
  "bin": {
54
- "csfd-mcp": "mcp-server.mjs",
55
- "csfd-server": "server.mjs"
54
+ "node-csfd-api": "cli.mjs"
56
55
  },
57
56
  "sideEffects": false
58
57
  }