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 +39 -3
- package/bin/export-ratings.mjs +61 -0
- package/{mcp-server.mjs → bin/mcp-server.mjs} +2 -2
- package/{server.mjs → bin/server.mjs} +2 -2
- package/cli.mjs +101 -0
- package/package.json +4 -5
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", "
|
|
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 "
|
|
6
|
-
import { csfd } from "
|
|
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 "
|
|
5
|
-
import { csfd } from "
|
|
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.
|
|
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
|
-
"
|
|
13
|
+
"dotenv": "^17.3.1",
|
|
14
14
|
"express": "^5.2.1",
|
|
15
|
-
"
|
|
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-
|
|
55
|
-
"csfd-server": "server.mjs"
|
|
54
|
+
"node-csfd-api": "cli.mjs"
|
|
56
55
|
},
|
|
57
56
|
"sideEffects": false
|
|
58
57
|
}
|