@muhammedaksam/easiarr 1.4.0 → 1.4.1
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/dist/index.js +29837 -0
- package/package.json +15 -15
- package/src/VersionInfo.ts +0 -12
- package/src/api/arr-api.ts +0 -521
- package/src/api/auto-setup-types.ts +0 -64
- package/src/api/base-api.ts +0 -216
- package/src/api/bazarr-api.ts +0 -297
- package/src/api/cloudflare-api.ts +0 -493
- package/src/api/custom-format-api.ts +0 -224
- package/src/api/grafana-api.ts +0 -230
- package/src/api/heimdall-api.ts +0 -141
- package/src/api/homarr-api.ts +0 -209
- package/src/api/huntarr-api.ts +0 -457
- package/src/api/index.ts +0 -9
- package/src/api/jellyfin-api.ts +0 -389
- package/src/api/jellyseerr-api.ts +0 -559
- package/src/api/maintainerr-api.ts +0 -461
- package/src/api/naming-config.ts +0 -101
- package/src/api/overseerr-api.ts +0 -392
- package/src/api/plex-api.ts +0 -308
- package/src/api/portainer-api.ts +0 -374
- package/src/api/profilarr-api.ts +0 -391
- package/src/api/prowlarr-api.ts +0 -488
- package/src/api/qbittorrent-api.ts +0 -300
- package/src/api/quality-profile-api.ts +0 -272
- package/src/api/tautulli-api.ts +0 -244
- package/src/api/uptime-kuma-api.ts +0 -343
- package/src/apps/categories.ts +0 -14
- package/src/apps/index.ts +0 -2
- package/src/apps/registry.ts +0 -1275
- package/src/compose/caddy-config.ts +0 -130
- package/src/compose/generator.ts +0 -287
- package/src/compose/index.ts +0 -7
- package/src/compose/templates.ts +0 -78
- package/src/compose/traefik-config.ts +0 -187
- package/src/config/bookmarks-generator.ts +0 -160
- package/src/config/defaults.ts +0 -37
- package/src/config/homepage-config.ts +0 -381
- package/src/config/index.ts +0 -6
- package/src/config/manager.ts +0 -150
- package/src/config/recyclarr-config.ts +0 -181
- package/src/config/schema.ts +0 -310
- package/src/config/slskd-config.ts +0 -86
- package/src/config/soularr-config.ts +0 -105
- package/src/config/trash-quality-definitions.ts +0 -213
- package/src/data/lidarr-custom-formats.ts +0 -127
- package/src/data/trash-profiles.ts +0 -333
- package/src/docker/client.ts +0 -312
- package/src/docker/index.ts +0 -18
- package/src/index.ts +0 -77
- package/src/setup/actions/arr-common.ts +0 -307
- package/src/setup/actions/dashboards.ts +0 -79
- package/src/setup/actions/download-clients.ts +0 -54
- package/src/setup/actions/jellyseerr.ts +0 -331
- package/src/setup/actions/media-servers.ts +0 -68
- package/src/setup/actions/monitoring.ts +0 -120
- package/src/setup/actions/prowlarr.ts +0 -137
- package/src/setup/actions/utilities.ts +0 -490
- package/src/setup/index.ts +0 -78
- package/src/setup/types.ts +0 -144
- package/src/structure/manager.ts +0 -129
- package/src/ui/App.ts +0 -111
- package/src/ui/components/ApplicationSelector.ts +0 -275
- package/src/ui/components/CredentialsForm.ts +0 -180
- package/src/ui/components/DownloadClientForm.ts +0 -114
- package/src/ui/components/FileEditor.ts +0 -91
- package/src/ui/components/FooterHint.ts +0 -119
- package/src/ui/components/PageLayout.ts +0 -160
- package/src/ui/components/UpdateNotification.ts +0 -102
- package/src/ui/index.ts +0 -7
- package/src/ui/screens/AdvancedSettings.ts +0 -188
- package/src/ui/screens/ApiKeyViewer.ts +0 -511
- package/src/ui/screens/AppConfigurator.ts +0 -543
- package/src/ui/screens/AppManager.ts +0 -284
- package/src/ui/screens/ContainerControl.ts +0 -581
- package/src/ui/screens/LogsViewer.ts +0 -478
- package/src/ui/screens/MainMenu.ts +0 -299
- package/src/ui/screens/MonitorDashboard.ts +0 -881
- package/src/ui/screens/SecretsEditor.ts +0 -216
- package/src/ui/screens/SettingsScreen.ts +0 -633
- package/src/ui/screens/index.ts +0 -22
- package/src/ui/screens/setup/BaseAppSetupScreen.ts +0 -375
- package/src/ui/screens/setup/CloudflaredSetup.ts +0 -460
- package/src/ui/screens/setup/FullAutoSetup.ts +0 -766
- package/src/ui/screens/setup/HomepageSetup.ts +0 -165
- package/src/ui/screens/setup/JellyfinSetup.ts +0 -328
- package/src/ui/screens/setup/JellyseerrSetup.ts +0 -242
- package/src/ui/screens/setup/ProwlarrSetup.ts +0 -500
- package/src/ui/screens/setup/QBittorrentSetup.ts +0 -221
- package/src/ui/screens/setup/QuickSetup.ts +0 -1169
- package/src/ui/screens/setup/RecyclarrSetup.ts +0 -270
- package/src/ui/screens/setup/TRaSHProfileSetup.ts +0 -381
- package/src/utils/arch.ts +0 -78
- package/src/utils/browser.ts +0 -27
- package/src/utils/categories.ts +0 -61
- package/src/utils/debug.ts +0 -84
- package/src/utils/env.ts +0 -138
- package/src/utils/index.ts +0 -8
- package/src/utils/logs.ts +0 -119
- package/src/utils/migrations/1765626338_rename_env_variables.ts +0 -102
- package/src/utils/migrations/1765707135_rename_easiarr_status.ts +0 -91
- package/src/utils/migrations/1765732722_remove_cloudflare_dns_api_token.ts +0 -45
- package/src/utils/migrations/1769515650_migrate_to_xdg.ts +0 -114
- package/src/utils/migrations.ts +0 -165
- package/src/utils/password.ts +0 -42
- package/src/utils/paths.ts +0 -33
- package/src/utils/unraid.ts +0 -103
- package/src/utils/update-checker.ts +0 -210
- package/src/utils/url-utils.ts +0 -38
|
@@ -1,559 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Jellyseerr API Client
|
|
3
|
-
* Handles setup wizard automation and service configuration
|
|
4
|
-
*
|
|
5
|
-
* Based on Jellyseerr source code analysis:
|
|
6
|
-
* - Auth endpoint: POST /api/v1/auth/jellyfin
|
|
7
|
-
* - Setup mode: requires hostname, port, serverType (2=Jellyfin, 3=Emby), useSsl
|
|
8
|
-
* - Login mode: only requires username and password (when server already configured)
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import type { AutoSetupOptions, AutoSetupResult, IAutoSetupClient } from "./auto-setup-types"
|
|
12
|
-
import { debugLog } from "~/utils/debug"
|
|
13
|
-
import { BaseApiClient } from "./base-api"
|
|
14
|
-
|
|
15
|
-
// ==========================================
|
|
16
|
-
// Enums (from Jellyseerr server/constants/server.ts)
|
|
17
|
-
// ==========================================
|
|
18
|
-
|
|
19
|
-
export enum MediaServerType {
|
|
20
|
-
PLEX = 1,
|
|
21
|
-
JELLYFIN = 2,
|
|
22
|
-
EMBY = 3,
|
|
23
|
-
NOT_CONFIGURED = 4,
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// ==========================================
|
|
27
|
-
// Types
|
|
28
|
-
// ==========================================
|
|
29
|
-
|
|
30
|
-
export interface JellyseerrPublicSettings {
|
|
31
|
-
initialized: boolean
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export interface JellyseerrMainSettings {
|
|
35
|
-
apiKey: string
|
|
36
|
-
appLanguage: string
|
|
37
|
-
applicationTitle: string
|
|
38
|
-
applicationUrl: string
|
|
39
|
-
mediaServerType: number
|
|
40
|
-
localLogin: boolean
|
|
41
|
-
newPlexLogin: boolean
|
|
42
|
-
defaultPermissions: number
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export interface JellyseerrJellyfinSettings {
|
|
46
|
-
name?: string
|
|
47
|
-
ip?: string
|
|
48
|
-
hostname?: string
|
|
49
|
-
port?: number
|
|
50
|
-
useSsl?: boolean
|
|
51
|
-
urlBase?: string
|
|
52
|
-
externalHostname?: string
|
|
53
|
-
adminUser?: string
|
|
54
|
-
adminPass?: string
|
|
55
|
-
serverId?: string
|
|
56
|
-
apiKey?: string
|
|
57
|
-
libraries?: JellyseerrLibrary[]
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export interface JellyseerrLibrary {
|
|
61
|
-
id: string
|
|
62
|
-
name: string
|
|
63
|
-
enabled: boolean
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export interface JellyseerrUser {
|
|
67
|
-
id: number
|
|
68
|
-
email: string
|
|
69
|
-
username?: string
|
|
70
|
-
jellyfinUsername?: string
|
|
71
|
-
jellyfinUserId?: string
|
|
72
|
-
userType: number
|
|
73
|
-
permissions: number
|
|
74
|
-
avatar?: string
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export interface JellyseerrRadarrSettings {
|
|
78
|
-
id?: number
|
|
79
|
-
name: string
|
|
80
|
-
hostname: string
|
|
81
|
-
port: number
|
|
82
|
-
apiKey: string
|
|
83
|
-
useSsl: boolean
|
|
84
|
-
baseUrl?: string
|
|
85
|
-
activeProfileId: number
|
|
86
|
-
activeProfileName: string
|
|
87
|
-
activeDirectory: string
|
|
88
|
-
is4k: boolean
|
|
89
|
-
minimumAvailability: string
|
|
90
|
-
isDefault: boolean
|
|
91
|
-
syncEnabled?: boolean
|
|
92
|
-
preventSearch?: boolean
|
|
93
|
-
externalUrl?: string
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
export interface JellyseerrSonarrSettings {
|
|
97
|
-
id?: number
|
|
98
|
-
name: string
|
|
99
|
-
hostname: string
|
|
100
|
-
port: number
|
|
101
|
-
apiKey: string
|
|
102
|
-
useSsl: boolean
|
|
103
|
-
baseUrl?: string
|
|
104
|
-
activeProfileId: number
|
|
105
|
-
activeProfileName: string
|
|
106
|
-
activeDirectory: string
|
|
107
|
-
activeLanguageProfileId?: number
|
|
108
|
-
is4k: boolean
|
|
109
|
-
enableSeasonFolders: boolean
|
|
110
|
-
isDefault: boolean
|
|
111
|
-
syncEnabled?: boolean
|
|
112
|
-
preventSearch?: boolean
|
|
113
|
-
externalUrl?: string
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
export interface ServiceProfile {
|
|
117
|
-
id: number
|
|
118
|
-
name: string
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
export interface ServiceTestResult {
|
|
122
|
-
profiles: ServiceProfile[]
|
|
123
|
-
rootFolders?: { id: number; path: string }[]
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
interface JellyfinSetupAuthRequest {
|
|
127
|
-
username: string
|
|
128
|
-
password: string
|
|
129
|
-
hostname: string
|
|
130
|
-
port: number
|
|
131
|
-
useSsl: boolean
|
|
132
|
-
urlBase: string
|
|
133
|
-
serverType: MediaServerType
|
|
134
|
-
email?: string
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
interface JellyfinLoginRequest {
|
|
138
|
-
username: string
|
|
139
|
-
password: string
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// ==========================================
|
|
143
|
-
// Client
|
|
144
|
-
// ==========================================
|
|
145
|
-
|
|
146
|
-
export class JellyseerrClient extends BaseApiClient implements IAutoSetupClient {
|
|
147
|
-
protected readonly logPrefix = "Jellyseerr"
|
|
148
|
-
private cookie?: string
|
|
149
|
-
|
|
150
|
-
constructor(host: string, port: number) {
|
|
151
|
-
super(host, port)
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
private async request<T>(endpoint: string, options: RequestInit = {}): Promise<T> {
|
|
155
|
-
const url = `${this.baseUrl}/api/v1${endpoint}`
|
|
156
|
-
const headers: Record<string, string> = {
|
|
157
|
-
"Content-Type": "application/json",
|
|
158
|
-
...(options.headers as Record<string, string>),
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
if (this.cookie) {
|
|
162
|
-
headers["Cookie"] = this.cookie
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
debugLog("Jellyseerr", `${options.method || "GET"} ${endpoint}`)
|
|
166
|
-
|
|
167
|
-
const response = await fetch(url, { ...options, headers })
|
|
168
|
-
|
|
169
|
-
const setCookie = response.headers.get("set-cookie")
|
|
170
|
-
if (setCookie) {
|
|
171
|
-
this.cookie = setCookie.split(";")[0]
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
if (!response.ok) {
|
|
175
|
-
const text = await response.text()
|
|
176
|
-
throw new Error(`Jellyseerr API error: ${response.status} - ${text}`)
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
const contentType = response.headers.get("content-type")
|
|
180
|
-
if (contentType?.includes("application/json")) {
|
|
181
|
-
return response.json()
|
|
182
|
-
}
|
|
183
|
-
return {} as T
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// ==========================================
|
|
187
|
-
// Health & Status
|
|
188
|
-
// ==========================================
|
|
189
|
-
|
|
190
|
-
async isHealthy(): Promise<boolean> {
|
|
191
|
-
try {
|
|
192
|
-
await this.request<{ version: string }>("/status")
|
|
193
|
-
return true
|
|
194
|
-
} catch {
|
|
195
|
-
return false
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
async isInitialized(): Promise<boolean> {
|
|
200
|
-
try {
|
|
201
|
-
const settings = await this.request<JellyseerrPublicSettings>("/settings/public")
|
|
202
|
-
return settings.initialized
|
|
203
|
-
} catch {
|
|
204
|
-
return false
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// ==========================================
|
|
209
|
-
// Main Settings
|
|
210
|
-
// ==========================================
|
|
211
|
-
|
|
212
|
-
async getMainSettings(): Promise<JellyseerrMainSettings> {
|
|
213
|
-
return this.request<JellyseerrMainSettings>("/settings/main")
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
async updateMainSettings(
|
|
217
|
-
settings: Partial<JellyseerrMainSettings>
|
|
218
|
-
): Promise<JellyseerrMainSettings> {
|
|
219
|
-
return this.request<JellyseerrMainSettings>("/settings/main", {
|
|
220
|
-
method: "POST",
|
|
221
|
-
body: JSON.stringify(settings),
|
|
222
|
-
})
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
async initialize(): Promise<{ initialized: boolean }> {
|
|
226
|
-
return this.request<{ initialized: boolean }>("/settings/initialize", { method: "POST" })
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
async setApplicationUrl(applicationUrl: string): Promise<JellyseerrMainSettings> {
|
|
230
|
-
return this.updateMainSettings({ applicationUrl })
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
// ==========================================
|
|
234
|
-
// Jellyfin Configuration
|
|
235
|
-
// ==========================================
|
|
236
|
-
|
|
237
|
-
async getJellyfinSettings(): Promise<JellyseerrJellyfinSettings> {
|
|
238
|
-
return this.request<JellyseerrJellyfinSettings>("/settings/jellyfin")
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
async updateJellyfinSettings(
|
|
242
|
-
settings: Partial<JellyseerrJellyfinSettings>
|
|
243
|
-
): Promise<JellyseerrJellyfinSettings> {
|
|
244
|
-
return this.request<JellyseerrJellyfinSettings>("/settings/jellyfin", {
|
|
245
|
-
method: "POST",
|
|
246
|
-
body: JSON.stringify(settings),
|
|
247
|
-
})
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
async syncJellyfinLibraries(): Promise<JellyseerrLibrary[]> {
|
|
251
|
-
return this.request<JellyseerrLibrary[]>("/settings/jellyfin/library?sync=true")
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
async enableLibraries(libraryIds: string[]): Promise<JellyseerrLibrary[]> {
|
|
255
|
-
const enable = libraryIds.join(",")
|
|
256
|
-
return this.request<JellyseerrLibrary[]>(
|
|
257
|
-
`/settings/jellyfin/library?enable=${encodeURIComponent(enable)}`
|
|
258
|
-
)
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
// ==========================================
|
|
262
|
-
// Authentication
|
|
263
|
-
// ==========================================
|
|
264
|
-
|
|
265
|
-
async authenticateJellyfin(
|
|
266
|
-
username: string,
|
|
267
|
-
password: string,
|
|
268
|
-
hostname: string,
|
|
269
|
-
port: number,
|
|
270
|
-
email?: string
|
|
271
|
-
): Promise<JellyseerrUser> {
|
|
272
|
-
const setupPayload: JellyfinSetupAuthRequest = {
|
|
273
|
-
username,
|
|
274
|
-
password,
|
|
275
|
-
hostname,
|
|
276
|
-
port,
|
|
277
|
-
useSsl: false,
|
|
278
|
-
urlBase: "",
|
|
279
|
-
serverType: MediaServerType.JELLYFIN,
|
|
280
|
-
email: email || `${username}@local`,
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
try {
|
|
284
|
-
return await this.request<JellyseerrUser>("/auth/jellyfin", {
|
|
285
|
-
method: "POST",
|
|
286
|
-
body: JSON.stringify(setupPayload),
|
|
287
|
-
})
|
|
288
|
-
} catch (err: unknown) {
|
|
289
|
-
const message = err instanceof Error ? err.message : String(err)
|
|
290
|
-
|
|
291
|
-
if (
|
|
292
|
-
message.includes("already configured") ||
|
|
293
|
-
message.includes("hostname already configured")
|
|
294
|
-
) {
|
|
295
|
-
const loginPayload: JellyfinLoginRequest = { username, password }
|
|
296
|
-
return this.request<JellyseerrUser>("/auth/jellyfin", {
|
|
297
|
-
method: "POST",
|
|
298
|
-
body: JSON.stringify(loginPayload),
|
|
299
|
-
})
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
if (message.includes("NO_ADMIN_USER") || message.includes("NotAdmin")) {
|
|
303
|
-
throw new Error(`Jellyfin user "${username}" is not an administrator.`, { cause: err })
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
if (message.includes("InvalidCredentials") || message.includes("401")) {
|
|
307
|
-
throw new Error(`Invalid Jellyfin credentials for user "${username}".`, { cause: err })
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
if (message.includes("InvalidUrl") || message.includes("INVALID_URL")) {
|
|
311
|
-
throw new Error(`Cannot reach Jellyfin at ${hostname}:${port}.`, { cause: err })
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
throw err
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
async authenticatePlex(authToken: string): Promise<JellyseerrUser> {
|
|
319
|
-
return this.request<JellyseerrUser>("/auth/plex", {
|
|
320
|
-
method: "POST",
|
|
321
|
-
body: JSON.stringify({ authToken }),
|
|
322
|
-
})
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
// ==========================================
|
|
326
|
-
// Radarr Configuration
|
|
327
|
-
// ==========================================
|
|
328
|
-
|
|
329
|
-
async getRadarrSettings(): Promise<JellyseerrRadarrSettings[]> {
|
|
330
|
-
return this.request<JellyseerrRadarrSettings[]>("/settings/radarr")
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
async testRadarr(config: {
|
|
334
|
-
hostname: string
|
|
335
|
-
port: number
|
|
336
|
-
apiKey: string
|
|
337
|
-
useSsl: boolean
|
|
338
|
-
baseUrl?: string
|
|
339
|
-
}): Promise<ServiceTestResult> {
|
|
340
|
-
return this.request<ServiceTestResult>("/settings/radarr/test", {
|
|
341
|
-
method: "POST",
|
|
342
|
-
body: JSON.stringify(config),
|
|
343
|
-
})
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
async addRadarr(settings: JellyseerrRadarrSettings): Promise<JellyseerrRadarrSettings> {
|
|
347
|
-
return this.request<JellyseerrRadarrSettings>("/settings/radarr", {
|
|
348
|
-
method: "POST",
|
|
349
|
-
body: JSON.stringify(settings),
|
|
350
|
-
})
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
async updateRadarr(
|
|
354
|
-
id: number,
|
|
355
|
-
settings: Partial<JellyseerrRadarrSettings>
|
|
356
|
-
): Promise<JellyseerrRadarrSettings> {
|
|
357
|
-
return this.request<JellyseerrRadarrSettings>(`/settings/radarr/${id}`, {
|
|
358
|
-
method: "PUT",
|
|
359
|
-
body: JSON.stringify(settings),
|
|
360
|
-
})
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
// ==========================================
|
|
364
|
-
// Sonarr Configuration
|
|
365
|
-
// ==========================================
|
|
366
|
-
|
|
367
|
-
async getSonarrSettings(): Promise<JellyseerrSonarrSettings[]> {
|
|
368
|
-
return this.request<JellyseerrSonarrSettings[]>("/settings/sonarr")
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
async testSonarr(config: {
|
|
372
|
-
hostname: string
|
|
373
|
-
port: number
|
|
374
|
-
apiKey: string
|
|
375
|
-
useSsl: boolean
|
|
376
|
-
baseUrl?: string
|
|
377
|
-
}): Promise<ServiceTestResult> {
|
|
378
|
-
return this.request<ServiceTestResult>("/settings/sonarr/test", {
|
|
379
|
-
method: "POST",
|
|
380
|
-
body: JSON.stringify(config),
|
|
381
|
-
})
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
async addSonarr(settings: JellyseerrSonarrSettings): Promise<JellyseerrSonarrSettings> {
|
|
385
|
-
return this.request<JellyseerrSonarrSettings>("/settings/sonarr", {
|
|
386
|
-
method: "POST",
|
|
387
|
-
body: JSON.stringify(settings),
|
|
388
|
-
})
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
async updateSonarr(
|
|
392
|
-
id: number,
|
|
393
|
-
settings: Partial<JellyseerrSonarrSettings>
|
|
394
|
-
): Promise<JellyseerrSonarrSettings> {
|
|
395
|
-
return this.request<JellyseerrSonarrSettings>(`/settings/sonarr/${id}`, {
|
|
396
|
-
method: "PUT",
|
|
397
|
-
body: JSON.stringify(settings),
|
|
398
|
-
})
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
// ==========================================
|
|
402
|
-
// Full Setup Wizard
|
|
403
|
-
// ==========================================
|
|
404
|
-
|
|
405
|
-
async runJellyfinSetup(
|
|
406
|
-
jellyfinHostname: string,
|
|
407
|
-
port: number,
|
|
408
|
-
username: string,
|
|
409
|
-
password: string,
|
|
410
|
-
email?: string
|
|
411
|
-
): Promise<string> {
|
|
412
|
-
await this.authenticateJellyfin(username, password, jellyfinHostname, port, email)
|
|
413
|
-
|
|
414
|
-
const fullUrl = `http://${jellyfinHostname}:${port}`
|
|
415
|
-
await this.updateJellyfinSettings({
|
|
416
|
-
hostname: fullUrl,
|
|
417
|
-
adminUser: username,
|
|
418
|
-
adminPass: password,
|
|
419
|
-
})
|
|
420
|
-
|
|
421
|
-
const libraries = await this.syncJellyfinLibraries()
|
|
422
|
-
const libraryIds = libraries.map((lib) => lib.id)
|
|
423
|
-
if (libraryIds.length > 0) {
|
|
424
|
-
await this.enableLibraries(libraryIds)
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
const mainSettings = await this.getMainSettings()
|
|
428
|
-
return mainSettings.apiKey
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
async configureRadarr(
|
|
432
|
-
hostname: string,
|
|
433
|
-
port: number,
|
|
434
|
-
apiKey: string,
|
|
435
|
-
rootFolder: string,
|
|
436
|
-
externalUrl?: string
|
|
437
|
-
): Promise<JellyseerrRadarrSettings | null> {
|
|
438
|
-
try {
|
|
439
|
-
const existingConfigs = await this.getRadarrSettings()
|
|
440
|
-
const existingConfig = existingConfigs.find((c) => c.hostname === hostname && c.port === port)
|
|
441
|
-
|
|
442
|
-
if (existingConfig?.id) {
|
|
443
|
-
const { id, ...configWithoutId } = existingConfig
|
|
444
|
-
return await this.updateRadarr(id, { ...configWithoutId, externalUrl })
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
const testResult = await this.testRadarr({ hostname, port, apiKey, useSsl: false })
|
|
448
|
-
if (!testResult.profiles || testResult.profiles.length === 0) {
|
|
449
|
-
return null
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
const profile = testResult.profiles[0]
|
|
453
|
-
return await this.addRadarr({
|
|
454
|
-
name: "Radarr",
|
|
455
|
-
hostname,
|
|
456
|
-
port,
|
|
457
|
-
apiKey,
|
|
458
|
-
useSsl: false,
|
|
459
|
-
activeProfileId: profile.id,
|
|
460
|
-
activeProfileName: profile.name,
|
|
461
|
-
activeDirectory: rootFolder,
|
|
462
|
-
is4k: false,
|
|
463
|
-
minimumAvailability: "announced",
|
|
464
|
-
isDefault: true,
|
|
465
|
-
externalUrl,
|
|
466
|
-
})
|
|
467
|
-
} catch {
|
|
468
|
-
return null
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
async configureSonarr(
|
|
473
|
-
hostname: string,
|
|
474
|
-
port: number,
|
|
475
|
-
apiKey: string,
|
|
476
|
-
rootFolder: string,
|
|
477
|
-
externalUrl?: string
|
|
478
|
-
): Promise<JellyseerrSonarrSettings | null> {
|
|
479
|
-
try {
|
|
480
|
-
const existingConfigs = await this.getSonarrSettings()
|
|
481
|
-
const existingConfig = existingConfigs.find((c) => c.hostname === hostname && c.port === port)
|
|
482
|
-
|
|
483
|
-
if (existingConfig?.id) {
|
|
484
|
-
const { id, ...configWithoutId } = existingConfig
|
|
485
|
-
return await this.updateSonarr(id, { ...configWithoutId, externalUrl })
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
const testResult = await this.testSonarr({ hostname, port, apiKey, useSsl: false })
|
|
489
|
-
if (!testResult.profiles || testResult.profiles.length === 0) {
|
|
490
|
-
return null
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
const profile = testResult.profiles[0]
|
|
494
|
-
return await this.addSonarr({
|
|
495
|
-
name: "Sonarr",
|
|
496
|
-
hostname,
|
|
497
|
-
port,
|
|
498
|
-
apiKey,
|
|
499
|
-
useSsl: false,
|
|
500
|
-
activeProfileId: profile.id,
|
|
501
|
-
activeProfileName: profile.name,
|
|
502
|
-
activeDirectory: rootFolder,
|
|
503
|
-
is4k: false,
|
|
504
|
-
enableSeasonFolders: true,
|
|
505
|
-
isDefault: true,
|
|
506
|
-
externalUrl,
|
|
507
|
-
})
|
|
508
|
-
} catch {
|
|
509
|
-
return null
|
|
510
|
-
}
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
// ==========================================
|
|
514
|
-
// Auto-Setup
|
|
515
|
-
// ==========================================
|
|
516
|
-
|
|
517
|
-
async setup(options: AutoSetupOptions): Promise<AutoSetupResult> {
|
|
518
|
-
const { username, password, env } = options
|
|
519
|
-
|
|
520
|
-
try {
|
|
521
|
-
const healthy = await this.isHealthy()
|
|
522
|
-
if (!healthy) {
|
|
523
|
-
return { success: false, message: "Jellyseerr not reachable" }
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
const jellyfinHost = env["JELLYFIN_HOST"] || "jellyfin"
|
|
527
|
-
const jellyfinPort = parseInt(env["JELLYFIN_PORT"] || "8096", 10)
|
|
528
|
-
|
|
529
|
-
const initialized = await this.isInitialized()
|
|
530
|
-
if (initialized) {
|
|
531
|
-
try {
|
|
532
|
-
await this.authenticateJellyfin(username, password, jellyfinHost, jellyfinPort)
|
|
533
|
-
} catch {
|
|
534
|
-
return { success: true, message: "Already configured (could not authenticate)" }
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
const settings = await this.getMainSettings()
|
|
538
|
-
return {
|
|
539
|
-
success: true,
|
|
540
|
-
message: "Already configured",
|
|
541
|
-
data: { apiKey: settings.apiKey },
|
|
542
|
-
envUpdates: { API_KEY_JELLYSEERR: settings.apiKey },
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
const apiKey = await this.runJellyfinSetup(jellyfinHost, jellyfinPort, username, password)
|
|
547
|
-
await this.initialize()
|
|
548
|
-
|
|
549
|
-
return {
|
|
550
|
-
success: true,
|
|
551
|
-
message: "Jellyseerr configured with Jellyfin",
|
|
552
|
-
data: { apiKey },
|
|
553
|
-
envUpdates: { API_KEY_JELLYSEERR: apiKey },
|
|
554
|
-
}
|
|
555
|
-
} catch (error) {
|
|
556
|
-
return { success: false, message: `${error}` }
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
}
|