tmdb-kit 1.1.0 → 1.2.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 +243 -79
- package/dist/cli.js +1068 -377
- package/dist/client.d.cts +13 -5
- package/dist/client.d.ts +13 -5
- package/dist/errors.d.cts +13 -1
- package/dist/errors.d.ts +13 -1
- package/dist/helpers/cache.d.cts +28 -0
- package/dist/helpers/cache.d.ts +28 -0
- package/dist/helpers/pagination.d.cts +35 -0
- package/dist/helpers/pagination.d.ts +35 -0
- package/dist/helpers/retry.d.cts +47 -0
- package/dist/helpers/retry.d.ts +47 -0
- package/dist/helpers/timeout.d.cts +20 -0
- package/dist/helpers/timeout.d.ts +20 -0
- package/dist/index.cjs +422 -54
- package/dist/index.cjs.map +18 -14
- package/dist/index.d.cts +7 -2
- package/dist/index.d.ts +7 -2
- package/dist/index.js +422 -54
- package/dist/index.js.map +18 -14
- package/dist/query.d.cts +14 -0
- package/dist/query.d.ts +14 -0
- package/dist/resources/configuration.d.cts +7 -7
- package/dist/resources/configuration.d.ts +7 -7
- package/dist/resources/movies.d.cts +3 -3
- package/dist/resources/movies.d.ts +3 -3
- package/dist/resources/tv.d.cts +9 -1
- package/dist/resources/tv.d.ts +9 -1
- package/dist/types.d.cts +100 -2
- package/dist/types.d.ts +100 -2
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -354,17 +354,6 @@ async function runCommand(cmd, opts) {
|
|
|
354
354
|
throw new Error("Multiple cleanup errors", { cause: cleanupErrors });
|
|
355
355
|
return { result };
|
|
356
356
|
}
|
|
357
|
-
async function resolveSubCommand(cmd, rawArgs, parent) {
|
|
358
|
-
const subCommands = await resolveValue(cmd.subCommands);
|
|
359
|
-
if (subCommands && Object.keys(subCommands).length > 0) {
|
|
360
|
-
const subCommandArgIndex = findSubCommandIndex(rawArgs, await resolveValue(cmd.args || {}));
|
|
361
|
-
const subCommandName = rawArgs[subCommandArgIndex];
|
|
362
|
-
const subCommand = await _findSubCommand(subCommands, subCommandName);
|
|
363
|
-
if (subCommand)
|
|
364
|
-
return resolveSubCommand(subCommand, rawArgs.slice(subCommandArgIndex + 1), cmd);
|
|
365
|
-
}
|
|
366
|
-
return [cmd, parent];
|
|
367
|
-
}
|
|
368
357
|
async function _findSubCommand(subCommands, name) {
|
|
369
358
|
if (name in subCommands)
|
|
370
359
|
return resolveValue(subCommands[name]);
|
|
@@ -404,14 +393,6 @@ function _isValueFlag(flag, argsDef) {
|
|
|
404
393
|
}
|
|
405
394
|
return false;
|
|
406
395
|
}
|
|
407
|
-
async function showUsage(cmd, parent) {
|
|
408
|
-
try {
|
|
409
|
-
console.log(await renderUsage(cmd, parent) + `
|
|
410
|
-
`);
|
|
411
|
-
} catch (error) {
|
|
412
|
-
console.error(error);
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
396
|
var negativePrefixRe = /^no[-A-Z]/;
|
|
416
397
|
async function renderUsage(cmd, parent) {
|
|
417
398
|
const cmdMeta = await resolveValue(cmd.meta || {});
|
|
@@ -494,57 +475,11 @@ function renderDescription(arg, required) {
|
|
|
494
475
|
defaultHint
|
|
495
476
|
].filter(Boolean).join(" ");
|
|
496
477
|
}
|
|
497
|
-
async function runMain(cmd, opts = {}) {
|
|
498
|
-
const rawArgs = opts.rawArgs || process.argv.slice(2);
|
|
499
|
-
const showUsage$1 = opts.showUsage || showUsage;
|
|
500
|
-
try {
|
|
501
|
-
const builtinFlags = await _resolveBuiltinFlags(cmd);
|
|
502
|
-
if (builtinFlags.help.length > 0 && rawArgs.some((arg) => builtinFlags.help.includes(arg))) {
|
|
503
|
-
await showUsage$1(...await resolveSubCommand(cmd, rawArgs));
|
|
504
|
-
process.exit(0);
|
|
505
|
-
} else if (rawArgs.length === 1 && builtinFlags.version.includes(rawArgs[0])) {
|
|
506
|
-
const meta = typeof cmd.meta === "function" ? await cmd.meta() : await cmd.meta;
|
|
507
|
-
if (!meta?.version)
|
|
508
|
-
throw new CLIError("No version specified", "E_NO_VERSION");
|
|
509
|
-
console.log(meta.version);
|
|
510
|
-
} else
|
|
511
|
-
await runCommand(cmd, { rawArgs });
|
|
512
|
-
} catch (error) {
|
|
513
|
-
if (error instanceof CLIError) {
|
|
514
|
-
await showUsage$1(...await resolveSubCommand(cmd, rawArgs));
|
|
515
|
-
console.error(error.message);
|
|
516
|
-
} else
|
|
517
|
-
console.error(error, `
|
|
518
|
-
`);
|
|
519
|
-
process.exit(1);
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
async function _resolveBuiltinFlags(cmd) {
|
|
523
|
-
const argsDef = await resolveValue(cmd.args || {});
|
|
524
|
-
const userNames = /* @__PURE__ */ new Set;
|
|
525
|
-
const userAliases = /* @__PURE__ */ new Set;
|
|
526
|
-
for (const [name, def] of Object.entries(argsDef)) {
|
|
527
|
-
userNames.add(name);
|
|
528
|
-
for (const alias of toArray(def.alias))
|
|
529
|
-
userAliases.add(alias);
|
|
530
|
-
}
|
|
531
|
-
return {
|
|
532
|
-
help: _getBuiltinFlags("help", "h", userNames, userAliases),
|
|
533
|
-
version: _getBuiltinFlags("version", "v", userNames, userAliases)
|
|
534
|
-
};
|
|
535
|
-
}
|
|
536
|
-
function _getBuiltinFlags(long, short, userNames, userAliases) {
|
|
537
|
-
if (userNames.has(long) || userAliases.has(long))
|
|
538
|
-
return [];
|
|
539
|
-
if (userNames.has(short) || userAliases.has(short))
|
|
540
|
-
return [`--${long}`];
|
|
541
|
-
return [`--${long}`, `-${short}`];
|
|
542
|
-
}
|
|
543
478
|
// package.json
|
|
544
479
|
var package_default = {
|
|
545
480
|
name: "tmdb-kit",
|
|
546
481
|
type: "module",
|
|
547
|
-
version: "1.
|
|
482
|
+
version: "1.2.0",
|
|
548
483
|
description: "A tiny, typed, zero-runtime-dependency TMDB SDK and CLI for Node.js, Bun, Deno, and edge runtimes.",
|
|
549
484
|
author: "isaced",
|
|
550
485
|
license: "MIT",
|
|
@@ -648,6 +583,15 @@ class TMDBRequestError extends TMDBError {
|
|
|
648
583
|
}
|
|
649
584
|
}
|
|
650
585
|
|
|
586
|
+
class TMDBTimeoutError extends TMDBError {
|
|
587
|
+
timeoutMs;
|
|
588
|
+
constructor(timeoutMs, message = `TMDB request timed out after ${timeoutMs}ms`) {
|
|
589
|
+
super(message);
|
|
590
|
+
this.name = "TMDBTimeoutError";
|
|
591
|
+
this.timeoutMs = timeoutMs;
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
|
|
651
595
|
class TMDBResponseError extends TMDBError {
|
|
652
596
|
body;
|
|
653
597
|
headers;
|
|
@@ -770,6 +714,19 @@ function toId(value, name) {
|
|
|
770
714
|
}
|
|
771
715
|
return String(value);
|
|
772
716
|
}
|
|
717
|
+
function toSeasonNumber(value) {
|
|
718
|
+
if (!Number.isInteger(value) || value < 0) {
|
|
719
|
+
throw new TMDBRequestError("seasonNumber must be a non-negative integer");
|
|
720
|
+
}
|
|
721
|
+
return String(value);
|
|
722
|
+
}
|
|
723
|
+
function toGroupId(value) {
|
|
724
|
+
const groupId = value.trim();
|
|
725
|
+
if (!/^[A-Za-z0-9_-]+$/.test(groupId)) {
|
|
726
|
+
throw new TMDBRequestError("groupId must be a non-empty identifier");
|
|
727
|
+
}
|
|
728
|
+
return groupId;
|
|
729
|
+
}
|
|
773
730
|
function toSearchQuery(value) {
|
|
774
731
|
const query = value.trim();
|
|
775
732
|
if (query.length === 0) {
|
|
@@ -903,23 +860,25 @@ class ConfigurationResource {
|
|
|
903
860
|
constructor(transport) {
|
|
904
861
|
this.#transport = transport;
|
|
905
862
|
}
|
|
906
|
-
details() {
|
|
907
|
-
return this.#transport.get("/configuration");
|
|
863
|
+
details(options = {}) {
|
|
864
|
+
return this.#transport.get("/configuration", { signal: options.signal });
|
|
908
865
|
}
|
|
909
|
-
countries() {
|
|
910
|
-
return this.#transport.get("/configuration/countries");
|
|
866
|
+
countries(options = {}) {
|
|
867
|
+
return this.#transport.get("/configuration/countries", { signal: options.signal });
|
|
911
868
|
}
|
|
912
|
-
jobs() {
|
|
913
|
-
return this.#transport.get("/configuration/jobs");
|
|
869
|
+
jobs(options = {}) {
|
|
870
|
+
return this.#transport.get("/configuration/jobs", { signal: options.signal });
|
|
914
871
|
}
|
|
915
|
-
languages() {
|
|
916
|
-
return this.#transport.get("/configuration/languages");
|
|
872
|
+
languages(options = {}) {
|
|
873
|
+
return this.#transport.get("/configuration/languages", { signal: options.signal });
|
|
917
874
|
}
|
|
918
|
-
primaryTranslations() {
|
|
919
|
-
return this.#transport.get("/configuration/primary_translations"
|
|
875
|
+
primaryTranslations(options = {}) {
|
|
876
|
+
return this.#transport.get("/configuration/primary_translations", {
|
|
877
|
+
signal: options.signal
|
|
878
|
+
});
|
|
920
879
|
}
|
|
921
|
-
timezones() {
|
|
922
|
-
return this.#transport.get("/configuration/timezones");
|
|
880
|
+
timezones(options = {}) {
|
|
881
|
+
return this.#transport.get("/configuration/timezones", { signal: options.signal });
|
|
923
882
|
}
|
|
924
883
|
}
|
|
925
884
|
|
|
@@ -933,14 +892,16 @@ class GenresResource {
|
|
|
933
892
|
return this.#transport.get("/genre/movie/list", {
|
|
934
893
|
query: withLanguage(this.#transport.defaults, {
|
|
935
894
|
language: options.language
|
|
936
|
-
})
|
|
895
|
+
}),
|
|
896
|
+
signal: options.signal
|
|
937
897
|
});
|
|
938
898
|
}
|
|
939
899
|
tv(options = {}) {
|
|
940
900
|
return this.#transport.get("/genre/tv/list", {
|
|
941
901
|
query: withLanguage(this.#transport.defaults, {
|
|
942
902
|
language: options.language
|
|
943
|
-
})
|
|
903
|
+
}),
|
|
904
|
+
signal: options.signal
|
|
944
905
|
});
|
|
945
906
|
}
|
|
946
907
|
}
|
|
@@ -969,22 +930,26 @@ class MoviesResource {
|
|
|
969
930
|
}
|
|
970
931
|
popular(options = {}) {
|
|
971
932
|
return this.#transport.get("/movie/popular", {
|
|
972
|
-
query: movieListQuery(this.#transport, options)
|
|
933
|
+
query: movieListQuery(this.#transport, options),
|
|
934
|
+
signal: options.signal
|
|
973
935
|
});
|
|
974
936
|
}
|
|
975
937
|
topRated(options = {}) {
|
|
976
938
|
return this.#transport.get("/movie/top_rated", {
|
|
977
|
-
query: movieListQuery(this.#transport, options)
|
|
939
|
+
query: movieListQuery(this.#transport, options),
|
|
940
|
+
signal: options.signal
|
|
978
941
|
});
|
|
979
942
|
}
|
|
980
943
|
nowPlaying(options = {}) {
|
|
981
944
|
return this.#transport.get("/movie/now_playing", {
|
|
982
|
-
query: movieListQuery(this.#transport, options)
|
|
945
|
+
query: movieListQuery(this.#transport, options),
|
|
946
|
+
signal: options.signal
|
|
983
947
|
});
|
|
984
948
|
}
|
|
985
949
|
upcoming(options = {}) {
|
|
986
950
|
return this.#transport.get("/movie/upcoming", {
|
|
987
|
-
query: movieListQuery(this.#transport, options)
|
|
951
|
+
query: movieListQuery(this.#transport, options),
|
|
952
|
+
signal: options.signal
|
|
988
953
|
});
|
|
989
954
|
}
|
|
990
955
|
details(movieId, options = {}) {
|
|
@@ -992,14 +957,16 @@ class MoviesResource {
|
|
|
992
957
|
query: withLanguage(this.#transport.defaults, {
|
|
993
958
|
append_to_response: toCommaList(options.appendToResponse),
|
|
994
959
|
language: options.language
|
|
995
|
-
})
|
|
960
|
+
}),
|
|
961
|
+
signal: options.signal
|
|
996
962
|
});
|
|
997
963
|
}
|
|
998
964
|
credits(movieId, options = {}) {
|
|
999
965
|
return this.#transport.get(`/movie/${toId(movieId, "movieId")}/credits`, {
|
|
1000
966
|
query: withLanguage(this.#transport.defaults, {
|
|
1001
967
|
language: options.language
|
|
1002
|
-
})
|
|
968
|
+
}),
|
|
969
|
+
signal: options.signal
|
|
1003
970
|
});
|
|
1004
971
|
}
|
|
1005
972
|
images(movieId, options = {}) {
|
|
@@ -1007,24 +974,28 @@ class MoviesResource {
|
|
|
1007
974
|
query: withLanguage(this.#transport.defaults, {
|
|
1008
975
|
include_image_language: toCommaList(options.includeImageLanguage),
|
|
1009
976
|
language: options.language
|
|
1010
|
-
})
|
|
977
|
+
}),
|
|
978
|
+
signal: options.signal
|
|
1011
979
|
});
|
|
1012
980
|
}
|
|
1013
981
|
recommendations(movieId, options = {}) {
|
|
1014
982
|
return this.#transport.get(`/movie/${toId(movieId, "movieId")}/recommendations`, {
|
|
1015
|
-
query: movieListQuery(this.#transport, options)
|
|
983
|
+
query: movieListQuery(this.#transport, options),
|
|
984
|
+
signal: options.signal
|
|
1016
985
|
});
|
|
1017
986
|
}
|
|
1018
987
|
similar(movieId, options = {}) {
|
|
1019
988
|
return this.#transport.get(`/movie/${toId(movieId, "movieId")}/similar`, {
|
|
1020
|
-
query: movieListQuery(this.#transport, options)
|
|
989
|
+
query: movieListQuery(this.#transport, options),
|
|
990
|
+
signal: options.signal
|
|
1021
991
|
});
|
|
1022
992
|
}
|
|
1023
993
|
videos(movieId, options = {}) {
|
|
1024
994
|
return this.#transport.get(`/movie/${toId(movieId, "movieId")}/videos`, {
|
|
1025
995
|
query: withLanguage(this.#transport.defaults, {
|
|
1026
996
|
language: options.language
|
|
1027
|
-
})
|
|
997
|
+
}),
|
|
998
|
+
signal: options.signal
|
|
1028
999
|
});
|
|
1029
1000
|
}
|
|
1030
1001
|
}
|
|
@@ -1044,21 +1015,24 @@ class PeopleResource {
|
|
|
1044
1015
|
}
|
|
1045
1016
|
popular(options = {}) {
|
|
1046
1017
|
return this.#transport.get("/person/popular", {
|
|
1047
|
-
query: peopleListQuery(this.#transport, options)
|
|
1018
|
+
query: peopleListQuery(this.#transport, options),
|
|
1019
|
+
signal: options.signal
|
|
1048
1020
|
});
|
|
1049
1021
|
}
|
|
1050
1022
|
details(personId, options = {}) {
|
|
1051
1023
|
return this.#transport.get(`/person/${toId(personId, "personId")}`, {
|
|
1052
1024
|
query: withLanguage(this.#transport.defaults, {
|
|
1053
1025
|
language: options.language
|
|
1054
|
-
})
|
|
1026
|
+
}),
|
|
1027
|
+
signal: options.signal
|
|
1055
1028
|
});
|
|
1056
1029
|
}
|
|
1057
1030
|
combinedCredits(personId, options = {}) {
|
|
1058
1031
|
return this.#transport.get(`/person/${toId(personId, "personId")}/combined_credits`, {
|
|
1059
1032
|
query: withLanguage(this.#transport.defaults, {
|
|
1060
1033
|
language: options.language
|
|
1061
|
-
})
|
|
1034
|
+
}),
|
|
1035
|
+
signal: options.signal
|
|
1062
1036
|
});
|
|
1063
1037
|
}
|
|
1064
1038
|
}
|
|
@@ -1077,22 +1051,26 @@ class SearchResource {
|
|
|
1077
1051
|
}
|
|
1078
1052
|
movies(query, options = {}) {
|
|
1079
1053
|
return this.#transport.get("/search/movie", {
|
|
1080
|
-
query: movieSearchQuery(this.#transport, query, options)
|
|
1054
|
+
query: movieSearchQuery(this.#transport, query, options),
|
|
1055
|
+
signal: options.signal
|
|
1081
1056
|
});
|
|
1082
1057
|
}
|
|
1083
1058
|
tv(query, options = {}) {
|
|
1084
1059
|
return this.#transport.get("/search/tv", {
|
|
1085
|
-
query: tvSearchQuery(this.#transport, query, options)
|
|
1060
|
+
query: tvSearchQuery(this.#transport, query, options),
|
|
1061
|
+
signal: options.signal
|
|
1086
1062
|
});
|
|
1087
1063
|
}
|
|
1088
1064
|
people(query, options = {}) {
|
|
1089
1065
|
return this.#transport.get("/search/person", {
|
|
1090
|
-
query: commonSearchQuery(this.#transport, query, options)
|
|
1066
|
+
query: commonSearchQuery(this.#transport, query, options),
|
|
1067
|
+
signal: options.signal
|
|
1091
1068
|
});
|
|
1092
1069
|
}
|
|
1093
1070
|
async multi(query, options = {}) {
|
|
1094
1071
|
const response = await this.#transport.get("/search/multi", {
|
|
1095
|
-
query: commonSearchQuery(this.#transport, query, options)
|
|
1072
|
+
query: commonSearchQuery(this.#transport, query, options),
|
|
1073
|
+
signal: options.signal
|
|
1096
1074
|
});
|
|
1097
1075
|
const excluded = options.exclude;
|
|
1098
1076
|
if (excluded === undefined || excluded.length === 0) {
|
|
@@ -1146,7 +1124,8 @@ class TrendingResource {
|
|
|
1146
1124
|
query: withLanguage(this.#transport.defaults, {
|
|
1147
1125
|
language: options.language,
|
|
1148
1126
|
page: options.page
|
|
1149
|
-
})
|
|
1127
|
+
}),
|
|
1128
|
+
signal: options.signal
|
|
1150
1129
|
});
|
|
1151
1130
|
}
|
|
1152
1131
|
movies(timeWindow = "day", options = {}) {
|
|
@@ -1154,7 +1133,8 @@ class TrendingResource {
|
|
|
1154
1133
|
query: withLanguage(this.#transport.defaults, {
|
|
1155
1134
|
language: options.language,
|
|
1156
1135
|
page: options.page
|
|
1157
|
-
})
|
|
1136
|
+
}),
|
|
1137
|
+
signal: options.signal
|
|
1158
1138
|
});
|
|
1159
1139
|
}
|
|
1160
1140
|
tv(timeWindow = "day", options = {}) {
|
|
@@ -1162,7 +1142,8 @@ class TrendingResource {
|
|
|
1162
1142
|
query: withLanguage(this.#transport.defaults, {
|
|
1163
1143
|
language: options.language,
|
|
1164
1144
|
page: options.page
|
|
1165
|
-
})
|
|
1145
|
+
}),
|
|
1146
|
+
signal: options.signal
|
|
1166
1147
|
});
|
|
1167
1148
|
}
|
|
1168
1149
|
people(timeWindow = "day", options = {}) {
|
|
@@ -1170,7 +1151,8 @@ class TrendingResource {
|
|
|
1170
1151
|
query: withLanguage(this.#transport.defaults, {
|
|
1171
1152
|
language: options.language,
|
|
1172
1153
|
page: options.page
|
|
1173
|
-
})
|
|
1154
|
+
}),
|
|
1155
|
+
signal: options.signal
|
|
1174
1156
|
});
|
|
1175
1157
|
}
|
|
1176
1158
|
}
|
|
@@ -1183,22 +1165,26 @@ class TVResource {
|
|
|
1183
1165
|
}
|
|
1184
1166
|
popular(options = {}) {
|
|
1185
1167
|
return this.#transport.get("/tv/popular", {
|
|
1186
|
-
query: tvListQuery(this.#transport, options)
|
|
1168
|
+
query: tvListQuery(this.#transport, options),
|
|
1169
|
+
signal: options.signal
|
|
1187
1170
|
});
|
|
1188
1171
|
}
|
|
1189
1172
|
topRated(options = {}) {
|
|
1190
1173
|
return this.#transport.get("/tv/top_rated", {
|
|
1191
|
-
query: tvListQuery(this.#transport, options)
|
|
1174
|
+
query: tvListQuery(this.#transport, options),
|
|
1175
|
+
signal: options.signal
|
|
1192
1176
|
});
|
|
1193
1177
|
}
|
|
1194
1178
|
airingToday(options = {}) {
|
|
1195
1179
|
return this.#transport.get("/tv/airing_today", {
|
|
1196
|
-
query: tvListQuery(this.#transport, options)
|
|
1180
|
+
query: tvListQuery(this.#transport, options),
|
|
1181
|
+
signal: options.signal
|
|
1197
1182
|
});
|
|
1198
1183
|
}
|
|
1199
1184
|
onTheAir(options = {}) {
|
|
1200
1185
|
return this.#transport.get("/tv/on_the_air", {
|
|
1201
|
-
query: tvListQuery(this.#transport, options)
|
|
1186
|
+
query: tvListQuery(this.#transport, options),
|
|
1187
|
+
signal: options.signal
|
|
1202
1188
|
});
|
|
1203
1189
|
}
|
|
1204
1190
|
details(seriesId, options = {}) {
|
|
@@ -1206,14 +1192,16 @@ class TVResource {
|
|
|
1206
1192
|
query: withLanguage(this.#transport.defaults, {
|
|
1207
1193
|
append_to_response: toCommaList(options.appendToResponse),
|
|
1208
1194
|
language: options.language
|
|
1209
|
-
})
|
|
1195
|
+
}),
|
|
1196
|
+
signal: options.signal
|
|
1210
1197
|
});
|
|
1211
1198
|
}
|
|
1212
1199
|
credits(seriesId, options = {}) {
|
|
1213
1200
|
return this.#transport.get(`/tv/${toId(seriesId, "seriesId")}/credits`, {
|
|
1214
1201
|
query: withLanguage(this.#transport.defaults, {
|
|
1215
1202
|
language: options.language
|
|
1216
|
-
})
|
|
1203
|
+
}),
|
|
1204
|
+
signal: options.signal
|
|
1217
1205
|
});
|
|
1218
1206
|
}
|
|
1219
1207
|
images(seriesId, options = {}) {
|
|
@@ -1221,32 +1209,103 @@ class TVResource {
|
|
|
1221
1209
|
query: withLanguage(this.#transport.defaults, {
|
|
1222
1210
|
include_image_language: toCommaList(options.includeImageLanguage),
|
|
1223
1211
|
language: options.language
|
|
1224
|
-
})
|
|
1212
|
+
}),
|
|
1213
|
+
signal: options.signal
|
|
1225
1214
|
});
|
|
1226
1215
|
}
|
|
1227
1216
|
recommendations(seriesId, options = {}) {
|
|
1228
1217
|
return this.#transport.get(`/tv/${toId(seriesId, "seriesId")}/recommendations`, {
|
|
1229
|
-
query: tvListQuery(this.#transport, options)
|
|
1218
|
+
query: tvListQuery(this.#transport, options),
|
|
1219
|
+
signal: options.signal
|
|
1230
1220
|
});
|
|
1231
1221
|
}
|
|
1232
1222
|
similar(seriesId, options = {}) {
|
|
1233
1223
|
return this.#transport.get(`/tv/${toId(seriesId, "seriesId")}/similar`, {
|
|
1234
|
-
query: tvListQuery(this.#transport, options)
|
|
1224
|
+
query: tvListQuery(this.#transport, options),
|
|
1225
|
+
signal: options.signal
|
|
1235
1226
|
});
|
|
1236
1227
|
}
|
|
1237
1228
|
videos(seriesId, options = {}) {
|
|
1238
1229
|
return this.#transport.get(`/tv/${toId(seriesId, "seriesId")}/videos`, {
|
|
1239
1230
|
query: withLanguage(this.#transport.defaults, {
|
|
1240
1231
|
language: options.language
|
|
1241
|
-
})
|
|
1232
|
+
}),
|
|
1233
|
+
signal: options.signal
|
|
1242
1234
|
});
|
|
1243
1235
|
}
|
|
1244
1236
|
seasonDetails(seriesId, seasonNumber, options = {}) {
|
|
1245
|
-
return this.#transport.get(`/tv/${toId(seriesId, "seriesId")}/season/${
|
|
1237
|
+
return this.#transport.get(`/tv/${toId(seriesId, "seriesId")}/season/${toSeasonNumber(seasonNumber)}`, {
|
|
1238
|
+
query: withLanguage(this.#transport.defaults, {
|
|
1239
|
+
append_to_response: toCommaList(options.appendToResponse),
|
|
1240
|
+
language: options.language
|
|
1241
|
+
}),
|
|
1242
|
+
signal: options.signal
|
|
1243
|
+
});
|
|
1244
|
+
}
|
|
1245
|
+
seasonCredits(seriesId, seasonNumber, options = {}) {
|
|
1246
|
+
return this.#transport.get(`/tv/${toId(seriesId, "seriesId")}/season/${toSeasonNumber(seasonNumber)}/credits`, {
|
|
1247
|
+
query: withLanguage(this.#transport.defaults, {
|
|
1248
|
+
language: options.language
|
|
1249
|
+
}),
|
|
1250
|
+
signal: options.signal
|
|
1251
|
+
});
|
|
1252
|
+
}
|
|
1253
|
+
seasonAggregateCredits(seriesId, seasonNumber, options = {}) {
|
|
1254
|
+
return this.#transport.get(`/tv/${toId(seriesId, "seriesId")}/season/${toSeasonNumber(seasonNumber)}/aggregate_credits`, {
|
|
1255
|
+
query: withLanguage(this.#transport.defaults, {
|
|
1256
|
+
language: options.language
|
|
1257
|
+
}),
|
|
1258
|
+
signal: options.signal
|
|
1259
|
+
});
|
|
1260
|
+
}
|
|
1261
|
+
seasonImages(seriesId, seasonNumber, options = {}) {
|
|
1262
|
+
return this.#transport.get(`/tv/${toId(seriesId, "seriesId")}/season/${toSeasonNumber(seasonNumber)}/images`, {
|
|
1263
|
+
query: withLanguage(this.#transport.defaults, {
|
|
1264
|
+
include_image_language: toCommaList(options.includeImageLanguage),
|
|
1265
|
+
language: options.language
|
|
1266
|
+
}),
|
|
1267
|
+
signal: options.signal
|
|
1268
|
+
});
|
|
1269
|
+
}
|
|
1270
|
+
seasonVideos(seriesId, seasonNumber, options = {}) {
|
|
1271
|
+
return this.#transport.get(`/tv/${toId(seriesId, "seriesId")}/season/${toSeasonNumber(seasonNumber)}/videos`, {
|
|
1272
|
+
query: withLanguage(this.#transport.defaults, {
|
|
1273
|
+
language: options.language
|
|
1274
|
+
}),
|
|
1275
|
+
signal: options.signal
|
|
1276
|
+
});
|
|
1277
|
+
}
|
|
1278
|
+
episodeDetails(seriesId, seasonNumber, episodeNumber, options = {}) {
|
|
1279
|
+
return this.#transport.get(`/tv/${toId(seriesId, "seriesId")}/season/${toSeasonNumber(seasonNumber)}/episode/${toId(episodeNumber, "episodeNumber")}`, {
|
|
1246
1280
|
query: withLanguage(this.#transport.defaults, {
|
|
1247
1281
|
append_to_response: toCommaList(options.appendToResponse),
|
|
1248
1282
|
language: options.language
|
|
1249
|
-
})
|
|
1283
|
+
}),
|
|
1284
|
+
signal: options.signal
|
|
1285
|
+
});
|
|
1286
|
+
}
|
|
1287
|
+
aggregateCredits(seriesId, options = {}) {
|
|
1288
|
+
return this.#transport.get(`/tv/${toId(seriesId, "seriesId")}/aggregate_credits`, {
|
|
1289
|
+
query: withLanguage(this.#transport.defaults, {
|
|
1290
|
+
language: options.language
|
|
1291
|
+
}),
|
|
1292
|
+
signal: options.signal
|
|
1293
|
+
});
|
|
1294
|
+
}
|
|
1295
|
+
episodeGroups(seriesId, options = {}) {
|
|
1296
|
+
return this.#transport.get(`/tv/${toId(seriesId, "seriesId")}/episode_groups`, {
|
|
1297
|
+
query: withLanguage(this.#transport.defaults, {
|
|
1298
|
+
language: options.language
|
|
1299
|
+
}),
|
|
1300
|
+
signal: options.signal
|
|
1301
|
+
});
|
|
1302
|
+
}
|
|
1303
|
+
episodeGroup(groupId, options = {}) {
|
|
1304
|
+
return this.#transport.get(`/tv/episode_group/${toGroupId(groupId)}`, {
|
|
1305
|
+
query: withLanguage(this.#transport.defaults, {
|
|
1306
|
+
language: options.language
|
|
1307
|
+
}),
|
|
1308
|
+
signal: options.signal
|
|
1250
1309
|
});
|
|
1251
1310
|
}
|
|
1252
1311
|
}
|
|
@@ -1269,13 +1328,17 @@ class TMDBClient {
|
|
|
1269
1328
|
tv;
|
|
1270
1329
|
#transport;
|
|
1271
1330
|
constructor(options) {
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1331
|
+
let imageTransform = options.images?.transform;
|
|
1332
|
+
if (options.transport === undefined) {
|
|
1333
|
+
const builtInClient = new TMDBHttpClient(options);
|
|
1334
|
+
this.#transport = builtInClient;
|
|
1335
|
+
imageTransform = builtInClient.imageTransform;
|
|
1336
|
+
} else {
|
|
1337
|
+
this.#transport = options.transport;
|
|
1338
|
+
}
|
|
1276
1339
|
this.configuration = new ConfigurationResource(this.#transport);
|
|
1277
1340
|
this.genres = new GenresResource(this.#transport);
|
|
1278
|
-
this.images = new ImagesHelper(this.#transport,
|
|
1341
|
+
this.images = new ImagesHelper(this.#transport, imageTransform);
|
|
1279
1342
|
this.movies = new MoviesResource(this.#transport);
|
|
1280
1343
|
this.people = new PeopleResource(this.#transport);
|
|
1281
1344
|
this.search = new SearchResource(this.#transport);
|
|
@@ -1353,14 +1416,50 @@ function errorMessage(error) {
|
|
|
1353
1416
|
}
|
|
1354
1417
|
return String(error);
|
|
1355
1418
|
}
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1419
|
+
var EXIT_CODE_KINDS = {
|
|
1420
|
+
2: "usage",
|
|
1421
|
+
3: "auth",
|
|
1422
|
+
4: "not_found",
|
|
1423
|
+
5: "rate_limit"
|
|
1424
|
+
};
|
|
1425
|
+
function errorKind(error) {
|
|
1426
|
+
if (error instanceof CliError) {
|
|
1427
|
+
return EXIT_CODE_KINDS[error.exitCode] ?? "internal";
|
|
1428
|
+
}
|
|
1429
|
+
if (error instanceof TMDBRateLimitError) {
|
|
1430
|
+
return "rate_limit";
|
|
1431
|
+
}
|
|
1432
|
+
if (error instanceof TMDBResponseError) {
|
|
1433
|
+
if (error.status === 401 || error.status === 403)
|
|
1434
|
+
return "auth";
|
|
1435
|
+
if (error.status === 404)
|
|
1436
|
+
return "not_found";
|
|
1437
|
+
if (error.status === 429)
|
|
1438
|
+
return "rate_limit";
|
|
1439
|
+
return "api";
|
|
1440
|
+
}
|
|
1441
|
+
if (error instanceof TMDBRequestError || error instanceof TMDBTimeoutError) {
|
|
1442
|
+
return "network";
|
|
1443
|
+
}
|
|
1444
|
+
return "internal";
|
|
1445
|
+
}
|
|
1446
|
+
function toErrorRecord(error) {
|
|
1447
|
+
const record = {
|
|
1448
|
+
type: "error",
|
|
1449
|
+
kind: errorKind(error),
|
|
1450
|
+
message: errorMessage(error),
|
|
1451
|
+
exitCode: exitCodeFor(error)
|
|
1452
|
+
};
|
|
1453
|
+
if (error instanceof TMDBResponseError) {
|
|
1454
|
+
record.status = error.status;
|
|
1455
|
+
if (error.requestId !== null) {
|
|
1456
|
+
record.requestId = error.requestId;
|
|
1457
|
+
}
|
|
1458
|
+
}
|
|
1459
|
+
if (error instanceof TMDBRateLimitError) {
|
|
1460
|
+
record.retryAfter = error.retryAfter;
|
|
1363
1461
|
}
|
|
1462
|
+
return record;
|
|
1364
1463
|
}
|
|
1365
1464
|
|
|
1366
1465
|
// src/cli/config.ts
|
|
@@ -1581,9 +1680,135 @@ function missingCredentialsMessage() {
|
|
|
1581
1680
|
`);
|
|
1582
1681
|
}
|
|
1583
1682
|
|
|
1683
|
+
// src/cli/options.ts
|
|
1684
|
+
var jsonArg = {
|
|
1685
|
+
json: {
|
|
1686
|
+
type: "boolean",
|
|
1687
|
+
description: "Alias for --format json"
|
|
1688
|
+
}
|
|
1689
|
+
};
|
|
1690
|
+
var formatArg = {
|
|
1691
|
+
format: {
|
|
1692
|
+
type: "string",
|
|
1693
|
+
description: "Output format: text, json, or ndjson (default: text)"
|
|
1694
|
+
},
|
|
1695
|
+
compact: {
|
|
1696
|
+
type: "boolean",
|
|
1697
|
+
description: "Print JSON on a single line"
|
|
1698
|
+
},
|
|
1699
|
+
fields: {
|
|
1700
|
+
type: "string",
|
|
1701
|
+
description: "Comma-separated top-level fields to keep in JSON output"
|
|
1702
|
+
}
|
|
1703
|
+
};
|
|
1704
|
+
var outputArgs = {
|
|
1705
|
+
...jsonArg,
|
|
1706
|
+
...formatArg
|
|
1707
|
+
};
|
|
1708
|
+
var paginationArgs = {
|
|
1709
|
+
all: {
|
|
1710
|
+
type: "boolean",
|
|
1711
|
+
description: "Fetch every page instead of one (requires --format ndjson)"
|
|
1712
|
+
},
|
|
1713
|
+
"max-items": {
|
|
1714
|
+
type: "string",
|
|
1715
|
+
description: "Stop after this many items (requires --format ndjson)"
|
|
1716
|
+
}
|
|
1717
|
+
};
|
|
1718
|
+
var commonArgs = {
|
|
1719
|
+
...outputArgs,
|
|
1720
|
+
lang: {
|
|
1721
|
+
type: "string",
|
|
1722
|
+
description: "TMDB language code (default: en-US)"
|
|
1723
|
+
}
|
|
1724
|
+
};
|
|
1725
|
+
var SEARCH_TYPES = ["all", "movie", "tv", "person"];
|
|
1726
|
+
function parseSearchType(value) {
|
|
1727
|
+
if (!SEARCH_TYPES.includes(value)) {
|
|
1728
|
+
throw new CliError(`Invalid --type "${value}". Expected one of: ${SEARCH_TYPES.join(", ")}.`, 2);
|
|
1729
|
+
}
|
|
1730
|
+
return value;
|
|
1731
|
+
}
|
|
1732
|
+
function parsePage(value) {
|
|
1733
|
+
if (value === undefined || value === "") {
|
|
1734
|
+
return 1;
|
|
1735
|
+
}
|
|
1736
|
+
const page = Number(value);
|
|
1737
|
+
if (!Number.isInteger(page) || page < 1) {
|
|
1738
|
+
throw new CliError("Invalid --page value: expected a positive integer.", 2);
|
|
1739
|
+
}
|
|
1740
|
+
return page;
|
|
1741
|
+
}
|
|
1742
|
+
function parseId(value, label) {
|
|
1743
|
+
const id = Number(value);
|
|
1744
|
+
if (!Number.isInteger(id) || id <= 0) {
|
|
1745
|
+
throw new CliError(`Invalid ${label}: expected a positive integer.`, 2);
|
|
1746
|
+
}
|
|
1747
|
+
return id;
|
|
1748
|
+
}
|
|
1749
|
+
function parseSeasonNumber(value) {
|
|
1750
|
+
const season = Number(value);
|
|
1751
|
+
if (!Number.isInteger(season) || season < 0) {
|
|
1752
|
+
throw new CliError("Invalid season number: expected a non-negative integer.", 2);
|
|
1753
|
+
}
|
|
1754
|
+
return season;
|
|
1755
|
+
}
|
|
1756
|
+
function parseGroupId(value) {
|
|
1757
|
+
const groupId = value.trim();
|
|
1758
|
+
if (!/^[A-Za-z0-9_-]+$/.test(groupId)) {
|
|
1759
|
+
throw new CliError("Invalid group ID: expected a non-empty identifier.", 2);
|
|
1760
|
+
}
|
|
1761
|
+
return groupId;
|
|
1762
|
+
}
|
|
1763
|
+
function parseLimit(value, fallback = 10) {
|
|
1764
|
+
if (value === undefined || value === "") {
|
|
1765
|
+
return fallback;
|
|
1766
|
+
}
|
|
1767
|
+
const limit = Number(value);
|
|
1768
|
+
if (!Number.isInteger(limit) || limit < 1) {
|
|
1769
|
+
throw new CliError("Invalid --limit value: expected a positive integer.", 2);
|
|
1770
|
+
}
|
|
1771
|
+
return limit;
|
|
1772
|
+
}
|
|
1773
|
+
function parseCommaList(value) {
|
|
1774
|
+
if (value === undefined) {
|
|
1775
|
+
return;
|
|
1776
|
+
}
|
|
1777
|
+
const values = value.split(",").map((item) => item.trim()).filter(Boolean);
|
|
1778
|
+
return values.length > 0 ? values : undefined;
|
|
1779
|
+
}
|
|
1780
|
+
var TRENDING_WINDOWS = ["day", "week"];
|
|
1781
|
+
function parseWindow(value) {
|
|
1782
|
+
if (!TRENDING_WINDOWS.includes(value)) {
|
|
1783
|
+
throw new CliError(`Invalid --window "${value}". Expected one of: ${TRENDING_WINDOWS.join(", ")}.`, 2);
|
|
1784
|
+
}
|
|
1785
|
+
return value;
|
|
1786
|
+
}
|
|
1787
|
+
var GENRE_TYPES = ["movie", "tv"];
|
|
1788
|
+
function parseGenreType(value) {
|
|
1789
|
+
if (!GENRE_TYPES.includes(value)) {
|
|
1790
|
+
throw new CliError(`Invalid --type "${value}". Expected one of: ${GENRE_TYPES.join(", ")}.`, 2);
|
|
1791
|
+
}
|
|
1792
|
+
return value;
|
|
1793
|
+
}
|
|
1794
|
+
var MOVIE_LIST_CATEGORIES = ["popular", "top-rated", "now-playing", "upcoming"];
|
|
1795
|
+
function parseMovieListCategory(value) {
|
|
1796
|
+
if (!MOVIE_LIST_CATEGORIES.includes(value)) {
|
|
1797
|
+
throw new CliError(`Invalid movie list category "${value}". Expected one of: ${MOVIE_LIST_CATEGORIES.join(", ")}.`, 2);
|
|
1798
|
+
}
|
|
1799
|
+
return value;
|
|
1800
|
+
}
|
|
1801
|
+
var TV_LIST_CATEGORIES = ["popular", "top-rated", "airing-today", "on-the-air"];
|
|
1802
|
+
function parseTVListCategory(value) {
|
|
1803
|
+
if (!TV_LIST_CATEGORIES.includes(value)) {
|
|
1804
|
+
throw new CliError(`Invalid TV list category "${value}". Expected one of: ${TV_LIST_CATEGORIES.join(", ")}.`, 2);
|
|
1805
|
+
}
|
|
1806
|
+
return value;
|
|
1807
|
+
}
|
|
1808
|
+
|
|
1584
1809
|
// src/cli/format.ts
|
|
1585
|
-
function printJson(value) {
|
|
1586
|
-
process.stdout.write(`${JSON.stringify(value, null, 2)}
|
|
1810
|
+
function printJson(value, compact = false) {
|
|
1811
|
+
process.stdout.write(`${compact ? JSON.stringify(value) : JSON.stringify(value, null, 2)}
|
|
1587
1812
|
`);
|
|
1588
1813
|
}
|
|
1589
1814
|
function printSearchResults(response, type) {
|
|
@@ -1706,6 +1931,30 @@ function printCredits(credits, limit = 10) {
|
|
|
1706
1931
|
}
|
|
1707
1932
|
writeLines(lines);
|
|
1708
1933
|
}
|
|
1934
|
+
function printAggregateCredits(credits, limit = 10) {
|
|
1935
|
+
const lines = [];
|
|
1936
|
+
if (credits.cast.length > 0) {
|
|
1937
|
+
lines.push("Cast:");
|
|
1938
|
+
for (const member of credits.cast.slice(0, limit)) {
|
|
1939
|
+
const roles = member.roles.map((role) => `${role.character} (${role.episode_count} eps)`).join(", ");
|
|
1940
|
+
lines.push(` ${member.name} as ${roles} #${member.id}`);
|
|
1941
|
+
}
|
|
1942
|
+
}
|
|
1943
|
+
if (credits.crew.length > 0) {
|
|
1944
|
+
if (lines.length > 0) {
|
|
1945
|
+
lines.push("");
|
|
1946
|
+
}
|
|
1947
|
+
lines.push("Crew:");
|
|
1948
|
+
for (const member of credits.crew.slice(0, limit)) {
|
|
1949
|
+
const jobs = member.jobs.map((job) => `${job.job} (${job.episode_count} eps)`).join(", ");
|
|
1950
|
+
lines.push(` ${member.name} — ${jobs} (${member.department}) #${member.id}`);
|
|
1951
|
+
}
|
|
1952
|
+
}
|
|
1953
|
+
if (lines.length === 0) {
|
|
1954
|
+
lines.push("No credits.");
|
|
1955
|
+
}
|
|
1956
|
+
writeLines(lines);
|
|
1957
|
+
}
|
|
1709
1958
|
function printImages(images, tmdb) {
|
|
1710
1959
|
const lines = [];
|
|
1711
1960
|
appendImageSection(lines, "Posters", images.posters, "w500", tmdb);
|
|
@@ -1716,6 +1965,14 @@ function printImages(images, tmdb) {
|
|
|
1716
1965
|
}
|
|
1717
1966
|
writeLines(lines);
|
|
1718
1967
|
}
|
|
1968
|
+
function printSeasonImages(images, tmdb) {
|
|
1969
|
+
const lines = [];
|
|
1970
|
+
appendImageSection(lines, "Posters", images.posters, "w500", tmdb);
|
|
1971
|
+
if (lines.length === 0) {
|
|
1972
|
+
lines.push("No images.");
|
|
1973
|
+
}
|
|
1974
|
+
writeLines(lines);
|
|
1975
|
+
}
|
|
1719
1976
|
function printVideos(videos) {
|
|
1720
1977
|
if (videos.results.length === 0) {
|
|
1721
1978
|
writeLines(["No videos."]);
|
|
@@ -1750,6 +2007,49 @@ function printSeason(season, tmdb) {
|
|
|
1750
2007
|
}
|
|
1751
2008
|
writeLines(lines);
|
|
1752
2009
|
}
|
|
2010
|
+
function printEpisode(episode, tmdb, seriesId) {
|
|
2011
|
+
const lines = [`${episodeCode(episode)} · ${episode.name}`];
|
|
2012
|
+
const facts = [
|
|
2013
|
+
episode.air_date,
|
|
2014
|
+
episode.runtime ? `${episode.runtime} min` : undefined,
|
|
2015
|
+
`★ ${episode.vote_average.toFixed(1)}/10 (${episode.vote_count} votes)`
|
|
2016
|
+
].filter(Boolean);
|
|
2017
|
+
lines.push(facts.join(" · "));
|
|
2018
|
+
lines.push(`TMDB: https://www.themoviedb.org/tv/${seriesId}/season/${episode.season_number}/episode/${episode.episode_number}`);
|
|
2019
|
+
const still = tmdb.images.url(episode.still_path, "w300");
|
|
2020
|
+
if (still) {
|
|
2021
|
+
lines.push(`Still: ${still}`);
|
|
2022
|
+
}
|
|
2023
|
+
if (episode.overview) {
|
|
2024
|
+
lines.push("", episode.overview);
|
|
2025
|
+
}
|
|
2026
|
+
writeLines(lines);
|
|
2027
|
+
}
|
|
2028
|
+
function printEpisodeGroups(response) {
|
|
2029
|
+
if (response.results.length === 0) {
|
|
2030
|
+
writeLines(["No episode groups."]);
|
|
2031
|
+
return;
|
|
2032
|
+
}
|
|
2033
|
+
writeLines(response.results.map((group) => `${group.id} ${group.name} (${group.group_count} groups, ${group.episode_count} episodes)`));
|
|
2034
|
+
}
|
|
2035
|
+
function printEpisodeGroup(group) {
|
|
2036
|
+
const lines = [group.name];
|
|
2037
|
+
if (group.description) {
|
|
2038
|
+
lines.push(group.description);
|
|
2039
|
+
}
|
|
2040
|
+
if (group.groups.length === 0) {
|
|
2041
|
+
lines.push("", "No episodes.");
|
|
2042
|
+
writeLines(lines);
|
|
2043
|
+
return;
|
|
2044
|
+
}
|
|
2045
|
+
for (const section of group.groups) {
|
|
2046
|
+
lines.push("", section.name);
|
|
2047
|
+
for (const episode of section.episodes) {
|
|
2048
|
+
lines.push(` ${episode.order}. ${episodeCode(episode)} · ${episode.name} #${episode.id}`);
|
|
2049
|
+
}
|
|
2050
|
+
}
|
|
2051
|
+
writeLines(lines);
|
|
2052
|
+
}
|
|
1753
2053
|
function printPerson(person, tmdb) {
|
|
1754
2054
|
const lines = [person.name];
|
|
1755
2055
|
const facts = [
|
|
@@ -1851,6 +2151,9 @@ function writeLines(lines) {
|
|
|
1851
2151
|
`)}
|
|
1852
2152
|
`);
|
|
1853
2153
|
}
|
|
2154
|
+
function episodeCode(episode) {
|
|
2155
|
+
return `S${String(episode.season_number).padStart(2, "0")}E${String(episode.episode_number).padStart(2, "0")}`;
|
|
2156
|
+
}
|
|
1854
2157
|
function formatSearchResult(result, index, fallbackType) {
|
|
1855
2158
|
const title = "title" in result ? result.title : result.name;
|
|
1856
2159
|
const date = "release_date" in result ? result.release_date : ("first_air_date" in result) ? result.first_air_date : "";
|
|
@@ -1860,95 +2163,211 @@ function formatSearchResult(result, index, fallbackType) {
|
|
|
1860
2163
|
return `${String(index + 1).padStart(2)}. ${title}${year}${score} ${mediaType} ${result.id}`;
|
|
1861
2164
|
}
|
|
1862
2165
|
|
|
1863
|
-
// src/cli/
|
|
1864
|
-
var
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
2166
|
+
// src/cli/output.ts
|
|
2167
|
+
var OUTPUT_FORMATS = ["text", "json", "ndjson"];
|
|
2168
|
+
var MAX_PAGE = 500;
|
|
2169
|
+
var FORMAT_ALIASES = { jsonl: "ndjson" };
|
|
2170
|
+
function resolveOutput(args, options = {}) {
|
|
2171
|
+
const explicitFormat = args.format !== undefined && args.format.trim() !== "";
|
|
2172
|
+
const format = parseFormat(args.format);
|
|
2173
|
+
const fields = parseFields(args.fields);
|
|
2174
|
+
const all = args.all === true;
|
|
2175
|
+
const maxItems = parseMaxItems(args["max-items"]);
|
|
2176
|
+
if (args.json && explicitFormat && format !== "json") {
|
|
2177
|
+
throw new CliError(`--json cannot be combined with --format ${format}.`, 2);
|
|
2178
|
+
}
|
|
2179
|
+
const resolvedFormat = args.json ? "json" : format;
|
|
2180
|
+
if (fields && resolvedFormat === "text") {
|
|
2181
|
+
throw new CliError("--fields requires --format json or ndjson.", 2);
|
|
2182
|
+
}
|
|
2183
|
+
if (all || maxItems !== undefined) {
|
|
2184
|
+
if (resolvedFormat !== "ndjson") {
|
|
2185
|
+
throw new CliError(`${all ? "--all" : "--max-items"} requires --format ndjson.`, 2);
|
|
2186
|
+
}
|
|
2187
|
+
if (options.paginated !== true) {
|
|
2188
|
+
throw new CliError(`${all ? "--all" : "--max-items"} is only supported by list commands.`, 2);
|
|
2189
|
+
}
|
|
1868
2190
|
}
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
2191
|
+
return {
|
|
2192
|
+
...all ? { all: true } : {},
|
|
2193
|
+
compact: args.compact === true,
|
|
2194
|
+
...fields ? { fields } : {},
|
|
2195
|
+
format: resolvedFormat,
|
|
2196
|
+
...maxItems !== undefined ? { maxItems } : {}
|
|
2197
|
+
};
|
|
2198
|
+
}
|
|
2199
|
+
function printResult(value, output, renderText) {
|
|
2200
|
+
if (output.format === "text") {
|
|
2201
|
+
renderText();
|
|
2202
|
+
return;
|
|
1875
2203
|
}
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
if (!SEARCH_TYPES.includes(value)) {
|
|
1880
|
-
throw new CliError(`Invalid --type "${value}". Expected one of: ${SEARCH_TYPES.join(", ")}.`, 2);
|
|
2204
|
+
if (output.format === "json") {
|
|
2205
|
+
printJson(output.fields ? projectPayload(value, output.fields) : value, output.compact);
|
|
2206
|
+
return;
|
|
1881
2207
|
}
|
|
1882
|
-
|
|
1883
|
-
}
|
|
1884
|
-
function
|
|
1885
|
-
|
|
1886
|
-
|
|
2208
|
+
printNdjson(value, output);
|
|
2209
|
+
}
|
|
2210
|
+
async function printPaged({
|
|
2211
|
+
fetchPage,
|
|
2212
|
+
output,
|
|
2213
|
+
renderText,
|
|
2214
|
+
startPage
|
|
2215
|
+
}) {
|
|
2216
|
+
let response = await fetchPage(startPage);
|
|
2217
|
+
if (output.format === "text") {
|
|
2218
|
+
renderText(response);
|
|
2219
|
+
return;
|
|
1887
2220
|
}
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
2221
|
+
if (output.format === "json") {
|
|
2222
|
+
printJson(output.fields ? projectPayload(response, output.fields) : response, output.compact);
|
|
2223
|
+
return;
|
|
1891
2224
|
}
|
|
1892
|
-
|
|
2225
|
+
const maxItems = output.maxItems ?? Number.POSITIVE_INFINITY;
|
|
2226
|
+
let page = startPage;
|
|
2227
|
+
let pages = 0;
|
|
2228
|
+
let count = 0;
|
|
2229
|
+
writeNdjson({ ...envelopeOf(response), type: "meta" });
|
|
2230
|
+
while (true) {
|
|
2231
|
+
pages += 1;
|
|
2232
|
+
for (const item of response.results) {
|
|
2233
|
+
if (count >= maxItems) {
|
|
2234
|
+
break;
|
|
2235
|
+
}
|
|
2236
|
+
writeNdjson({
|
|
2237
|
+
type: "item",
|
|
2238
|
+
index: count,
|
|
2239
|
+
value: output.fields ? projectRecord(item, output.fields, count === 0) : item
|
|
2240
|
+
});
|
|
2241
|
+
count += 1;
|
|
2242
|
+
}
|
|
2243
|
+
if (!output.all || count >= maxItems || response.results.length === 0) {
|
|
2244
|
+
break;
|
|
2245
|
+
}
|
|
2246
|
+
if (page >= Math.min(response.total_pages, MAX_PAGE)) {
|
|
2247
|
+
break;
|
|
2248
|
+
}
|
|
2249
|
+
page += 1;
|
|
2250
|
+
response = await fetchPage(page);
|
|
2251
|
+
}
|
|
2252
|
+
writeNdjson({ type: "summary", count, pages });
|
|
1893
2253
|
}
|
|
1894
|
-
function
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
2254
|
+
async function runSafely(args, task, options = {}) {
|
|
2255
|
+
let output;
|
|
2256
|
+
try {
|
|
2257
|
+
output = resolveOutput(args, options);
|
|
2258
|
+
await task(output);
|
|
2259
|
+
} catch (error) {
|
|
2260
|
+
writeError(error, output ?? fallbackOutput(args));
|
|
2261
|
+
process.exitCode = exitCodeFor(error);
|
|
1898
2262
|
}
|
|
1899
|
-
return id;
|
|
1900
2263
|
}
|
|
1901
|
-
function
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
2264
|
+
function writeError(error, output) {
|
|
2265
|
+
if (output.format === "text") {
|
|
2266
|
+
process.stderr.write(`${errorMessage(error)}
|
|
2267
|
+
`);
|
|
2268
|
+
return;
|
|
1905
2269
|
}
|
|
1906
|
-
|
|
2270
|
+
process.stderr.write(`${JSON.stringify(toErrorRecord(error))}
|
|
2271
|
+
`);
|
|
1907
2272
|
}
|
|
1908
|
-
function
|
|
2273
|
+
function fallbackOutput(args) {
|
|
2274
|
+
const format = args.format?.trim().toLowerCase();
|
|
2275
|
+
const wantsJson = args.json === true || format === "json" || format === "jsonl" || format === "ndjson";
|
|
2276
|
+
return {
|
|
2277
|
+
compact: args.compact === true,
|
|
2278
|
+
format: wantsJson ? "json" : "text"
|
|
2279
|
+
};
|
|
2280
|
+
}
|
|
2281
|
+
function parseFormat(value) {
|
|
1909
2282
|
if (value === undefined || value === "") {
|
|
1910
|
-
return
|
|
2283
|
+
return "text";
|
|
1911
2284
|
}
|
|
1912
|
-
const
|
|
1913
|
-
|
|
1914
|
-
|
|
2285
|
+
const requested = value.trim().toLowerCase();
|
|
2286
|
+
const format = FORMAT_ALIASES[requested] ?? requested;
|
|
2287
|
+
if (!OUTPUT_FORMATS.includes(format)) {
|
|
2288
|
+
throw new CliError(`Invalid --format "${value}". Expected one of: ${OUTPUT_FORMATS.join(", ")}.`, 2);
|
|
1915
2289
|
}
|
|
1916
|
-
return
|
|
2290
|
+
return format;
|
|
1917
2291
|
}
|
|
1918
|
-
function
|
|
2292
|
+
function parseFields(value) {
|
|
1919
2293
|
if (value === undefined) {
|
|
1920
2294
|
return;
|
|
1921
2295
|
}
|
|
1922
|
-
const
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
if (!TRENDING_WINDOWS.includes(value)) {
|
|
1928
|
-
throw new CliError(`Invalid --window "${value}". Expected one of: ${TRENDING_WINDOWS.join(", ")}.`, 2);
|
|
2296
|
+
const fields = [
|
|
2297
|
+
...new Set(value.split(",").map((field) => field.trim()).filter(Boolean))
|
|
2298
|
+
];
|
|
2299
|
+
if (fields.length === 0) {
|
|
2300
|
+
throw new CliError("--fields requires at least one field name.", 2);
|
|
1929
2301
|
}
|
|
1930
|
-
return
|
|
2302
|
+
return fields;
|
|
1931
2303
|
}
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
throw new CliError(`Invalid --type "${value}". Expected one of: ${GENRE_TYPES.join(", ")}.`, 2);
|
|
2304
|
+
function parseMaxItems(value) {
|
|
2305
|
+
if (value === undefined) {
|
|
2306
|
+
return;
|
|
1936
2307
|
}
|
|
1937
|
-
|
|
2308
|
+
const maxItems = Number(value);
|
|
2309
|
+
if (!Number.isInteger(maxItems) || maxItems < 1) {
|
|
2310
|
+
throw new CliError("Invalid --max-items value: expected a positive integer.", 2);
|
|
2311
|
+
}
|
|
2312
|
+
return maxItems;
|
|
2313
|
+
}
|
|
2314
|
+
function printNdjson(value, output) {
|
|
2315
|
+
if (isRecord(value) && Array.isArray(value.results)) {
|
|
2316
|
+
const { results, ...envelope } = value;
|
|
2317
|
+
writeNdjson({ ...envelope, type: "meta" });
|
|
2318
|
+
results.forEach((item, index) => {
|
|
2319
|
+
writeNdjson({
|
|
2320
|
+
type: "item",
|
|
2321
|
+
index,
|
|
2322
|
+
value: output.fields ? projectRecord(item, output.fields, index === 0) : item
|
|
2323
|
+
});
|
|
2324
|
+
});
|
|
2325
|
+
writeNdjson({ type: "summary", count: results.length, pages: 1 });
|
|
2326
|
+
return;
|
|
2327
|
+
}
|
|
2328
|
+
writeNdjson({
|
|
2329
|
+
type: "result",
|
|
2330
|
+
value: output.fields ? projectRecord(value, output.fields, true) : value
|
|
2331
|
+
});
|
|
1938
2332
|
}
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
2333
|
+
function writeNdjson(record) {
|
|
2334
|
+
process.stdout.write(`${JSON.stringify(record)}
|
|
2335
|
+
`);
|
|
2336
|
+
}
|
|
2337
|
+
function envelopeOf(response) {
|
|
2338
|
+
const envelope = { ...response };
|
|
2339
|
+
delete envelope.results;
|
|
2340
|
+
return envelope;
|
|
2341
|
+
}
|
|
2342
|
+
function projectPayload(value, fields) {
|
|
2343
|
+
if (!isRecord(value)) {
|
|
2344
|
+
throw new CliError("--fields requires an object response.", 2);
|
|
1943
2345
|
}
|
|
1944
|
-
|
|
2346
|
+
if (Array.isArray(value.results)) {
|
|
2347
|
+
return {
|
|
2348
|
+
...value,
|
|
2349
|
+
results: value.results.map((item, index) => projectRecord(item, fields, index === 0))
|
|
2350
|
+
};
|
|
2351
|
+
}
|
|
2352
|
+
return projectRecord(value, fields, true);
|
|
1945
2353
|
}
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
throw new CliError(`Invalid TV list category "${value}". Expected one of: ${TV_LIST_CATEGORIES.join(", ")}.`, 2);
|
|
2354
|
+
function projectRecord(record, fields, validate) {
|
|
2355
|
+
if (!isRecord(record)) {
|
|
2356
|
+
throw new CliError("--fields requires object records.", 2);
|
|
1950
2357
|
}
|
|
1951
|
-
|
|
2358
|
+
const projected = {};
|
|
2359
|
+
for (const field of fields) {
|
|
2360
|
+
if (Object.hasOwn(record, field)) {
|
|
2361
|
+
projected[field] = record[field];
|
|
2362
|
+
}
|
|
2363
|
+
}
|
|
2364
|
+
if (validate && Object.keys(projected).length === 0) {
|
|
2365
|
+
throw new CliError(`Unknown --fields value(s): ${fields.join(", ")}. Available: ${Object.keys(record).sort().join(", ")}.`, 2);
|
|
2366
|
+
}
|
|
2367
|
+
return projected;
|
|
2368
|
+
}
|
|
2369
|
+
function isRecord(value) {
|
|
2370
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1952
2371
|
}
|
|
1953
2372
|
|
|
1954
2373
|
// src/cli/prompt.ts
|
|
@@ -2018,7 +2437,7 @@ var authCommand = defineCommand({
|
|
|
2018
2437
|
}
|
|
2019
2438
|
},
|
|
2020
2439
|
async run({ args }) {
|
|
2021
|
-
await runSafely(async () => {
|
|
2440
|
+
await runSafely({}, async () => {
|
|
2022
2441
|
const existing = resolveCredentials();
|
|
2023
2442
|
if (!args.force && existing.source !== "none") {
|
|
2024
2443
|
if (!process.stdin.isTTY) {
|
|
@@ -2085,9 +2504,9 @@ Saved ${credential.kind} to ${getConfigPath()}
|
|
|
2085
2504
|
name: "status",
|
|
2086
2505
|
description: "Show credential source, token mask, and config path"
|
|
2087
2506
|
},
|
|
2088
|
-
args: { ...
|
|
2507
|
+
args: { ...outputArgs },
|
|
2089
2508
|
async run({ args }) {
|
|
2090
|
-
await runSafely(async () => {
|
|
2509
|
+
await runSafely(args, async (output) => {
|
|
2091
2510
|
const credentials = resolveCredentials();
|
|
2092
2511
|
const path = getConfigPath();
|
|
2093
2512
|
const configExists = existsSync2(path);
|
|
@@ -2101,10 +2520,6 @@ Saved ${credential.kind} to ${getConfigPath()}
|
|
|
2101
2520
|
language: credentials.config.language ?? null,
|
|
2102
2521
|
region: credentials.config.region ?? null
|
|
2103
2522
|
};
|
|
2104
|
-
if (args.json) {
|
|
2105
|
-
printJson(view);
|
|
2106
|
-
return;
|
|
2107
|
-
}
|
|
2108
2523
|
const lines = [
|
|
2109
2524
|
`Credential source: ${describeSource(credentials)}`,
|
|
2110
2525
|
`Token: ${view.token ?? "(not set)"}`,
|
|
@@ -2119,9 +2534,9 @@ Saved ${credential.kind} to ${getConfigPath()}
|
|
|
2119
2534
|
if (credentials.source === "none") {
|
|
2120
2535
|
lines.push("", "Run `tmdb auth login` to set up credentials.");
|
|
2121
2536
|
}
|
|
2122
|
-
process.stdout.write(`${lines.join(`
|
|
2537
|
+
printResult(view, output, () => process.stdout.write(`${lines.join(`
|
|
2123
2538
|
`)}
|
|
2124
|
-
`);
|
|
2539
|
+
`));
|
|
2125
2540
|
});
|
|
2126
2541
|
}
|
|
2127
2542
|
}),
|
|
@@ -2131,7 +2546,7 @@ Saved ${credential.kind} to ${getConfigPath()}
|
|
|
2131
2546
|
description: "Remove saved API credentials from the config file"
|
|
2132
2547
|
},
|
|
2133
2548
|
async run() {
|
|
2134
|
-
await runSafely(async () => {
|
|
2549
|
+
await runSafely({}, async () => {
|
|
2135
2550
|
const config = readConfig();
|
|
2136
2551
|
const hadCredentials = Boolean(config.accessToken || config.apiKey);
|
|
2137
2552
|
if (hadCredentials) {
|
|
@@ -2238,9 +2653,9 @@ var configCommand = defineCommand({
|
|
|
2238
2653
|
name: "show",
|
|
2239
2654
|
description: "Show the resolved config with secrets masked"
|
|
2240
2655
|
},
|
|
2241
|
-
args: { ...
|
|
2656
|
+
args: { ...outputArgs },
|
|
2242
2657
|
async run({ args }) {
|
|
2243
|
-
await runSafely(async () => {
|
|
2658
|
+
await runSafely(args, async (output) => {
|
|
2244
2659
|
const config = readConfig();
|
|
2245
2660
|
const path = getConfigPath();
|
|
2246
2661
|
const view = {
|
|
@@ -2250,19 +2665,17 @@ var configCommand = defineCommand({
|
|
|
2250
2665
|
language: config.language ?? null,
|
|
2251
2666
|
region: config.region ?? null
|
|
2252
2667
|
};
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
`language: ${view.language ?? "(not set)"}`,
|
|
2262
|
-
`region: ${view.region ?? "(not set)"}`
|
|
2263
|
-
].join(`
|
|
2668
|
+
printResult(view, output, () => {
|
|
2669
|
+
process.stdout.write([
|
|
2670
|
+
`Path: ${path}`,
|
|
2671
|
+
`accessToken: ${view.accessToken ?? "(not set)"}`,
|
|
2672
|
+
`apiKey: ${view.apiKey ?? "(not set)"}`,
|
|
2673
|
+
`language: ${view.language ?? "(not set)"}`,
|
|
2674
|
+
`region: ${view.region ?? "(not set)"}`
|
|
2675
|
+
].join(`
|
|
2264
2676
|
`) + `
|
|
2265
2677
|
`);
|
|
2678
|
+
});
|
|
2266
2679
|
});
|
|
2267
2680
|
}
|
|
2268
2681
|
}),
|
|
@@ -2281,18 +2694,16 @@ var configCommand = defineCommand({
|
|
|
2281
2694
|
name: "test",
|
|
2282
2695
|
description: "Verify credentials with an authenticated TMDB request"
|
|
2283
2696
|
},
|
|
2284
|
-
args: { ...
|
|
2697
|
+
args: { ...outputArgs },
|
|
2285
2698
|
async run({ args }) {
|
|
2286
|
-
await runSafely(async () => {
|
|
2699
|
+
await runSafely(args, async (output) => {
|
|
2287
2700
|
const client = createClient();
|
|
2288
2701
|
await client.movies.details(550);
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
return;
|
|
2292
|
-
}
|
|
2293
|
-
process.stdout.write(`TMDB connection OK
|
|
2702
|
+
printResult({ ok: true }, output, () => {
|
|
2703
|
+
process.stdout.write(`TMDB connection OK
|
|
2294
2704
|
Credentials are valid.
|
|
2295
2705
|
`);
|
|
2706
|
+
});
|
|
2296
2707
|
});
|
|
2297
2708
|
}
|
|
2298
2709
|
}),
|
|
@@ -2314,7 +2725,7 @@ Credentials are valid.
|
|
|
2314
2725
|
}
|
|
2315
2726
|
},
|
|
2316
2727
|
async run({ args }) {
|
|
2317
|
-
await runSafely(async () => {
|
|
2728
|
+
await runSafely({}, async () => {
|
|
2318
2729
|
const field = parseConfigKey(args.key);
|
|
2319
2730
|
const path = getConfigPath();
|
|
2320
2731
|
if (field === "accessToken" || field === "apiKey") {
|
|
@@ -2354,7 +2765,7 @@ Credentials are valid.
|
|
|
2354
2765
|
}
|
|
2355
2766
|
},
|
|
2356
2767
|
async run({ args }) {
|
|
2357
|
-
await runSafely(async () => {
|
|
2768
|
+
await runSafely({}, async () => {
|
|
2358
2769
|
const field = parseConfigKey(args.key);
|
|
2359
2770
|
unsetConfigValue(field);
|
|
2360
2771
|
process.stdout.write(`Removed ${field} from ${getConfigPath()}
|
|
@@ -2386,15 +2797,11 @@ var genresCommand = defineCommand({
|
|
|
2386
2797
|
}
|
|
2387
2798
|
},
|
|
2388
2799
|
async run({ args }) {
|
|
2389
|
-
await runSafely(async () => {
|
|
2800
|
+
await runSafely(args, async (output) => {
|
|
2390
2801
|
const type = parseGenreType(args.type);
|
|
2391
2802
|
const client = createClient({ language: args.lang });
|
|
2392
2803
|
const response = type === "tv" ? await client.genres.tv() : await client.genres.movies();
|
|
2393
|
-
|
|
2394
|
-
printJson(response);
|
|
2395
|
-
} else {
|
|
2396
|
-
printGenres(response);
|
|
2397
|
-
}
|
|
2804
|
+
printResult(response, output, () => printGenres(response));
|
|
2398
2805
|
});
|
|
2399
2806
|
}
|
|
2400
2807
|
});
|
|
@@ -2413,6 +2820,7 @@ var movieCommand = defineCommand({
|
|
|
2413
2820
|
},
|
|
2414
2821
|
args: {
|
|
2415
2822
|
...commonArgs,
|
|
2823
|
+
...paginationArgs,
|
|
2416
2824
|
category: {
|
|
2417
2825
|
type: "positional",
|
|
2418
2826
|
description: "popular, top-rated, now-playing, or upcoming",
|
|
@@ -2428,17 +2836,20 @@ var movieCommand = defineCommand({
|
|
|
2428
2836
|
}
|
|
2429
2837
|
},
|
|
2430
2838
|
async run({ args }) {
|
|
2431
|
-
await runSafely(async () => {
|
|
2839
|
+
await runSafely(args, async (output) => {
|
|
2432
2840
|
const category = parseMovieListCategory(args.category);
|
|
2433
|
-
const
|
|
2841
|
+
const params = { page: parsePage(args.page), region: args.region };
|
|
2434
2842
|
const client = createClient({ language: args.lang });
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2843
|
+
await printPaged({
|
|
2844
|
+
fetchPage: (page) => {
|
|
2845
|
+
const options = { ...params, page };
|
|
2846
|
+
return category === "top-rated" ? client.movies.topRated(options) : category === "now-playing" ? client.movies.nowPlaying(options) : category === "upcoming" ? client.movies.upcoming(options) : client.movies.popular(options);
|
|
2847
|
+
},
|
|
2848
|
+
output,
|
|
2849
|
+
renderText: (response) => printSearchResults(response, "movie"),
|
|
2850
|
+
startPage: params.page
|
|
2851
|
+
});
|
|
2852
|
+
}, { paginated: true });
|
|
2442
2853
|
}
|
|
2443
2854
|
}),
|
|
2444
2855
|
get: defineCommand({
|
|
@@ -2459,16 +2870,12 @@ var movieCommand = defineCommand({
|
|
|
2459
2870
|
}
|
|
2460
2871
|
},
|
|
2461
2872
|
async run({ args }) {
|
|
2462
|
-
await runSafely(async () => {
|
|
2873
|
+
await runSafely(args, async (output) => {
|
|
2463
2874
|
const id = parseId(args.id, "movie ID");
|
|
2464
2875
|
const client = createClient({ language: args.lang });
|
|
2465
2876
|
const appendToResponse = parseCommaList(args.append);
|
|
2466
2877
|
const movie = await client.movies.details(id, { appendToResponse });
|
|
2467
|
-
|
|
2468
|
-
printJson(movie);
|
|
2469
|
-
} else {
|
|
2470
|
-
printMovie(movie, client);
|
|
2471
|
-
}
|
|
2878
|
+
printResult(movie, output, () => printMovie(movie, client));
|
|
2472
2879
|
});
|
|
2473
2880
|
}
|
|
2474
2881
|
}),
|
|
@@ -2490,16 +2897,12 @@ var movieCommand = defineCommand({
|
|
|
2490
2897
|
}
|
|
2491
2898
|
},
|
|
2492
2899
|
async run({ args }) {
|
|
2493
|
-
await runSafely(async () => {
|
|
2900
|
+
await runSafely(args, async (output) => {
|
|
2494
2901
|
const id = parseId(args.id, "movie ID");
|
|
2495
2902
|
const limit = parseLimit(args.limit);
|
|
2496
2903
|
const client = createClient({ language: args.lang });
|
|
2497
2904
|
const credits = await client.movies.credits(id);
|
|
2498
|
-
|
|
2499
|
-
printJson(credits);
|
|
2500
|
-
} else {
|
|
2501
|
-
printCredits(credits, limit);
|
|
2502
|
-
}
|
|
2905
|
+
printResult(credits, output, () => printCredits(credits, limit));
|
|
2503
2906
|
});
|
|
2504
2907
|
}
|
|
2505
2908
|
}),
|
|
@@ -2521,16 +2924,12 @@ var movieCommand = defineCommand({
|
|
|
2521
2924
|
}
|
|
2522
2925
|
},
|
|
2523
2926
|
async run({ args }) {
|
|
2524
|
-
await runSafely(async () => {
|
|
2927
|
+
await runSafely(args, async (output) => {
|
|
2525
2928
|
const id = parseId(args.id, "movie ID");
|
|
2526
2929
|
const includeImageLanguage = parseCommaList(args["image-language"]);
|
|
2527
2930
|
const client = createClient({ language: args.lang });
|
|
2528
2931
|
const images = await client.movies.images(id, { includeImageLanguage });
|
|
2529
|
-
|
|
2530
|
-
printJson(images);
|
|
2531
|
-
} else {
|
|
2532
|
-
printImages(images, client);
|
|
2533
|
-
}
|
|
2932
|
+
printResult(images, output, () => printImages(images, client));
|
|
2534
2933
|
});
|
|
2535
2934
|
}
|
|
2536
2935
|
}),
|
|
@@ -2548,15 +2947,11 @@ var movieCommand = defineCommand({
|
|
|
2548
2947
|
}
|
|
2549
2948
|
},
|
|
2550
2949
|
async run({ args }) {
|
|
2551
|
-
await runSafely(async () => {
|
|
2950
|
+
await runSafely(args, async (output) => {
|
|
2552
2951
|
const id = parseId(args.id, "movie ID");
|
|
2553
2952
|
const client = createClient({ language: args.lang });
|
|
2554
2953
|
const videos = await client.movies.videos(id);
|
|
2555
|
-
|
|
2556
|
-
printJson(videos);
|
|
2557
|
-
} else {
|
|
2558
|
-
printVideos(videos);
|
|
2559
|
-
}
|
|
2954
|
+
printResult(videos, output, () => printVideos(videos));
|
|
2560
2955
|
});
|
|
2561
2956
|
}
|
|
2562
2957
|
}),
|
|
@@ -2579,20 +2974,21 @@ var movieCommand = defineCommand({
|
|
|
2579
2974
|
region: {
|
|
2580
2975
|
type: "string",
|
|
2581
2976
|
description: "ISO 3166-1 region code"
|
|
2582
|
-
}
|
|
2977
|
+
},
|
|
2978
|
+
...paginationArgs
|
|
2583
2979
|
},
|
|
2584
2980
|
async run({ args }) {
|
|
2585
|
-
await runSafely(async () => {
|
|
2981
|
+
await runSafely(args, async (output) => {
|
|
2586
2982
|
const id = parseId(args.id, "movie ID");
|
|
2587
|
-
const
|
|
2983
|
+
const params = { page: parsePage(args.page), region: args.region };
|
|
2588
2984
|
const client = createClient({ language: args.lang });
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
}
|
|
2595
|
-
});
|
|
2985
|
+
await printPaged({
|
|
2986
|
+
fetchPage: (page) => client.movies.similar(id, { ...params, page }),
|
|
2987
|
+
output,
|
|
2988
|
+
renderText: (response) => printSearchResults(response, "movie"),
|
|
2989
|
+
startPage: params.page
|
|
2990
|
+
});
|
|
2991
|
+
}, { paginated: true });
|
|
2596
2992
|
}
|
|
2597
2993
|
}),
|
|
2598
2994
|
recommendations: defineCommand({
|
|
@@ -2614,20 +3010,21 @@ var movieCommand = defineCommand({
|
|
|
2614
3010
|
region: {
|
|
2615
3011
|
type: "string",
|
|
2616
3012
|
description: "ISO 3166-1 region code"
|
|
2617
|
-
}
|
|
3013
|
+
},
|
|
3014
|
+
...paginationArgs
|
|
2618
3015
|
},
|
|
2619
3016
|
async run({ args }) {
|
|
2620
|
-
await runSafely(async () => {
|
|
3017
|
+
await runSafely(args, async (output) => {
|
|
2621
3018
|
const id = parseId(args.id, "movie ID");
|
|
2622
|
-
const
|
|
3019
|
+
const params = { page: parsePage(args.page), region: args.region };
|
|
2623
3020
|
const client = createClient({ language: args.lang });
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
}
|
|
2630
|
-
});
|
|
3021
|
+
await printPaged({
|
|
3022
|
+
fetchPage: (page) => client.movies.recommendations(id, { ...params, page }),
|
|
3023
|
+
output,
|
|
3024
|
+
renderText: (response) => printSearchResults(response, "movie"),
|
|
3025
|
+
startPage: params.page
|
|
3026
|
+
});
|
|
3027
|
+
}, { paginated: true });
|
|
2631
3028
|
}
|
|
2632
3029
|
})
|
|
2633
3030
|
}
|
|
@@ -2654,15 +3051,11 @@ var personCommand = defineCommand({
|
|
|
2654
3051
|
}
|
|
2655
3052
|
},
|
|
2656
3053
|
async run({ args }) {
|
|
2657
|
-
await runSafely(async () => {
|
|
3054
|
+
await runSafely(args, async (output) => {
|
|
2658
3055
|
const id = parseId(args.id, "person ID");
|
|
2659
3056
|
const client = createClient({ language: args.lang });
|
|
2660
3057
|
const person = await client.people.details(id);
|
|
2661
|
-
|
|
2662
|
-
printJson(person);
|
|
2663
|
-
} else {
|
|
2664
|
-
printPerson(person, client);
|
|
2665
|
-
}
|
|
3058
|
+
printResult(person, output, () => printPerson(person, client));
|
|
2666
3059
|
});
|
|
2667
3060
|
}
|
|
2668
3061
|
}),
|
|
@@ -2684,16 +3077,12 @@ var personCommand = defineCommand({
|
|
|
2684
3077
|
}
|
|
2685
3078
|
},
|
|
2686
3079
|
async run({ args }) {
|
|
2687
|
-
await runSafely(async () => {
|
|
3080
|
+
await runSafely(args, async (output) => {
|
|
2688
3081
|
const id = parseId(args.id, "person ID");
|
|
2689
3082
|
const limit = parseLimit(args.limit);
|
|
2690
3083
|
const client = createClient({ language: args.lang });
|
|
2691
3084
|
const credits = await client.people.combinedCredits(id);
|
|
2692
|
-
|
|
2693
|
-
printJson(credits);
|
|
2694
|
-
} else {
|
|
2695
|
-
printPersonCredits(credits, limit);
|
|
2696
|
-
}
|
|
3085
|
+
printResult(credits, output, () => printPersonCredits(credits, limit));
|
|
2697
3086
|
});
|
|
2698
3087
|
}
|
|
2699
3088
|
})
|
|
@@ -2708,6 +3097,7 @@ var searchCommand = defineCommand({
|
|
|
2708
3097
|
},
|
|
2709
3098
|
args: {
|
|
2710
3099
|
...commonArgs,
|
|
3100
|
+
...paginationArgs,
|
|
2711
3101
|
query: {
|
|
2712
3102
|
type: "positional",
|
|
2713
3103
|
description: "Search query",
|
|
@@ -2729,7 +3119,7 @@ var searchCommand = defineCommand({
|
|
|
2729
3119
|
}
|
|
2730
3120
|
},
|
|
2731
3121
|
async run({ args }) {
|
|
2732
|
-
await runSafely(async () => {
|
|
3122
|
+
await runSafely(args, async (output) => {
|
|
2733
3123
|
const query = args.query.trim();
|
|
2734
3124
|
if (!query) {
|
|
2735
3125
|
throw new CliError("Search query must not be empty.", 2);
|
|
@@ -2741,14 +3131,16 @@ var searchCommand = defineCommand({
|
|
|
2741
3131
|
throw new CliError("--year is only supported with --type movie or tv.", 2);
|
|
2742
3132
|
}
|
|
2743
3133
|
const client = createClient({ language: args.lang });
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
3134
|
+
await printPaged({
|
|
3135
|
+
fetchPage: (nextPage) => {
|
|
3136
|
+
const options = { page: nextPage };
|
|
3137
|
+
return type === "movie" ? client.search.movies(query, { ...options, year }) : type === "tv" ? client.search.tv(query, { ...options, firstAirDateYear: year }) : type === "person" ? client.search.people(query, options) : client.search.multi(query, options);
|
|
3138
|
+
},
|
|
3139
|
+
output,
|
|
3140
|
+
renderText: (response) => printSearchResults(response, type),
|
|
3141
|
+
startPage: page
|
|
3142
|
+
});
|
|
3143
|
+
}, { paginated: true });
|
|
2752
3144
|
}
|
|
2753
3145
|
});
|
|
2754
3146
|
|
|
@@ -2760,6 +3152,7 @@ var trendingCommand = defineCommand({
|
|
|
2760
3152
|
},
|
|
2761
3153
|
args: {
|
|
2762
3154
|
...commonArgs,
|
|
3155
|
+
...paginationArgs,
|
|
2763
3156
|
type: {
|
|
2764
3157
|
type: "positional",
|
|
2765
3158
|
description: "Media type: all, movie, tv, or person",
|
|
@@ -2777,18 +3170,21 @@ var trendingCommand = defineCommand({
|
|
|
2777
3170
|
}
|
|
2778
3171
|
},
|
|
2779
3172
|
async run({ args }) {
|
|
2780
|
-
await runSafely(async () => {
|
|
3173
|
+
await runSafely(args, async (output) => {
|
|
2781
3174
|
const type = parseSearchType(args.type);
|
|
2782
3175
|
const window = parseWindow(args.window);
|
|
2783
3176
|
const page = parsePage(args.page);
|
|
2784
3177
|
const client = createClient({ language: args.lang });
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
3178
|
+
await printPaged({
|
|
3179
|
+
fetchPage: (nextPage) => {
|
|
3180
|
+
const options = { page: nextPage };
|
|
3181
|
+
return type === "movie" ? client.trending.movies(window, options) : type === "tv" ? client.trending.tv(window, options) : type === "person" ? client.trending.people(window, options) : client.trending.all(window, options);
|
|
3182
|
+
},
|
|
3183
|
+
output,
|
|
3184
|
+
renderText: (response) => printSearchResults(response, type),
|
|
3185
|
+
startPage: page
|
|
3186
|
+
});
|
|
3187
|
+
}, { paginated: true });
|
|
2792
3188
|
}
|
|
2793
3189
|
});
|
|
2794
3190
|
|
|
@@ -2806,6 +3202,7 @@ var tvCommand = defineCommand({
|
|
|
2806
3202
|
},
|
|
2807
3203
|
args: {
|
|
2808
3204
|
...commonArgs,
|
|
3205
|
+
...paginationArgs,
|
|
2809
3206
|
category: {
|
|
2810
3207
|
type: "positional",
|
|
2811
3208
|
description: "popular, top-rated, airing-today, or on-the-air",
|
|
@@ -2817,17 +3214,20 @@ var tvCommand = defineCommand({
|
|
|
2817
3214
|
}
|
|
2818
3215
|
},
|
|
2819
3216
|
async run({ args }) {
|
|
2820
|
-
await runSafely(async () => {
|
|
3217
|
+
await runSafely(args, async (output) => {
|
|
2821
3218
|
const category = parseTVListCategory(args.category);
|
|
2822
|
-
const
|
|
3219
|
+
const params = { page: parsePage(args.page) };
|
|
2823
3220
|
const client = createClient({ language: args.lang });
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
3221
|
+
await printPaged({
|
|
3222
|
+
fetchPage: (page) => {
|
|
3223
|
+
const options = { ...params, page };
|
|
3224
|
+
return category === "top-rated" ? client.tv.topRated(options) : category === "airing-today" ? client.tv.airingToday(options) : category === "on-the-air" ? client.tv.onTheAir(options) : client.tv.popular(options);
|
|
3225
|
+
},
|
|
3226
|
+
output,
|
|
3227
|
+
renderText: (response) => printSearchResults(response, "tv"),
|
|
3228
|
+
startPage: params.page
|
|
3229
|
+
});
|
|
3230
|
+
}, { paginated: true });
|
|
2831
3231
|
}
|
|
2832
3232
|
}),
|
|
2833
3233
|
get: defineCommand({
|
|
@@ -2848,16 +3248,12 @@ var tvCommand = defineCommand({
|
|
|
2848
3248
|
}
|
|
2849
3249
|
},
|
|
2850
3250
|
async run({ args }) {
|
|
2851
|
-
await runSafely(async () => {
|
|
3251
|
+
await runSafely(args, async (output) => {
|
|
2852
3252
|
const id = parseId(args.id, "TV show ID");
|
|
2853
3253
|
const client = createClient({ language: args.lang });
|
|
2854
3254
|
const appendToResponse = parseCommaList(args.append);
|
|
2855
3255
|
const tv = await client.tv.details(id, { appendToResponse });
|
|
2856
|
-
|
|
2857
|
-
printJson(tv);
|
|
2858
|
-
} else {
|
|
2859
|
-
printTV(tv, client);
|
|
2860
|
-
}
|
|
3256
|
+
printResult(tv, output, () => printTV(tv, client));
|
|
2861
3257
|
});
|
|
2862
3258
|
}
|
|
2863
3259
|
}),
|
|
@@ -2879,16 +3275,12 @@ var tvCommand = defineCommand({
|
|
|
2879
3275
|
}
|
|
2880
3276
|
},
|
|
2881
3277
|
async run({ args }) {
|
|
2882
|
-
await runSafely(async () => {
|
|
3278
|
+
await runSafely(args, async (output) => {
|
|
2883
3279
|
const id = parseId(args.id, "TV show ID");
|
|
2884
3280
|
const limit = parseLimit(args.limit);
|
|
2885
3281
|
const client = createClient({ language: args.lang });
|
|
2886
3282
|
const credits = await client.tv.credits(id);
|
|
2887
|
-
|
|
2888
|
-
printJson(credits);
|
|
2889
|
-
} else {
|
|
2890
|
-
printCredits(credits, limit);
|
|
2891
|
-
}
|
|
3283
|
+
printResult(credits, output, () => printCredits(credits, limit));
|
|
2892
3284
|
});
|
|
2893
3285
|
}
|
|
2894
3286
|
}),
|
|
@@ -2910,16 +3302,12 @@ var tvCommand = defineCommand({
|
|
|
2910
3302
|
}
|
|
2911
3303
|
},
|
|
2912
3304
|
async run({ args }) {
|
|
2913
|
-
await runSafely(async () => {
|
|
3305
|
+
await runSafely(args, async (output) => {
|
|
2914
3306
|
const id = parseId(args.id, "TV show ID");
|
|
2915
3307
|
const includeImageLanguage = parseCommaList(args["image-language"]);
|
|
2916
3308
|
const client = createClient({ language: args.lang });
|
|
2917
3309
|
const images = await client.tv.images(id, { includeImageLanguage });
|
|
2918
|
-
|
|
2919
|
-
printJson(images);
|
|
2920
|
-
} else {
|
|
2921
|
-
printImages(images, client);
|
|
2922
|
-
}
|
|
3310
|
+
printResult(images, output, () => printImages(images, client));
|
|
2923
3311
|
});
|
|
2924
3312
|
}
|
|
2925
3313
|
}),
|
|
@@ -2937,15 +3325,11 @@ var tvCommand = defineCommand({
|
|
|
2937
3325
|
}
|
|
2938
3326
|
},
|
|
2939
3327
|
async run({ args }) {
|
|
2940
|
-
await runSafely(async () => {
|
|
3328
|
+
await runSafely(args, async (output) => {
|
|
2941
3329
|
const id = parseId(args.id, "TV show ID");
|
|
2942
3330
|
const client = createClient({ language: args.lang });
|
|
2943
3331
|
const videos = await client.tv.videos(id);
|
|
2944
|
-
|
|
2945
|
-
printJson(videos);
|
|
2946
|
-
} else {
|
|
2947
|
-
printVideos(videos);
|
|
2948
|
-
}
|
|
3332
|
+
printResult(videos, output, () => printVideos(videos));
|
|
2949
3333
|
});
|
|
2950
3334
|
}
|
|
2951
3335
|
}),
|
|
@@ -2956,6 +3340,7 @@ var tvCommand = defineCommand({
|
|
|
2956
3340
|
},
|
|
2957
3341
|
args: {
|
|
2958
3342
|
...commonArgs,
|
|
3343
|
+
...paginationArgs,
|
|
2959
3344
|
id: {
|
|
2960
3345
|
type: "positional",
|
|
2961
3346
|
description: "TMDB TV show ID",
|
|
@@ -2967,17 +3352,17 @@ var tvCommand = defineCommand({
|
|
|
2967
3352
|
}
|
|
2968
3353
|
},
|
|
2969
3354
|
async run({ args }) {
|
|
2970
|
-
await runSafely(async () => {
|
|
3355
|
+
await runSafely(args, async (output) => {
|
|
2971
3356
|
const id = parseId(args.id, "TV show ID");
|
|
2972
|
-
const
|
|
3357
|
+
const params = { page: parsePage(args.page) };
|
|
2973
3358
|
const client = createClient({ language: args.lang });
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
}
|
|
2980
|
-
});
|
|
3359
|
+
await printPaged({
|
|
3360
|
+
fetchPage: (page) => client.tv.similar(id, { ...params, page }),
|
|
3361
|
+
output,
|
|
3362
|
+
renderText: (response) => printSearchResults(response, "tv"),
|
|
3363
|
+
startPage: params.page
|
|
3364
|
+
});
|
|
3365
|
+
}, { paginated: true });
|
|
2981
3366
|
}
|
|
2982
3367
|
}),
|
|
2983
3368
|
recommendations: defineCommand({
|
|
@@ -2987,6 +3372,7 @@ var tvCommand = defineCommand({
|
|
|
2987
3372
|
},
|
|
2988
3373
|
args: {
|
|
2989
3374
|
...commonArgs,
|
|
3375
|
+
...paginationArgs,
|
|
2990
3376
|
id: {
|
|
2991
3377
|
type: "positional",
|
|
2992
3378
|
description: "TMDB TV show ID",
|
|
@@ -2998,17 +3384,17 @@ var tvCommand = defineCommand({
|
|
|
2998
3384
|
}
|
|
2999
3385
|
},
|
|
3000
3386
|
async run({ args }) {
|
|
3001
|
-
await runSafely(async () => {
|
|
3387
|
+
await runSafely(args, async (output) => {
|
|
3002
3388
|
const id = parseId(args.id, "TV show ID");
|
|
3003
|
-
const
|
|
3389
|
+
const params = { page: parsePage(args.page) };
|
|
3004
3390
|
const client = createClient({ language: args.lang });
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
}
|
|
3011
|
-
});
|
|
3391
|
+
await printPaged({
|
|
3392
|
+
fetchPage: (page) => client.tv.recommendations(id, { ...params, page }),
|
|
3393
|
+
output,
|
|
3394
|
+
renderText: (response) => printSearchResults(response, "tv"),
|
|
3395
|
+
startPage: params.page
|
|
3396
|
+
});
|
|
3397
|
+
}, { paginated: true });
|
|
3012
3398
|
}
|
|
3013
3399
|
}),
|
|
3014
3400
|
season: defineCommand({
|
|
@@ -3030,16 +3416,251 @@ var tvCommand = defineCommand({
|
|
|
3030
3416
|
}
|
|
3031
3417
|
},
|
|
3032
3418
|
async run({ args }) {
|
|
3033
|
-
await runSafely(async () => {
|
|
3419
|
+
await runSafely(args, async (output) => {
|
|
3034
3420
|
const id = parseId(args.id, "TV show ID");
|
|
3035
3421
|
const seasonNumber = parseSeasonNumber(args.season);
|
|
3036
3422
|
const client = createClient({ language: args.lang });
|
|
3037
3423
|
const season = await client.tv.seasonDetails(id, seasonNumber);
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3424
|
+
printResult(season, output, () => printSeason(season, client));
|
|
3425
|
+
});
|
|
3426
|
+
}
|
|
3427
|
+
}),
|
|
3428
|
+
episode: defineCommand({
|
|
3429
|
+
meta: {
|
|
3430
|
+
name: "episode",
|
|
3431
|
+
description: "Show a TV episode"
|
|
3432
|
+
},
|
|
3433
|
+
args: {
|
|
3434
|
+
...commonArgs,
|
|
3435
|
+
id: {
|
|
3436
|
+
type: "positional",
|
|
3437
|
+
description: "TMDB TV show ID",
|
|
3438
|
+
required: true
|
|
3439
|
+
},
|
|
3440
|
+
season: {
|
|
3441
|
+
type: "positional",
|
|
3442
|
+
description: "Season number (0 for specials)",
|
|
3443
|
+
required: true
|
|
3444
|
+
},
|
|
3445
|
+
episode: {
|
|
3446
|
+
type: "positional",
|
|
3447
|
+
description: "Episode number",
|
|
3448
|
+
required: true
|
|
3449
|
+
},
|
|
3450
|
+
append: {
|
|
3451
|
+
type: "string",
|
|
3452
|
+
description: "Comma-separated append_to_response values"
|
|
3453
|
+
}
|
|
3454
|
+
},
|
|
3455
|
+
async run({ args }) {
|
|
3456
|
+
await runSafely(args, async (output) => {
|
|
3457
|
+
const id = parseId(args.id, "TV show ID");
|
|
3458
|
+
const seasonNumber = parseSeasonNumber(args.season);
|
|
3459
|
+
const episodeNumber = parseId(args.episode, "episode number");
|
|
3460
|
+
const appendToResponse = parseCommaList(args.append);
|
|
3461
|
+
const client = createClient({ language: args.lang });
|
|
3462
|
+
const episode = await client.tv.episodeDetails(id, seasonNumber, episodeNumber, {
|
|
3463
|
+
appendToResponse
|
|
3464
|
+
});
|
|
3465
|
+
printResult(episode, output, () => printEpisode(episode, client, id));
|
|
3466
|
+
});
|
|
3467
|
+
}
|
|
3468
|
+
}),
|
|
3469
|
+
"aggregate-credits": defineCommand({
|
|
3470
|
+
meta: {
|
|
3471
|
+
name: "aggregate-credits",
|
|
3472
|
+
description: "Show aggregate cast and crew credits for a TV show"
|
|
3473
|
+
},
|
|
3474
|
+
args: {
|
|
3475
|
+
...commonArgs,
|
|
3476
|
+
id: {
|
|
3477
|
+
type: "positional",
|
|
3478
|
+
description: "TMDB TV show ID",
|
|
3479
|
+
required: true
|
|
3480
|
+
},
|
|
3481
|
+
limit: {
|
|
3482
|
+
type: "string",
|
|
3483
|
+
description: "Maximum cast/crew rows (default: 10)"
|
|
3484
|
+
}
|
|
3485
|
+
},
|
|
3486
|
+
async run({ args }) {
|
|
3487
|
+
await runSafely(args, async (output) => {
|
|
3488
|
+
const id = parseId(args.id, "TV show ID");
|
|
3489
|
+
const limit = parseLimit(args.limit);
|
|
3490
|
+
const client = createClient({ language: args.lang });
|
|
3491
|
+
const credits = await client.tv.aggregateCredits(id);
|
|
3492
|
+
printResult(credits, output, () => printAggregateCredits(credits, limit));
|
|
3493
|
+
});
|
|
3494
|
+
}
|
|
3495
|
+
}),
|
|
3496
|
+
"season-credits": defineCommand({
|
|
3497
|
+
meta: {
|
|
3498
|
+
name: "season-credits",
|
|
3499
|
+
description: "Show cast and crew credits for a TV season"
|
|
3500
|
+
},
|
|
3501
|
+
args: {
|
|
3502
|
+
...commonArgs,
|
|
3503
|
+
id: {
|
|
3504
|
+
type: "positional",
|
|
3505
|
+
description: "TMDB TV show ID",
|
|
3506
|
+
required: true
|
|
3507
|
+
},
|
|
3508
|
+
season: {
|
|
3509
|
+
type: "positional",
|
|
3510
|
+
description: "Season number (0 for specials)",
|
|
3511
|
+
required: true
|
|
3512
|
+
},
|
|
3513
|
+
limit: {
|
|
3514
|
+
type: "string",
|
|
3515
|
+
description: "Maximum cast/crew rows (default: 10)"
|
|
3516
|
+
}
|
|
3517
|
+
},
|
|
3518
|
+
async run({ args }) {
|
|
3519
|
+
await runSafely(args, async (output) => {
|
|
3520
|
+
const id = parseId(args.id, "TV show ID");
|
|
3521
|
+
const seasonNumber = parseSeasonNumber(args.season);
|
|
3522
|
+
const limit = parseLimit(args.limit);
|
|
3523
|
+
const client = createClient({ language: args.lang });
|
|
3524
|
+
const credits = await client.tv.seasonCredits(id, seasonNumber);
|
|
3525
|
+
printResult(credits, output, () => printCredits(credits, limit));
|
|
3526
|
+
});
|
|
3527
|
+
}
|
|
3528
|
+
}),
|
|
3529
|
+
"season-aggregate-credits": defineCommand({
|
|
3530
|
+
meta: {
|
|
3531
|
+
name: "season-aggregate-credits",
|
|
3532
|
+
description: "Show aggregate cast and crew credits for a TV season"
|
|
3533
|
+
},
|
|
3534
|
+
args: {
|
|
3535
|
+
...commonArgs,
|
|
3536
|
+
id: {
|
|
3537
|
+
type: "positional",
|
|
3538
|
+
description: "TMDB TV show ID",
|
|
3539
|
+
required: true
|
|
3540
|
+
},
|
|
3541
|
+
season: {
|
|
3542
|
+
type: "positional",
|
|
3543
|
+
description: "Season number (0 for specials)",
|
|
3544
|
+
required: true
|
|
3545
|
+
},
|
|
3546
|
+
limit: {
|
|
3547
|
+
type: "string",
|
|
3548
|
+
description: "Maximum cast/crew rows (default: 10)"
|
|
3549
|
+
}
|
|
3550
|
+
},
|
|
3551
|
+
async run({ args }) {
|
|
3552
|
+
await runSafely(args, async (output) => {
|
|
3553
|
+
const id = parseId(args.id, "TV show ID");
|
|
3554
|
+
const seasonNumber = parseSeasonNumber(args.season);
|
|
3555
|
+
const limit = parseLimit(args.limit);
|
|
3556
|
+
const client = createClient({ language: args.lang });
|
|
3557
|
+
const credits = await client.tv.seasonAggregateCredits(id, seasonNumber);
|
|
3558
|
+
printResult(credits, output, () => printAggregateCredits(credits, limit));
|
|
3559
|
+
});
|
|
3560
|
+
}
|
|
3561
|
+
}),
|
|
3562
|
+
"season-images": defineCommand({
|
|
3563
|
+
meta: {
|
|
3564
|
+
name: "season-images",
|
|
3565
|
+
description: "Show TV season image URLs"
|
|
3566
|
+
},
|
|
3567
|
+
args: {
|
|
3568
|
+
...commonArgs,
|
|
3569
|
+
id: {
|
|
3570
|
+
type: "positional",
|
|
3571
|
+
description: "TMDB TV show ID",
|
|
3572
|
+
required: true
|
|
3573
|
+
},
|
|
3574
|
+
season: {
|
|
3575
|
+
type: "positional",
|
|
3576
|
+
description: "Season number (0 for specials)",
|
|
3577
|
+
required: true
|
|
3578
|
+
},
|
|
3579
|
+
"image-language": {
|
|
3580
|
+
type: "string",
|
|
3581
|
+
description: "Comma-separated image language codes"
|
|
3582
|
+
}
|
|
3583
|
+
},
|
|
3584
|
+
async run({ args }) {
|
|
3585
|
+
await runSafely(args, async (output) => {
|
|
3586
|
+
const id = parseId(args.id, "TV show ID");
|
|
3587
|
+
const seasonNumber = parseSeasonNumber(args.season);
|
|
3588
|
+
const includeImageLanguage = parseCommaList(args["image-language"]);
|
|
3589
|
+
const client = createClient({ language: args.lang });
|
|
3590
|
+
const images = await client.tv.seasonImages(id, seasonNumber, { includeImageLanguage });
|
|
3591
|
+
printResult(images, output, () => printSeasonImages(images, client));
|
|
3592
|
+
});
|
|
3593
|
+
}
|
|
3594
|
+
}),
|
|
3595
|
+
"season-videos": defineCommand({
|
|
3596
|
+
meta: {
|
|
3597
|
+
name: "season-videos",
|
|
3598
|
+
description: "Show TV season videos and trailers"
|
|
3599
|
+
},
|
|
3600
|
+
args: {
|
|
3601
|
+
...commonArgs,
|
|
3602
|
+
id: {
|
|
3603
|
+
type: "positional",
|
|
3604
|
+
description: "TMDB TV show ID",
|
|
3605
|
+
required: true
|
|
3606
|
+
},
|
|
3607
|
+
season: {
|
|
3608
|
+
type: "positional",
|
|
3609
|
+
description: "Season number (0 for specials)",
|
|
3610
|
+
required: true
|
|
3611
|
+
}
|
|
3612
|
+
},
|
|
3613
|
+
async run({ args }) {
|
|
3614
|
+
await runSafely(args, async (output) => {
|
|
3615
|
+
const id = parseId(args.id, "TV show ID");
|
|
3616
|
+
const seasonNumber = parseSeasonNumber(args.season);
|
|
3617
|
+
const client = createClient({ language: args.lang });
|
|
3618
|
+
const videos = await client.tv.seasonVideos(id, seasonNumber);
|
|
3619
|
+
printResult(videos, output, () => printVideos(videos));
|
|
3620
|
+
});
|
|
3621
|
+
}
|
|
3622
|
+
}),
|
|
3623
|
+
"episode-groups": defineCommand({
|
|
3624
|
+
meta: {
|
|
3625
|
+
name: "episode-groups",
|
|
3626
|
+
description: "List alternate episode groupings for a TV show"
|
|
3627
|
+
},
|
|
3628
|
+
args: {
|
|
3629
|
+
...commonArgs,
|
|
3630
|
+
id: {
|
|
3631
|
+
type: "positional",
|
|
3632
|
+
description: "TMDB TV show ID",
|
|
3633
|
+
required: true
|
|
3634
|
+
}
|
|
3635
|
+
},
|
|
3636
|
+
async run({ args }) {
|
|
3637
|
+
await runSafely(args, async (output) => {
|
|
3638
|
+
const id = parseId(args.id, "TV show ID");
|
|
3639
|
+
const client = createClient({ language: args.lang });
|
|
3640
|
+
const groups = await client.tv.episodeGroups(id);
|
|
3641
|
+
printResult(groups, output, () => printEpisodeGroups(groups));
|
|
3642
|
+
});
|
|
3643
|
+
}
|
|
3644
|
+
}),
|
|
3645
|
+
"episode-group": defineCommand({
|
|
3646
|
+
meta: {
|
|
3647
|
+
name: "episode-group",
|
|
3648
|
+
description: "Show an alternate episode group"
|
|
3649
|
+
},
|
|
3650
|
+
args: {
|
|
3651
|
+
...commonArgs,
|
|
3652
|
+
"group-id": {
|
|
3653
|
+
type: "positional",
|
|
3654
|
+
description: "TMDB episode group ID",
|
|
3655
|
+
required: true
|
|
3656
|
+
}
|
|
3657
|
+
},
|
|
3658
|
+
async run({ args }) {
|
|
3659
|
+
await runSafely(args, async (output) => {
|
|
3660
|
+
const groupId = parseGroupId(args["group-id"]);
|
|
3661
|
+
const client = createClient({ language: args.lang });
|
|
3662
|
+
const group = await client.tv.episodeGroup(groupId);
|
|
3663
|
+
printResult(group, output, () => printEpisodeGroup(group));
|
|
3043
3664
|
});
|
|
3044
3665
|
}
|
|
3045
3666
|
})
|
|
@@ -3082,12 +3703,82 @@ var AUTHENTICATION_HELP = [
|
|
|
3082
3703
|
`);
|
|
3083
3704
|
|
|
3084
3705
|
// src/cli/main.ts
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3706
|
+
var rawArgs = process.argv.slice(2);
|
|
3707
|
+
if (rawArgs.length === 0) {
|
|
3708
|
+
await showFullUsage(mainCommand);
|
|
3709
|
+
process.exitCode = 1;
|
|
3710
|
+
} else if (rawArgs.some((arg) => arg === "--help" || arg === "-h")) {
|
|
3711
|
+
await showFullUsage(await resolveCommand(rawArgs));
|
|
3712
|
+
} else if (isVersionRequest(rawArgs)) {
|
|
3713
|
+
process.stdout.write(`${CLI_VERSION}
|
|
3714
|
+
`);
|
|
3715
|
+
} else {
|
|
3716
|
+
try {
|
|
3717
|
+
await runCommand(mainCommand, { rawArgs });
|
|
3718
|
+
} catch (error) {
|
|
3719
|
+
reportUsageError(error, rawArgs);
|
|
3720
|
+
}
|
|
3721
|
+
}
|
|
3722
|
+
function reportUsageError(error, args) {
|
|
3723
|
+
const output = outputFromArgs(args);
|
|
3724
|
+
const message = stripAnsi(error instanceof Error ? error.message : String(error));
|
|
3725
|
+
const usageError = new CliError(message, 2);
|
|
3726
|
+
if (output.format === "json") {
|
|
3727
|
+
process.stderr.write(`${JSON.stringify(toErrorRecord(usageError))}
|
|
3728
|
+
`);
|
|
3729
|
+
} else {
|
|
3730
|
+
process.stderr.write(`${message}
|
|
3731
|
+
`);
|
|
3732
|
+
}
|
|
3733
|
+
process.exitCode = 2;
|
|
3734
|
+
}
|
|
3735
|
+
function outputFromArgs(args) {
|
|
3736
|
+
const outputArgs = {};
|
|
3737
|
+
for (let index = 0;index < args.length; index += 1) {
|
|
3738
|
+
const arg = args[index];
|
|
3739
|
+
if (arg === "--json") {
|
|
3740
|
+
outputArgs.json = true;
|
|
3741
|
+
} else if (arg === "--compact") {
|
|
3742
|
+
outputArgs.compact = true;
|
|
3743
|
+
} else if (arg === "--format") {
|
|
3744
|
+
outputArgs.format = args[index + 1];
|
|
3745
|
+
index += 1;
|
|
3746
|
+
} else if (arg.startsWith("--format=")) {
|
|
3747
|
+
outputArgs.format = arg.slice("--format=".length);
|
|
3748
|
+
}
|
|
3749
|
+
}
|
|
3750
|
+
try {
|
|
3751
|
+
return resolveOutput(outputArgs);
|
|
3752
|
+
} catch {
|
|
3753
|
+
return { compact: false, format: "text" };
|
|
3754
|
+
}
|
|
3755
|
+
}
|
|
3756
|
+
function isVersionRequest(args) {
|
|
3757
|
+
return args.length === 1 && (args[0] === "--version" || args[0] === "-v");
|
|
3758
|
+
}
|
|
3759
|
+
function stripAnsi(value) {
|
|
3760
|
+
return value.replace(/\u001B\[[0-9;]*m/g, "");
|
|
3761
|
+
}
|
|
3762
|
+
async function resolveCommand(args) {
|
|
3763
|
+
let command = mainCommand;
|
|
3764
|
+
for (const arg of args) {
|
|
3765
|
+
if (arg.startsWith("-")) {
|
|
3766
|
+
continue;
|
|
3767
|
+
}
|
|
3768
|
+
const subCommands = await resolveValue2(command.subCommands);
|
|
3769
|
+
const next = subCommands?.[arg];
|
|
3770
|
+
if (next === undefined) {
|
|
3771
|
+
break;
|
|
3772
|
+
}
|
|
3773
|
+
command = await resolveValue2(next);
|
|
3774
|
+
}
|
|
3775
|
+
return command;
|
|
3776
|
+
}
|
|
3777
|
+
async function showFullUsage(command) {
|
|
3778
|
+
const path = await findPath(mainCommand, command);
|
|
3779
|
+
if (!path || path.length === 0) {
|
|
3780
|
+
const usage = await renderUsage(command);
|
|
3781
|
+
const authentication = command === mainCommand ? `
|
|
3091
3782
|
|
|
3092
3783
|
${AUTHENTICATION_HELP}` : "";
|
|
3093
3784
|
process.stdout.write(`${usage}${authentication}
|
|
@@ -3095,10 +3786,10 @@ ${AUTHENTICATION_HELP}` : "";
|
|
|
3095
3786
|
return;
|
|
3096
3787
|
}
|
|
3097
3788
|
const parentName = ["tmdb", ...path.slice(0, -1)].join(" ");
|
|
3098
|
-
const
|
|
3789
|
+
const parent = defineCommand({
|
|
3099
3790
|
meta: { name: parentName, version: CLI_VERSION }
|
|
3100
3791
|
});
|
|
3101
|
-
process.stdout.write(`${await renderUsage(
|
|
3792
|
+
process.stdout.write(`${await renderUsage(command, parent)}
|
|
3102
3793
|
`);
|
|
3103
3794
|
}
|
|
3104
3795
|
async function findPath(root, target, path = []) {
|