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
|
@@ -18,6 +18,93 @@ class ConnectionError extends TsarrError {
|
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
// src/core/fetch.ts
|
|
22
|
+
var DEFAULT_TIMEOUT = 30000;
|
|
23
|
+
var DEFAULT_MAX_RETRIES = 3;
|
|
24
|
+
var DEFAULT_INITIAL_DELAY = 1000;
|
|
25
|
+
var DEFAULT_MAX_DELAY = 1e4;
|
|
26
|
+
var RETRYABLE_STATUS_CODES = new Set([408, 429, 502, 503, 504]);
|
|
27
|
+
function isRetryable(error) {
|
|
28
|
+
if (error instanceof DOMException && error.name === "AbortError") {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
if (error instanceof TypeError) {
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
function getRetryDelay(attempt, initialDelayMs, maxDelayMs) {
|
|
37
|
+
const delay = initialDelayMs * 2 ** attempt;
|
|
38
|
+
const jitter = delay * 0.2 * Math.random();
|
|
39
|
+
return Math.min(delay + jitter, maxDelayMs);
|
|
40
|
+
}
|
|
41
|
+
function createResilientFetch(options = {}) {
|
|
42
|
+
const timeout = options.timeout ?? DEFAULT_TIMEOUT;
|
|
43
|
+
const maxRetries = options.retry ? options.retry.maxRetries ?? DEFAULT_MAX_RETRIES : 0;
|
|
44
|
+
const initialDelayMs = options.retry?.initialDelayMs ?? DEFAULT_INITIAL_DELAY;
|
|
45
|
+
const maxDelayMs = options.retry?.maxDelayMs ?? DEFAULT_MAX_DELAY;
|
|
46
|
+
const resilientFetch = async (input, init) => {
|
|
47
|
+
let lastError;
|
|
48
|
+
const template = createRequestTemplate(input, init);
|
|
49
|
+
for (let attempt = 0;attempt <= maxRetries; attempt++) {
|
|
50
|
+
const controller = new AbortController;
|
|
51
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
52
|
+
const callerSignal = init?.signal;
|
|
53
|
+
if (callerSignal?.aborted) {
|
|
54
|
+
clearTimeout(timeoutId);
|
|
55
|
+
throw callerSignal.reason ?? new DOMException("The operation was aborted.", "AbortError");
|
|
56
|
+
}
|
|
57
|
+
const onCallerAbort = () => controller.abort(callerSignal.reason);
|
|
58
|
+
callerSignal?.addEventListener("abort", onCallerAbort, { once: true });
|
|
59
|
+
try {
|
|
60
|
+
const response = await globalThis.fetch(new Request(template.clone(), { signal: controller.signal }));
|
|
61
|
+
clearTimeout(timeoutId);
|
|
62
|
+
callerSignal?.removeEventListener("abort", onCallerAbort);
|
|
63
|
+
if (RETRYABLE_STATUS_CODES.has(response.status) && attempt < maxRetries) {
|
|
64
|
+
lastError = new ConnectionError(`Request failed with status ${response.status}`);
|
|
65
|
+
const delay = getRetryDelay(attempt, initialDelayMs, maxDelayMs);
|
|
66
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
return response;
|
|
70
|
+
} catch (error) {
|
|
71
|
+
clearTimeout(timeoutId);
|
|
72
|
+
callerSignal?.removeEventListener("abort", onCallerAbort);
|
|
73
|
+
if (callerSignal?.aborted) {
|
|
74
|
+
throw callerSignal.reason ?? new DOMException("The operation was aborted.", "AbortError");
|
|
75
|
+
}
|
|
76
|
+
if (error instanceof DOMException && error.name === "AbortError") {
|
|
77
|
+
lastError = new ConnectionError(`Request timed out after ${timeout}ms`);
|
|
78
|
+
if (attempt < maxRetries) {
|
|
79
|
+
const delay = getRetryDelay(attempt, initialDelayMs, maxDelayMs);
|
|
80
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
throw lastError;
|
|
84
|
+
}
|
|
85
|
+
if (isRetryable(error) && attempt < maxRetries) {
|
|
86
|
+
lastError = error;
|
|
87
|
+
const delay = getRetryDelay(attempt, initialDelayMs, maxDelayMs);
|
|
88
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
throw error;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
throw lastError;
|
|
95
|
+
};
|
|
96
|
+
return Object.assign(resilientFetch, {
|
|
97
|
+
preconnect: globalThis.fetch.preconnect?.bind(globalThis.fetch)
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
function createRequestTemplate(input, init) {
|
|
101
|
+
const { signal: _signal, ...requestInit } = init ?? {};
|
|
102
|
+
if (input instanceof Request) {
|
|
103
|
+
return init ? new Request(input.clone(), requestInit) : input.clone();
|
|
104
|
+
}
|
|
105
|
+
return new Request(input, requestInit);
|
|
106
|
+
}
|
|
107
|
+
|
|
21
108
|
// src/generated/qbittorrent/core/bodySerializer.gen.ts
|
|
22
109
|
var serializeUrlSearchParamsPair = (data, key, value) => {
|
|
23
110
|
if (typeof value === "string") {
|
|
@@ -658,126 +745,115 @@ var createClient = (config = {}) => {
|
|
|
658
745
|
return { opts: resolvedOpts, url };
|
|
659
746
|
};
|
|
660
747
|
const request = async (options) => {
|
|
661
|
-
const
|
|
662
|
-
const
|
|
663
|
-
|
|
664
|
-
...opts,
|
|
665
|
-
body: getValidRequestBody(opts)
|
|
666
|
-
};
|
|
667
|
-
let request2 = new Request(url, requestInit);
|
|
668
|
-
for (const fn of interceptors.request.fns) {
|
|
669
|
-
if (fn) {
|
|
670
|
-
request2 = await fn(request2, opts);
|
|
671
|
-
}
|
|
672
|
-
}
|
|
673
|
-
const _fetch = opts.fetch;
|
|
748
|
+
const throwOnError = options.throwOnError ?? _config.throwOnError;
|
|
749
|
+
const responseStyle = options.responseStyle ?? _config.responseStyle;
|
|
750
|
+
let request2;
|
|
674
751
|
let response;
|
|
675
752
|
try {
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
753
|
+
const { opts, url } = await beforeRequest(options);
|
|
754
|
+
const requestInit = {
|
|
755
|
+
redirect: "follow",
|
|
756
|
+
...opts,
|
|
757
|
+
body: getValidRequestBody(opts)
|
|
758
|
+
};
|
|
759
|
+
request2 = new Request(url, requestInit);
|
|
760
|
+
for (const fn of interceptors.request.fns) {
|
|
680
761
|
if (fn) {
|
|
681
|
-
|
|
762
|
+
request2 = await fn(request2, opts);
|
|
682
763
|
}
|
|
683
764
|
}
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
765
|
+
const _fetch = opts.fetch;
|
|
766
|
+
response = await _fetch(request2);
|
|
767
|
+
for (const fn of interceptors.response.fns) {
|
|
768
|
+
if (fn) {
|
|
769
|
+
response = await fn(response, request2, opts);
|
|
770
|
+
}
|
|
687
771
|
}
|
|
688
|
-
|
|
689
|
-
error: finalError2,
|
|
772
|
+
const result = {
|
|
690
773
|
request: request2,
|
|
691
|
-
response
|
|
774
|
+
response
|
|
692
775
|
};
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
776
|
+
if (response.ok) {
|
|
777
|
+
const parseAs = (opts.parseAs === "auto" ? getParseAs(response.headers.get("Content-Type")) : opts.parseAs) ?? "json";
|
|
778
|
+
if (response.status === 204 || response.headers.get("Content-Length") === "0") {
|
|
779
|
+
let emptyData;
|
|
780
|
+
switch (parseAs) {
|
|
781
|
+
case "arrayBuffer":
|
|
782
|
+
case "blob":
|
|
783
|
+
case "text":
|
|
784
|
+
emptyData = await response[parseAs]();
|
|
785
|
+
break;
|
|
786
|
+
case "formData":
|
|
787
|
+
emptyData = new FormData;
|
|
788
|
+
break;
|
|
789
|
+
case "stream":
|
|
790
|
+
emptyData = response.body;
|
|
791
|
+
break;
|
|
792
|
+
case "json":
|
|
793
|
+
default:
|
|
794
|
+
emptyData = {};
|
|
795
|
+
break;
|
|
796
|
+
}
|
|
797
|
+
return opts.responseStyle === "data" ? emptyData : {
|
|
798
|
+
data: emptyData,
|
|
799
|
+
...result
|
|
800
|
+
};
|
|
801
|
+
}
|
|
802
|
+
let data;
|
|
707
803
|
switch (parseAs) {
|
|
708
804
|
case "arrayBuffer":
|
|
709
805
|
case "blob":
|
|
806
|
+
case "formData":
|
|
710
807
|
case "text":
|
|
711
|
-
|
|
808
|
+
data = await response[parseAs]();
|
|
712
809
|
break;
|
|
713
|
-
case "
|
|
714
|
-
|
|
810
|
+
case "json": {
|
|
811
|
+
const text = await response.text();
|
|
812
|
+
data = text ? JSON.parse(text) : {};
|
|
715
813
|
break;
|
|
814
|
+
}
|
|
716
815
|
case "stream":
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
816
|
+
return opts.responseStyle === "data" ? response.body : {
|
|
817
|
+
data: response.body,
|
|
818
|
+
...result
|
|
819
|
+
};
|
|
820
|
+
}
|
|
821
|
+
if (parseAs === "json") {
|
|
822
|
+
if (opts.responseValidator) {
|
|
823
|
+
await opts.responseValidator(data);
|
|
824
|
+
}
|
|
825
|
+
if (opts.responseTransformer) {
|
|
826
|
+
data = await opts.responseTransformer(data);
|
|
827
|
+
}
|
|
723
828
|
}
|
|
724
|
-
return opts.responseStyle === "data" ?
|
|
725
|
-
data
|
|
829
|
+
return opts.responseStyle === "data" ? data : {
|
|
830
|
+
data,
|
|
726
831
|
...result
|
|
727
832
|
};
|
|
728
833
|
}
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
break;
|
|
834
|
+
const textError = await response.text();
|
|
835
|
+
let jsonError;
|
|
836
|
+
try {
|
|
837
|
+
jsonError = JSON.parse(textError);
|
|
838
|
+
} catch {}
|
|
839
|
+
throw jsonError ?? textError;
|
|
840
|
+
} catch (error) {
|
|
841
|
+
let finalError = error;
|
|
842
|
+
for (const fn of interceptors.error.fns) {
|
|
843
|
+
if (fn) {
|
|
844
|
+
finalError = await fn(finalError, response, request2, options);
|
|
741
845
|
}
|
|
742
|
-
case "stream":
|
|
743
|
-
return opts.responseStyle === "data" ? response.body : {
|
|
744
|
-
data: response.body,
|
|
745
|
-
...result
|
|
746
|
-
};
|
|
747
846
|
}
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
}
|
|
752
|
-
if (opts.responseTransformer) {
|
|
753
|
-
data = await opts.responseTransformer(data);
|
|
754
|
-
}
|
|
847
|
+
finalError = finalError || {};
|
|
848
|
+
if (throwOnError) {
|
|
849
|
+
throw finalError;
|
|
755
850
|
}
|
|
756
|
-
return
|
|
757
|
-
|
|
758
|
-
|
|
851
|
+
return responseStyle === "data" ? undefined : {
|
|
852
|
+
error: finalError,
|
|
853
|
+
request: request2,
|
|
854
|
+
response
|
|
759
855
|
};
|
|
760
856
|
}
|
|
761
|
-
const textError = await response.text();
|
|
762
|
-
let jsonError;
|
|
763
|
-
try {
|
|
764
|
-
jsonError = JSON.parse(textError);
|
|
765
|
-
} catch {}
|
|
766
|
-
const error = jsonError ?? textError;
|
|
767
|
-
let finalError = error;
|
|
768
|
-
for (const fn of interceptors.error.fns) {
|
|
769
|
-
if (fn) {
|
|
770
|
-
finalError = await fn(error, response, request2, opts);
|
|
771
|
-
}
|
|
772
|
-
}
|
|
773
|
-
finalError = finalError || {};
|
|
774
|
-
if (opts.throwOnError) {
|
|
775
|
-
throw finalError;
|
|
776
|
-
}
|
|
777
|
-
return opts.responseStyle === "data" ? undefined : {
|
|
778
|
-
error: finalError,
|
|
779
|
-
...result
|
|
780
|
-
};
|
|
781
857
|
};
|
|
782
858
|
const makeMethodFn = (method) => (options) => request({ ...options, method });
|
|
783
859
|
const makeSseFn = (method) => async (options) => {
|
|
@@ -785,7 +861,6 @@ var createClient = (config = {}) => {
|
|
|
785
861
|
return createSseClient({
|
|
786
862
|
...opts,
|
|
787
863
|
body: opts.body,
|
|
788
|
-
headers: opts.headers,
|
|
789
864
|
method,
|
|
790
865
|
onRequest: async (url2, init) => {
|
|
791
866
|
let request2 = new Request(url2, init);
|
|
@@ -924,7 +999,8 @@ class QBittorrentClient {
|
|
|
924
999
|
username;
|
|
925
1000
|
password;
|
|
926
1001
|
sid = null;
|
|
927
|
-
|
|
1002
|
+
cookieName = "SID";
|
|
1003
|
+
fetch;
|
|
928
1004
|
constructor(config) {
|
|
929
1005
|
if (!config.baseUrl) {
|
|
930
1006
|
throw new ConnectionError("No base URL provided");
|
|
@@ -932,13 +1008,40 @@ class QBittorrentClient {
|
|
|
932
1008
|
this.baseUrl = config.baseUrl.replace(/\/$/, "");
|
|
933
1009
|
this.username = config.username;
|
|
934
1010
|
this.password = config.password;
|
|
935
|
-
|
|
1011
|
+
const baseFetch = createResilientFetch({
|
|
1012
|
+
timeout: config.timeout ?? DEFAULT_TIMEOUT_MS,
|
|
1013
|
+
retry: config.retry
|
|
1014
|
+
});
|
|
1015
|
+
this.fetch = (input, init) => this.fetchWithCookieName(baseFetch, input, init);
|
|
936
1016
|
client.setConfig({
|
|
937
1017
|
baseUrl: `${this.baseUrl}/api/v2`,
|
|
938
1018
|
auth: () => this.ensureAuth(),
|
|
939
|
-
|
|
1019
|
+
fetch: this.fetch
|
|
940
1020
|
});
|
|
941
1021
|
}
|
|
1022
|
+
async fetchWithCookieName(baseFetch, input, init) {
|
|
1023
|
+
if (this.cookieName === "SID") {
|
|
1024
|
+
return baseFetch(input, init);
|
|
1025
|
+
}
|
|
1026
|
+
if (input instanceof Request) {
|
|
1027
|
+
const cookie = input.headers.get("cookie");
|
|
1028
|
+
if (cookie?.includes("SID=")) {
|
|
1029
|
+
const headers = new Headers(input.headers);
|
|
1030
|
+
headers.set("cookie", cookie.replace(/(^|;\s*)SID=/, `$1${this.cookieName}=`));
|
|
1031
|
+
return baseFetch(new Request(input, { headers }), init);
|
|
1032
|
+
}
|
|
1033
|
+
return baseFetch(input, init);
|
|
1034
|
+
}
|
|
1035
|
+
if (init?.headers) {
|
|
1036
|
+
const headers = new Headers(init.headers);
|
|
1037
|
+
const cookie = headers.get("cookie");
|
|
1038
|
+
if (cookie?.includes("SID=")) {
|
|
1039
|
+
headers.set("cookie", cookie.replace(/(^|;\s*)SID=/, `$1${this.cookieName}=`));
|
|
1040
|
+
return baseFetch(input, { ...init, headers });
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
return baseFetch(input, init);
|
|
1044
|
+
}
|
|
942
1045
|
async ensureAuth() {
|
|
943
1046
|
if (!this.sid) {
|
|
944
1047
|
await this.login();
|
|
@@ -946,7 +1049,7 @@ class QBittorrentClient {
|
|
|
946
1049
|
return this.sid;
|
|
947
1050
|
}
|
|
948
1051
|
async login() {
|
|
949
|
-
const response = await fetch(`${this.baseUrl}/api/v2/auth/login`, {
|
|
1052
|
+
const response = await this.fetch(`${this.baseUrl}/api/v2/auth/login`, {
|
|
950
1053
|
method: "POST",
|
|
951
1054
|
headers: {
|
|
952
1055
|
"Content-Type": "application/x-www-form-urlencoded",
|
|
@@ -955,22 +1058,22 @@ class QBittorrentClient {
|
|
|
955
1058
|
body: new URLSearchParams({
|
|
956
1059
|
username: this.username,
|
|
957
1060
|
password: this.password
|
|
958
|
-
})
|
|
959
|
-
signal: AbortSignal.timeout(this.timeoutMs)
|
|
1061
|
+
})
|
|
960
1062
|
});
|
|
961
1063
|
if (!response.ok) {
|
|
962
1064
|
throw new ConnectionError(`qBittorrent login failed (${response.status})`);
|
|
963
1065
|
}
|
|
964
|
-
const text = await response.text();
|
|
965
|
-
if (text
|
|
1066
|
+
const text = (await response.text()).trim();
|
|
1067
|
+
if (text && text !== "Ok.") {
|
|
966
1068
|
throw new ConnectionError("qBittorrent authentication failed: invalid username or password");
|
|
967
1069
|
}
|
|
968
1070
|
const setCookie = response.headers.get("set-cookie");
|
|
969
|
-
const sidMatch = setCookie?.match(/SID=([^;]+)/);
|
|
1071
|
+
const sidMatch = setCookie?.match(/(SID|QBT_SID_\d+)=([^;]+)/);
|
|
970
1072
|
if (!sidMatch) {
|
|
971
1073
|
throw new ConnectionError("qBittorrent login succeeded but no SID cookie received");
|
|
972
1074
|
}
|
|
973
|
-
this.
|
|
1075
|
+
this.cookieName = sidMatch[1];
|
|
1076
|
+
this.sid = sidMatch[2];
|
|
974
1077
|
}
|
|
975
1078
|
async getAppVersion() {
|
|
976
1079
|
const result = await appVersionGet();
|
|
@@ -1015,3 +1118,6 @@ class QBittorrentClient {
|
|
|
1015
1118
|
export {
|
|
1016
1119
|
QBittorrentClient
|
|
1017
1120
|
};
|
|
1121
|
+
|
|
1122
|
+
//# debugId=EEFE0D3C2E02FFD464756E2164756E21
|
|
1123
|
+
//# sourceMappingURL=qbittorrent.js.map
|