node-csfd-api 5.6.0 → 5.6.2
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 +15 -4
- package/bin/search.js +43 -0
- package/cli.js +44 -9
- package/package.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -565,16 +565,27 @@ Download `csfd-windows-x64.zip` from the [latest release](https://github.com/bar
|
|
|
565
565
|
|
|
566
566
|
> 💡 The examples below use `csfd` (Options B, C & D). If you use npx, replace it with `npx node-csfd-api` — e.g. `npx node-csfd-api export ratings 912`.
|
|
567
567
|
|
|
568
|
-
#### 1.
|
|
568
|
+
#### 1. Search
|
|
569
569
|
|
|
570
570
|
```bash
|
|
571
|
-
csfd
|
|
571
|
+
csfd search tarantino
|
|
572
|
+
# npx node-csfd-api search tarantino
|
|
573
|
+
csfd search "blade runner" --json # raw JSON output
|
|
574
|
+
# npx node-csfd-api search "blade runner" --json
|
|
575
|
+
```
|
|
576
|
+
|
|
577
|
+
#### 2. Movie Details
|
|
578
|
+
|
|
579
|
+
```bash
|
|
580
|
+
csfd movie 535121 # by ID
|
|
572
581
|
# npx node-csfd-api movie 535121
|
|
573
|
-
csfd movie
|
|
582
|
+
csfd movie "blade runner" # by title — searches and shows the top result
|
|
583
|
+
# npx node-csfd-api movie "blade runner"
|
|
584
|
+
csfd movie 535121 --json # raw JSON output, pipe-friendly
|
|
574
585
|
# npx node-csfd-api movie 535121 --json
|
|
575
586
|
```
|
|
576
587
|
|
|
577
|
-
####
|
|
588
|
+
#### 3. Export Ratings (CSV, JSON & Letterboxd)
|
|
578
589
|
|
|
579
590
|
> Backup your personal user ratings. _Use this tool just to keep a local copy of your data._
|
|
580
591
|
|
package/bin/search.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { csfd } from "../index.js";
|
|
3
|
+
import { c } from "./utils.js";
|
|
4
|
+
|
|
5
|
+
//#region src/bin/search.ts
|
|
6
|
+
async function runSearch(query, json) {
|
|
7
|
+
const results = await csfd.search(query);
|
|
8
|
+
if (json) {
|
|
9
|
+
console.log(JSON.stringify(results, null, 2));
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
printSearch(query, results);
|
|
13
|
+
}
|
|
14
|
+
function printSearch(query, results) {
|
|
15
|
+
const ratingDot = (colorRating) => colorRating === "good" ? c.green("●") : colorRating === "average" ? c.yellow("●") : colorRating === "bad" ? c.red("●") : c.dim("●");
|
|
16
|
+
const section = (label, count) => count > 0 ? `\n${c.bold(label)} ${c.dim(`(${count})`)}` : null;
|
|
17
|
+
const total = results.movies.length + results.tvSeries.length + results.creators.length + results.users.length;
|
|
18
|
+
console.log("");
|
|
19
|
+
console.log(`${c.bold("Search results for")} ${c.cyan(`"${query}"`)} ${c.dim(`— ${total} found`)}`);
|
|
20
|
+
console.log(c.dim("─".repeat(52)));
|
|
21
|
+
const movieLine = (r) => ` ${c.dim(String(r.id).padEnd(8))} ${ratingDot(r.colorRating)} ${r.title}` + (r.year ? c.dim(` ${r.year}`) : "") + (r.origins?.length ? c.dim(` ${r.origins[0]}`) : "");
|
|
22
|
+
if (results.movies.length > 0) {
|
|
23
|
+
console.log(section("Movies", results.movies.length));
|
|
24
|
+
results.movies.forEach((r) => console.log(movieLine(r)));
|
|
25
|
+
}
|
|
26
|
+
if (results.tvSeries.length > 0) {
|
|
27
|
+
console.log(section("TV Series", results.tvSeries.length));
|
|
28
|
+
results.tvSeries.forEach((r) => console.log(movieLine(r)));
|
|
29
|
+
}
|
|
30
|
+
if (results.creators.length > 0) {
|
|
31
|
+
console.log(section("Creators", results.creators.length));
|
|
32
|
+
results.creators.forEach((r) => console.log(` ${c.dim(String(r.id).padEnd(8))} ${r.name}`));
|
|
33
|
+
}
|
|
34
|
+
if (results.users.length > 0) {
|
|
35
|
+
console.log(section("Users", results.users.length));
|
|
36
|
+
results.users.forEach((r) => console.log(` ${c.dim(String(r.id).padEnd(8))} ${r.user}` + (r.userRealName ? c.dim(` (${r.userRealName})`) : "")));
|
|
37
|
+
}
|
|
38
|
+
if (total === 0) console.log(c.dim(" No results found."));
|
|
39
|
+
console.log("");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
//#endregion
|
|
43
|
+
export { runSearch };
|
package/cli.js
CHANGED
|
@@ -94,11 +94,45 @@ async function main() {
|
|
|
94
94
|
process.exit(1);
|
|
95
95
|
}
|
|
96
96
|
break;
|
|
97
|
+
case "search": {
|
|
98
|
+
const query = args.slice(1).filter((a) => !a.startsWith("--")).join(" ");
|
|
99
|
+
if (!query) {
|
|
100
|
+
console.error(err("Please provide a search query."));
|
|
101
|
+
console.log(c.dim(` Usage: ${getCommandName()} search <query> [--json]`));
|
|
102
|
+
process.exit(1);
|
|
103
|
+
}
|
|
104
|
+
try {
|
|
105
|
+
const { runSearch } = await import("./bin/search.js");
|
|
106
|
+
await runSearch(query, args.includes("--json"));
|
|
107
|
+
} catch (error) {
|
|
108
|
+
console.error(err("Search failed:"), error);
|
|
109
|
+
process.exit(1);
|
|
110
|
+
}
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
97
113
|
case "movie": {
|
|
98
|
-
const
|
|
114
|
+
const input = args.slice(1).filter((a) => !a.startsWith("--")).join(" ");
|
|
115
|
+
if (!input) {
|
|
116
|
+
console.error(err("Please provide a movie ID or title."));
|
|
117
|
+
console.log(c.dim(` Usage: ${getCommandName()} movie <id|title> [--json]`));
|
|
118
|
+
process.exit(1);
|
|
119
|
+
}
|
|
120
|
+
const json = args.includes("--json");
|
|
99
121
|
try {
|
|
100
122
|
const { runMovieLookup } = await import("./bin/lookup-movie.js");
|
|
101
|
-
|
|
123
|
+
const numericId = /^\d+$/.test(input) ? Number(input) : null;
|
|
124
|
+
if (numericId !== null) await runMovieLookup(numericId, json);
|
|
125
|
+
else {
|
|
126
|
+
const { csfd } = await import("./index.js");
|
|
127
|
+
const results = await csfd.search(input);
|
|
128
|
+
const first = results.movies[0] ?? results.tvSeries[0];
|
|
129
|
+
if (!first) {
|
|
130
|
+
console.error(err(`No movies found for "${input}".`));
|
|
131
|
+
process.exit(1);
|
|
132
|
+
}
|
|
133
|
+
console.log(c.dim(` → ${first.title}${first.year ? ` (${first.year})` : ""}`));
|
|
134
|
+
await runMovieLookup(first.id, json);
|
|
135
|
+
}
|
|
102
136
|
} catch (error) {
|
|
103
137
|
console.error(err("Failed to fetch movie:"), error);
|
|
104
138
|
process.exit(1);
|
|
@@ -107,7 +141,7 @@ async function main() {
|
|
|
107
141
|
}
|
|
108
142
|
case "--version":
|
|
109
143
|
case "-v":
|
|
110
|
-
console.log(c.bold("5.6.
|
|
144
|
+
console.log(c.bold("5.6.2"));
|
|
111
145
|
break;
|
|
112
146
|
case "update":
|
|
113
147
|
await runUpdate();
|
|
@@ -200,15 +234,15 @@ async function checkForUpdateInBackground() {
|
|
|
200
234
|
} catch {}
|
|
201
235
|
}
|
|
202
236
|
} catch {}
|
|
203
|
-
if (!latestVersion || compareSemver("5.6.
|
|
237
|
+
if (!latestVersion || compareSemver("5.6.2", latestVersion) >= 0) return;
|
|
204
238
|
console.log("");
|
|
205
239
|
console.log(c.dim(" " + "─".repeat(44)));
|
|
206
|
-
console.log(` ${c.yellow(c.bold("↑ Update available:"))} ${c.dim("5.6.
|
|
240
|
+
console.log(` ${c.yellow(c.bold("↑ Update available:"))} ${c.dim("5.6.2")} → ${c.bold(c.green(latestVersion))}`);
|
|
207
241
|
console.log(` ${c.dim("Run")} ${c.cyan(getCommandName() + " update")} ${c.dim("for upgrade instructions.")}`);
|
|
208
242
|
} catch {}
|
|
209
243
|
}
|
|
210
244
|
async function runUpdate() {
|
|
211
|
-
console.log(c.dim("Current version: ") + c.bold("5.6.
|
|
245
|
+
console.log(c.dim("Current version: ") + c.bold("5.6.2"));
|
|
212
246
|
console.log(c.dim("Checking for updates..."));
|
|
213
247
|
let latest;
|
|
214
248
|
try {
|
|
@@ -221,7 +255,7 @@ async function runUpdate() {
|
|
|
221
255
|
console.error(err("Could not determine latest version."));
|
|
222
256
|
process.exit(1);
|
|
223
257
|
}
|
|
224
|
-
const cmp = compareSemver("5.6.
|
|
258
|
+
const cmp = compareSemver("5.6.2", latest);
|
|
225
259
|
if (cmp === 0) {
|
|
226
260
|
console.log(c.green("✔ Already up to date."));
|
|
227
261
|
return;
|
|
@@ -234,7 +268,7 @@ async function runUpdate() {
|
|
|
234
268
|
}
|
|
235
269
|
function printUsage() {
|
|
236
270
|
const cmd = getCommandName();
|
|
237
|
-
const header = c.bold(c.cyan("csfd")) + " " + c.dim(`v5.6.
|
|
271
|
+
const header = c.bold(c.cyan("csfd")) + " " + c.dim(`v5.6.2`);
|
|
238
272
|
const usage = c.bold("Usage:") + ` ${c.cyan(cmd)} ${c.dim("<command> [options]")}`;
|
|
239
273
|
const section = (title) => c.bold(title);
|
|
240
274
|
const cmd_ = (name) => " " + c.cyan(name);
|
|
@@ -256,7 +290,8 @@ ${sub_("--letterboxd")} ${desc("Letterboxd-compatible CSV")}
|
|
|
256
290
|
${cmd_("export reviews <userId>")} ${desc("Export user reviews")}
|
|
257
291
|
${sub_("--csv")} ${desc("CSV format (default)")}
|
|
258
292
|
${sub_("--json")} ${desc("JSON format")}
|
|
259
|
-
${cmd_("
|
|
293
|
+
${cmd_("search <query>")} ${desc("Search movies, series, creators and users")}
|
|
294
|
+
${cmd_("movie <id|title>")} ${desc("Show movie details (ID or film name)")}
|
|
260
295
|
${sub_("--json")} ${desc("Output raw JSON")}
|
|
261
296
|
${cmd_("update")} ${desc("Check for updates")}
|
|
262
297
|
${cmd_("help")} ${desc("Show this help")}
|
package/package.js
CHANGED