tsarr 2.9.1 → 2.11.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/LICENSE +21 -0
- package/README.md +120 -233
- package/dist/cli/commands/doctor.d.ts.map +1 -1
- package/dist/cli/commands/manual-import.d.ts +30 -0
- package/dist/cli/commands/manual-import.d.ts.map +1 -0
- package/dist/cli/commands/radarr.d.ts.map +1 -1
- package/dist/cli/commands/sonarr.d.ts.map +1 -1
- package/dist/cli/index.js +5230 -4783
- package/dist/cli/index.js.map +133 -0
- package/dist/clients/base.d.ts +8 -3
- package/dist/clients/base.d.ts.map +1 -1
- package/dist/clients/bazarr.d.ts +125 -124
- package/dist/clients/bazarr.d.ts.map +1 -1
- package/dist/clients/bazarr.js +183 -100
- package/dist/clients/bazarr.js.map +22 -0
- package/dist/clients/lidarr.d.ts +148 -147
- package/dist/clients/lidarr.d.ts.map +1 -1
- package/dist/clients/lidarr.js +190 -102
- package/dist/clients/lidarr.js.map +23 -0
- package/dist/clients/prowlarr.d.ts +30 -29
- package/dist/clients/prowlarr.d.ts.map +1 -1
- package/dist/clients/prowlarr.js +190 -102
- package/dist/clients/prowlarr.js.map +23 -0
- package/dist/clients/qbittorrent.d.ts +3 -1
- package/dist/clients/qbittorrent.d.ts.map +1 -1
- package/dist/clients/qbittorrent.js +213 -107
- package/dist/clients/qbittorrent.js.map +21 -0
- package/dist/clients/radarr.d.ts +195 -150
- package/dist/clients/radarr.d.ts.map +1 -1
- package/dist/clients/radarr.js +230 -102
- package/dist/clients/radarr.js.map +23 -0
- package/dist/clients/readarr.d.ts +150 -149
- package/dist/clients/readarr.d.ts.map +1 -1
- package/dist/clients/readarr.js +190 -102
- package/dist/clients/readarr.js.map +23 -0
- package/dist/clients/seerr-types.d.ts +1 -1
- package/dist/clients/seerr-types.d.ts.map +1 -1
- package/dist/clients/seerr.d.ts +17 -16
- package/dist/clients/seerr.d.ts.map +1 -1
- package/dist/clients/seerr.js +183 -100
- package/dist/clients/seerr.js.map +22 -0
- package/dist/clients/sonarr.d.ts +203 -168
- package/dist/clients/sonarr.d.ts.map +1 -1
- package/dist/clients/sonarr.js +206 -112
- package/dist/clients/sonarr.js.map +23 -0
- package/dist/core/client.d.ts +2 -0
- package/dist/core/client.d.ts.map +1 -1
- package/dist/core/fetch.d.ts +23 -0
- package/dist/core/fetch.d.ts.map +1 -0
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/types.d.ts +7 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/generated/bazarr/client/client.gen.d.ts.map +1 -1
- package/dist/generated/bazarr/client/types.gen.d.ts +5 -2
- package/dist/generated/bazarr/client/types.gen.d.ts.map +1 -1
- package/dist/generated/bazarr/client/utils.gen.d.ts +5 -1
- package/dist/generated/bazarr/client/utils.gen.d.ts.map +1 -1
- package/dist/generated/lidarr/client/client.gen.d.ts.map +1 -1
- package/dist/generated/lidarr/client/types.gen.d.ts +5 -2
- package/dist/generated/lidarr/client/types.gen.d.ts.map +1 -1
- package/dist/generated/lidarr/client/utils.gen.d.ts +5 -1
- package/dist/generated/lidarr/client/utils.gen.d.ts.map +1 -1
- package/dist/generated/prowlarr/client/client.gen.d.ts.map +1 -1
- package/dist/generated/prowlarr/client/types.gen.d.ts +5 -2
- package/dist/generated/prowlarr/client/types.gen.d.ts.map +1 -1
- package/dist/generated/prowlarr/client/utils.gen.d.ts +5 -1
- package/dist/generated/prowlarr/client/utils.gen.d.ts.map +1 -1
- package/dist/generated/qbittorrent/client/client.gen.d.ts.map +1 -1
- package/dist/generated/qbittorrent/client/types.gen.d.ts +5 -2
- package/dist/generated/qbittorrent/client/types.gen.d.ts.map +1 -1
- package/dist/generated/qbittorrent/client/utils.gen.d.ts +5 -1
- package/dist/generated/qbittorrent/client/utils.gen.d.ts.map +1 -1
- package/dist/generated/radarr/client/client.gen.d.ts.map +1 -1
- package/dist/generated/radarr/client/types.gen.d.ts +5 -2
- package/dist/generated/radarr/client/types.gen.d.ts.map +1 -1
- package/dist/generated/radarr/client/utils.gen.d.ts +5 -1
- package/dist/generated/radarr/client/utils.gen.d.ts.map +1 -1
- package/dist/generated/readarr/client/client.gen.d.ts.map +1 -1
- package/dist/generated/readarr/client/types.gen.d.ts +5 -2
- package/dist/generated/readarr/client/types.gen.d.ts.map +1 -1
- package/dist/generated/readarr/client/utils.gen.d.ts +5 -1
- package/dist/generated/readarr/client/utils.gen.d.ts.map +1 -1
- package/dist/generated/seerr/client/client.gen.d.ts.map +1 -1
- package/dist/generated/seerr/client/types.gen.d.ts +5 -2
- package/dist/generated/seerr/client/types.gen.d.ts.map +1 -1
- package/dist/generated/seerr/client/utils.gen.d.ts +5 -1
- package/dist/generated/seerr/client/utils.gen.d.ts.map +1 -1
- package/dist/generated/seerr/index.d.ts +2 -2
- package/dist/generated/seerr/index.d.ts.map +1 -1
- package/dist/generated/seerr/sdk.gen.d.ts +13 -1
- package/dist/generated/seerr/sdk.gen.d.ts.map +1 -1
- package/dist/generated/seerr/types.gen.d.ts +78 -7
- package/dist/generated/seerr/types.gen.d.ts.map +1 -1
- package/dist/generated/sonarr/client/client.gen.d.ts.map +1 -1
- package/dist/generated/sonarr/client/types.gen.d.ts +5 -2
- package/dist/generated/sonarr/client/types.gen.d.ts.map +1 -1
- package/dist/generated/sonarr/client/utils.gen.d.ts +5 -1
- package/dist/generated/sonarr/client/utils.gen.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +12 -0
- package/dist/tsarr-2.11.0.tgz +0 -0
- package/package.json +18 -9
- package/dist/tsarr-2.9.1.tgz +0 -0
package/dist/clients/bazarr.js
CHANGED
|
@@ -26,6 +26,93 @@ class ConnectionError extends TsarrError {
|
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
// src/core/fetch.ts
|
|
30
|
+
var DEFAULT_TIMEOUT = 30000;
|
|
31
|
+
var DEFAULT_MAX_RETRIES = 3;
|
|
32
|
+
var DEFAULT_INITIAL_DELAY = 1000;
|
|
33
|
+
var DEFAULT_MAX_DELAY = 1e4;
|
|
34
|
+
var RETRYABLE_STATUS_CODES = new Set([408, 429, 502, 503, 504]);
|
|
35
|
+
function isRetryable(error) {
|
|
36
|
+
if (error instanceof DOMException && error.name === "AbortError") {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
if (error instanceof TypeError) {
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
function getRetryDelay(attempt, initialDelayMs, maxDelayMs) {
|
|
45
|
+
const delay = initialDelayMs * 2 ** attempt;
|
|
46
|
+
const jitter = delay * 0.2 * Math.random();
|
|
47
|
+
return Math.min(delay + jitter, maxDelayMs);
|
|
48
|
+
}
|
|
49
|
+
function createResilientFetch(options = {}) {
|
|
50
|
+
const timeout = options.timeout ?? DEFAULT_TIMEOUT;
|
|
51
|
+
const maxRetries = options.retry ? options.retry.maxRetries ?? DEFAULT_MAX_RETRIES : 0;
|
|
52
|
+
const initialDelayMs = options.retry?.initialDelayMs ?? DEFAULT_INITIAL_DELAY;
|
|
53
|
+
const maxDelayMs = options.retry?.maxDelayMs ?? DEFAULT_MAX_DELAY;
|
|
54
|
+
const resilientFetch = async (input, init) => {
|
|
55
|
+
let lastError;
|
|
56
|
+
const template = createRequestTemplate(input, init);
|
|
57
|
+
for (let attempt = 0;attempt <= maxRetries; attempt++) {
|
|
58
|
+
const controller = new AbortController;
|
|
59
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
60
|
+
const callerSignal = init?.signal;
|
|
61
|
+
if (callerSignal?.aborted) {
|
|
62
|
+
clearTimeout(timeoutId);
|
|
63
|
+
throw callerSignal.reason ?? new DOMException("The operation was aborted.", "AbortError");
|
|
64
|
+
}
|
|
65
|
+
const onCallerAbort = () => controller.abort(callerSignal.reason);
|
|
66
|
+
callerSignal?.addEventListener("abort", onCallerAbort, { once: true });
|
|
67
|
+
try {
|
|
68
|
+
const response = await globalThis.fetch(new Request(template.clone(), { signal: controller.signal }));
|
|
69
|
+
clearTimeout(timeoutId);
|
|
70
|
+
callerSignal?.removeEventListener("abort", onCallerAbort);
|
|
71
|
+
if (RETRYABLE_STATUS_CODES.has(response.status) && attempt < maxRetries) {
|
|
72
|
+
lastError = new ConnectionError(`Request failed with status ${response.status}`);
|
|
73
|
+
const delay = getRetryDelay(attempt, initialDelayMs, maxDelayMs);
|
|
74
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
return response;
|
|
78
|
+
} catch (error) {
|
|
79
|
+
clearTimeout(timeoutId);
|
|
80
|
+
callerSignal?.removeEventListener("abort", onCallerAbort);
|
|
81
|
+
if (callerSignal?.aborted) {
|
|
82
|
+
throw callerSignal.reason ?? new DOMException("The operation was aborted.", "AbortError");
|
|
83
|
+
}
|
|
84
|
+
if (error instanceof DOMException && error.name === "AbortError") {
|
|
85
|
+
lastError = new ConnectionError(`Request timed out after ${timeout}ms`);
|
|
86
|
+
if (attempt < maxRetries) {
|
|
87
|
+
const delay = getRetryDelay(attempt, initialDelayMs, maxDelayMs);
|
|
88
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
throw lastError;
|
|
92
|
+
}
|
|
93
|
+
if (isRetryable(error) && attempt < maxRetries) {
|
|
94
|
+
lastError = error;
|
|
95
|
+
const delay = getRetryDelay(attempt, initialDelayMs, maxDelayMs);
|
|
96
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
throw error;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
throw lastError;
|
|
103
|
+
};
|
|
104
|
+
return Object.assign(resilientFetch, {
|
|
105
|
+
preconnect: globalThis.fetch.preconnect?.bind(globalThis.fetch)
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
function createRequestTemplate(input, init) {
|
|
109
|
+
const { signal: _signal, ...requestInit } = init ?? {};
|
|
110
|
+
if (input instanceof Request) {
|
|
111
|
+
return init ? new Request(input.clone(), requestInit) : input.clone();
|
|
112
|
+
}
|
|
113
|
+
return new Request(input, requestInit);
|
|
114
|
+
}
|
|
115
|
+
|
|
29
116
|
// src/core/client.ts
|
|
30
117
|
var DEFAULT_TIMEOUT_MS = 30000;
|
|
31
118
|
function createServarrClient(config) {
|
|
@@ -40,6 +127,10 @@ function createServarrClient(config) {
|
|
|
40
127
|
baseUrl: config.baseUrl.replace(/\/$/, "")
|
|
41
128
|
};
|
|
42
129
|
const timeoutMs = validatedConfig.timeout ?? DEFAULT_TIMEOUT_MS;
|
|
130
|
+
const resilientFetch = createResilientFetch({
|
|
131
|
+
timeout: timeoutMs,
|
|
132
|
+
retry: validatedConfig.retry
|
|
133
|
+
});
|
|
43
134
|
return {
|
|
44
135
|
config: validatedConfig,
|
|
45
136
|
getHeaders: () => ({
|
|
@@ -48,7 +139,8 @@ function createServarrClient(config) {
|
|
|
48
139
|
...validatedConfig.headers
|
|
49
140
|
}),
|
|
50
141
|
getBaseUrl: () => validatedConfig.baseUrl,
|
|
51
|
-
getTimeout: () => timeoutMs
|
|
142
|
+
getTimeout: () => timeoutMs,
|
|
143
|
+
getFetch: () => resilientFetch
|
|
52
144
|
};
|
|
53
145
|
}
|
|
54
146
|
|
|
@@ -694,126 +786,115 @@ var createClient = (config = {}) => {
|
|
|
694
786
|
return { opts: resolvedOpts, url };
|
|
695
787
|
};
|
|
696
788
|
const request = async (options) => {
|
|
697
|
-
const
|
|
698
|
-
const
|
|
699
|
-
|
|
700
|
-
...opts,
|
|
701
|
-
body: getValidRequestBody(opts)
|
|
702
|
-
};
|
|
703
|
-
let request2 = new Request(url, requestInit);
|
|
704
|
-
for (const fn of interceptors.request.fns) {
|
|
705
|
-
if (fn) {
|
|
706
|
-
request2 = await fn(request2, opts);
|
|
707
|
-
}
|
|
708
|
-
}
|
|
709
|
-
const _fetch = opts.fetch;
|
|
789
|
+
const throwOnError = options.throwOnError ?? _config.throwOnError;
|
|
790
|
+
const responseStyle = options.responseStyle ?? _config.responseStyle;
|
|
791
|
+
let request2;
|
|
710
792
|
let response;
|
|
711
793
|
try {
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
794
|
+
const { opts, url } = await beforeRequest(options);
|
|
795
|
+
const requestInit = {
|
|
796
|
+
redirect: "follow",
|
|
797
|
+
...opts,
|
|
798
|
+
body: getValidRequestBody(opts)
|
|
799
|
+
};
|
|
800
|
+
request2 = new Request(url, requestInit);
|
|
801
|
+
for (const fn of interceptors.request.fns) {
|
|
716
802
|
if (fn) {
|
|
717
|
-
|
|
803
|
+
request2 = await fn(request2, opts);
|
|
718
804
|
}
|
|
719
805
|
}
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
806
|
+
const _fetch = opts.fetch;
|
|
807
|
+
response = await _fetch(request2);
|
|
808
|
+
for (const fn of interceptors.response.fns) {
|
|
809
|
+
if (fn) {
|
|
810
|
+
response = await fn(response, request2, opts);
|
|
811
|
+
}
|
|
723
812
|
}
|
|
724
|
-
|
|
725
|
-
error: finalError2,
|
|
813
|
+
const result = {
|
|
726
814
|
request: request2,
|
|
727
|
-
response
|
|
815
|
+
response
|
|
728
816
|
};
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
817
|
+
if (response.ok) {
|
|
818
|
+
const parseAs = (opts.parseAs === "auto" ? getParseAs(response.headers.get("Content-Type")) : opts.parseAs) ?? "json";
|
|
819
|
+
if (response.status === 204 || response.headers.get("Content-Length") === "0") {
|
|
820
|
+
let emptyData;
|
|
821
|
+
switch (parseAs) {
|
|
822
|
+
case "arrayBuffer":
|
|
823
|
+
case "blob":
|
|
824
|
+
case "text":
|
|
825
|
+
emptyData = await response[parseAs]();
|
|
826
|
+
break;
|
|
827
|
+
case "formData":
|
|
828
|
+
emptyData = new FormData;
|
|
829
|
+
break;
|
|
830
|
+
case "stream":
|
|
831
|
+
emptyData = response.body;
|
|
832
|
+
break;
|
|
833
|
+
case "json":
|
|
834
|
+
default:
|
|
835
|
+
emptyData = {};
|
|
836
|
+
break;
|
|
837
|
+
}
|
|
838
|
+
return opts.responseStyle === "data" ? emptyData : {
|
|
839
|
+
data: emptyData,
|
|
840
|
+
...result
|
|
841
|
+
};
|
|
842
|
+
}
|
|
843
|
+
let data;
|
|
743
844
|
switch (parseAs) {
|
|
744
845
|
case "arrayBuffer":
|
|
745
846
|
case "blob":
|
|
847
|
+
case "formData":
|
|
746
848
|
case "text":
|
|
747
|
-
|
|
849
|
+
data = await response[parseAs]();
|
|
748
850
|
break;
|
|
749
|
-
case "
|
|
750
|
-
|
|
851
|
+
case "json": {
|
|
852
|
+
const text = await response.text();
|
|
853
|
+
data = text ? JSON.parse(text) : {};
|
|
751
854
|
break;
|
|
855
|
+
}
|
|
752
856
|
case "stream":
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
emptyData = {};
|
|
758
|
-
break;
|
|
857
|
+
return opts.responseStyle === "data" ? response.body : {
|
|
858
|
+
data: response.body,
|
|
859
|
+
...result
|
|
860
|
+
};
|
|
759
861
|
}
|
|
760
|
-
|
|
761
|
-
|
|
862
|
+
if (parseAs === "json") {
|
|
863
|
+
if (opts.responseValidator) {
|
|
864
|
+
await opts.responseValidator(data);
|
|
865
|
+
}
|
|
866
|
+
if (opts.responseTransformer) {
|
|
867
|
+
data = await opts.responseTransformer(data);
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
return opts.responseStyle === "data" ? data : {
|
|
871
|
+
data,
|
|
762
872
|
...result
|
|
763
873
|
};
|
|
764
874
|
}
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
break;
|
|
875
|
+
const textError = await response.text();
|
|
876
|
+
let jsonError;
|
|
877
|
+
try {
|
|
878
|
+
jsonError = JSON.parse(textError);
|
|
879
|
+
} catch {}
|
|
880
|
+
throw jsonError ?? textError;
|
|
881
|
+
} catch (error) {
|
|
882
|
+
let finalError = error;
|
|
883
|
+
for (const fn of interceptors.error.fns) {
|
|
884
|
+
if (fn) {
|
|
885
|
+
finalError = await fn(finalError, response, request2, options);
|
|
777
886
|
}
|
|
778
|
-
case "stream":
|
|
779
|
-
return opts.responseStyle === "data" ? response.body : {
|
|
780
|
-
data: response.body,
|
|
781
|
-
...result
|
|
782
|
-
};
|
|
783
887
|
}
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
}
|
|
788
|
-
if (opts.responseTransformer) {
|
|
789
|
-
data = await opts.responseTransformer(data);
|
|
790
|
-
}
|
|
888
|
+
finalError = finalError || {};
|
|
889
|
+
if (throwOnError) {
|
|
890
|
+
throw finalError;
|
|
791
891
|
}
|
|
792
|
-
return
|
|
793
|
-
|
|
794
|
-
|
|
892
|
+
return responseStyle === "data" ? undefined : {
|
|
893
|
+
error: finalError,
|
|
894
|
+
request: request2,
|
|
895
|
+
response
|
|
795
896
|
};
|
|
796
897
|
}
|
|
797
|
-
const textError = await response.text();
|
|
798
|
-
let jsonError;
|
|
799
|
-
try {
|
|
800
|
-
jsonError = JSON.parse(textError);
|
|
801
|
-
} catch {}
|
|
802
|
-
const error = jsonError ?? textError;
|
|
803
|
-
let finalError = error;
|
|
804
|
-
for (const fn of interceptors.error.fns) {
|
|
805
|
-
if (fn) {
|
|
806
|
-
finalError = await fn(error, response, request2, opts);
|
|
807
|
-
}
|
|
808
|
-
}
|
|
809
|
-
finalError = finalError || {};
|
|
810
|
-
if (opts.throwOnError) {
|
|
811
|
-
throw finalError;
|
|
812
|
-
}
|
|
813
|
-
return opts.responseStyle === "data" ? undefined : {
|
|
814
|
-
error: finalError,
|
|
815
|
-
...result
|
|
816
|
-
};
|
|
817
898
|
};
|
|
818
899
|
const makeMethodFn = (method) => (options) => request({ ...options, method });
|
|
819
900
|
const makeSseFn = (method) => async (options) => {
|
|
@@ -821,7 +902,6 @@ var createClient = (config = {}) => {
|
|
|
821
902
|
return createSseClient({
|
|
822
903
|
...opts,
|
|
823
904
|
body: opts.body,
|
|
824
|
-
headers: opts.headers,
|
|
825
905
|
method,
|
|
826
906
|
onRequest: async (url2, init) => {
|
|
827
907
|
let request2 = new Request(url2, init);
|
|
@@ -1212,7 +1292,7 @@ class BazarrClient {
|
|
|
1212
1292
|
baseUrl: getBazarrApiBaseUrl(this.clientConfig.getBaseUrl()),
|
|
1213
1293
|
headers: getBazarrHeaders(this.clientConfig),
|
|
1214
1294
|
auth: this.clientConfig.config.apiKey,
|
|
1215
|
-
|
|
1295
|
+
fetch: this.clientConfig.getFetch()
|
|
1216
1296
|
});
|
|
1217
1297
|
}
|
|
1218
1298
|
async getSystemStatus() {
|
|
@@ -1554,7 +1634,7 @@ class BazarrClient {
|
|
|
1554
1634
|
baseUrl: getBazarrApiBaseUrl(this.clientConfig.getBaseUrl()),
|
|
1555
1635
|
headers: getBazarrHeaders(this.clientConfig),
|
|
1556
1636
|
auth: this.clientConfig.config.apiKey,
|
|
1557
|
-
|
|
1637
|
+
fetch: this.clientConfig.getFetch()
|
|
1558
1638
|
});
|
|
1559
1639
|
return this.clientConfig.config;
|
|
1560
1640
|
}
|
|
@@ -1562,3 +1642,6 @@ class BazarrClient {
|
|
|
1562
1642
|
export {
|
|
1563
1643
|
BazarrClient
|
|
1564
1644
|
};
|
|
1645
|
+
|
|
1646
|
+
//# debugId=2BD161259ECE024964756E2164756E21
|
|
1647
|
+
//# sourceMappingURL=bazarr.js.map
|