@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.
Files changed (109) hide show
  1. package/dist/index.js +29837 -0
  2. package/package.json +15 -15
  3. package/src/VersionInfo.ts +0 -12
  4. package/src/api/arr-api.ts +0 -521
  5. package/src/api/auto-setup-types.ts +0 -64
  6. package/src/api/base-api.ts +0 -216
  7. package/src/api/bazarr-api.ts +0 -297
  8. package/src/api/cloudflare-api.ts +0 -493
  9. package/src/api/custom-format-api.ts +0 -224
  10. package/src/api/grafana-api.ts +0 -230
  11. package/src/api/heimdall-api.ts +0 -141
  12. package/src/api/homarr-api.ts +0 -209
  13. package/src/api/huntarr-api.ts +0 -457
  14. package/src/api/index.ts +0 -9
  15. package/src/api/jellyfin-api.ts +0 -389
  16. package/src/api/jellyseerr-api.ts +0 -559
  17. package/src/api/maintainerr-api.ts +0 -461
  18. package/src/api/naming-config.ts +0 -101
  19. package/src/api/overseerr-api.ts +0 -392
  20. package/src/api/plex-api.ts +0 -308
  21. package/src/api/portainer-api.ts +0 -374
  22. package/src/api/profilarr-api.ts +0 -391
  23. package/src/api/prowlarr-api.ts +0 -488
  24. package/src/api/qbittorrent-api.ts +0 -300
  25. package/src/api/quality-profile-api.ts +0 -272
  26. package/src/api/tautulli-api.ts +0 -244
  27. package/src/api/uptime-kuma-api.ts +0 -343
  28. package/src/apps/categories.ts +0 -14
  29. package/src/apps/index.ts +0 -2
  30. package/src/apps/registry.ts +0 -1275
  31. package/src/compose/caddy-config.ts +0 -130
  32. package/src/compose/generator.ts +0 -287
  33. package/src/compose/index.ts +0 -7
  34. package/src/compose/templates.ts +0 -78
  35. package/src/compose/traefik-config.ts +0 -187
  36. package/src/config/bookmarks-generator.ts +0 -160
  37. package/src/config/defaults.ts +0 -37
  38. package/src/config/homepage-config.ts +0 -381
  39. package/src/config/index.ts +0 -6
  40. package/src/config/manager.ts +0 -150
  41. package/src/config/recyclarr-config.ts +0 -181
  42. package/src/config/schema.ts +0 -310
  43. package/src/config/slskd-config.ts +0 -86
  44. package/src/config/soularr-config.ts +0 -105
  45. package/src/config/trash-quality-definitions.ts +0 -213
  46. package/src/data/lidarr-custom-formats.ts +0 -127
  47. package/src/data/trash-profiles.ts +0 -333
  48. package/src/docker/client.ts +0 -312
  49. package/src/docker/index.ts +0 -18
  50. package/src/index.ts +0 -77
  51. package/src/setup/actions/arr-common.ts +0 -307
  52. package/src/setup/actions/dashboards.ts +0 -79
  53. package/src/setup/actions/download-clients.ts +0 -54
  54. package/src/setup/actions/jellyseerr.ts +0 -331
  55. package/src/setup/actions/media-servers.ts +0 -68
  56. package/src/setup/actions/monitoring.ts +0 -120
  57. package/src/setup/actions/prowlarr.ts +0 -137
  58. package/src/setup/actions/utilities.ts +0 -490
  59. package/src/setup/index.ts +0 -78
  60. package/src/setup/types.ts +0 -144
  61. package/src/structure/manager.ts +0 -129
  62. package/src/ui/App.ts +0 -111
  63. package/src/ui/components/ApplicationSelector.ts +0 -275
  64. package/src/ui/components/CredentialsForm.ts +0 -180
  65. package/src/ui/components/DownloadClientForm.ts +0 -114
  66. package/src/ui/components/FileEditor.ts +0 -91
  67. package/src/ui/components/FooterHint.ts +0 -119
  68. package/src/ui/components/PageLayout.ts +0 -160
  69. package/src/ui/components/UpdateNotification.ts +0 -102
  70. package/src/ui/index.ts +0 -7
  71. package/src/ui/screens/AdvancedSettings.ts +0 -188
  72. package/src/ui/screens/ApiKeyViewer.ts +0 -511
  73. package/src/ui/screens/AppConfigurator.ts +0 -543
  74. package/src/ui/screens/AppManager.ts +0 -284
  75. package/src/ui/screens/ContainerControl.ts +0 -581
  76. package/src/ui/screens/LogsViewer.ts +0 -478
  77. package/src/ui/screens/MainMenu.ts +0 -299
  78. package/src/ui/screens/MonitorDashboard.ts +0 -881
  79. package/src/ui/screens/SecretsEditor.ts +0 -216
  80. package/src/ui/screens/SettingsScreen.ts +0 -633
  81. package/src/ui/screens/index.ts +0 -22
  82. package/src/ui/screens/setup/BaseAppSetupScreen.ts +0 -375
  83. package/src/ui/screens/setup/CloudflaredSetup.ts +0 -460
  84. package/src/ui/screens/setup/FullAutoSetup.ts +0 -766
  85. package/src/ui/screens/setup/HomepageSetup.ts +0 -165
  86. package/src/ui/screens/setup/JellyfinSetup.ts +0 -328
  87. package/src/ui/screens/setup/JellyseerrSetup.ts +0 -242
  88. package/src/ui/screens/setup/ProwlarrSetup.ts +0 -500
  89. package/src/ui/screens/setup/QBittorrentSetup.ts +0 -221
  90. package/src/ui/screens/setup/QuickSetup.ts +0 -1169
  91. package/src/ui/screens/setup/RecyclarrSetup.ts +0 -270
  92. package/src/ui/screens/setup/TRaSHProfileSetup.ts +0 -381
  93. package/src/utils/arch.ts +0 -78
  94. package/src/utils/browser.ts +0 -27
  95. package/src/utils/categories.ts +0 -61
  96. package/src/utils/debug.ts +0 -84
  97. package/src/utils/env.ts +0 -138
  98. package/src/utils/index.ts +0 -8
  99. package/src/utils/logs.ts +0 -119
  100. package/src/utils/migrations/1765626338_rename_env_variables.ts +0 -102
  101. package/src/utils/migrations/1765707135_rename_easiarr_status.ts +0 -91
  102. package/src/utils/migrations/1765732722_remove_cloudflare_dns_api_token.ts +0 -45
  103. package/src/utils/migrations/1769515650_migrate_to_xdg.ts +0 -114
  104. package/src/utils/migrations.ts +0 -165
  105. package/src/utils/password.ts +0 -42
  106. package/src/utils/paths.ts +0 -33
  107. package/src/utils/unraid.ts +0 -103
  108. package/src/utils/update-checker.ts +0 -210
  109. package/src/utils/url-utils.ts +0 -38
@@ -1,392 +0,0 @@
1
- /**
2
- * Overseerr API Client
3
- * Handles Overseerr auto-setup for Plex media requests
4
- * Fully automated using Plex token authentication
5
- */
6
-
7
- import type { AutoSetupOptions, AutoSetupResult, IAutoSetupClient } from "./auto-setup-types"
8
- import { debugLog } from "~/utils/debug"
9
- import { BaseApiClient } from "./base-api"
10
-
11
- interface OverseerrStatus {
12
- version: string
13
- status: number
14
- }
15
-
16
- interface OverseerrUser {
17
- id: number
18
- email: string
19
- username?: string
20
- plexToken?: string
21
- plexUsername?: string
22
- userType: number
23
- permissions: number
24
- avatar?: string
25
- }
26
-
27
- interface PlexSettings {
28
- name: string
29
- machineId: string
30
- ip: string
31
- port: number
32
- useSsl?: boolean
33
- libraries: { id: string; name: string; enabled: boolean }[]
34
- }
35
-
36
- interface PlexDevice {
37
- name: string
38
- clientIdentifier: string
39
- connection: { uri: string; local: boolean }[]
40
- }
41
-
42
- interface RadarrSettings {
43
- name: string
44
- hostname: string
45
- port: number
46
- apiKey: string
47
- useSsl?: boolean
48
- baseUrl?: string
49
- activeProfileId: number
50
- activeDirectory: string
51
- is4k: boolean
52
- isDefault: boolean
53
- minimumAvailability?: string
54
- }
55
-
56
- interface SonarrSettings {
57
- name: string
58
- hostname: string
59
- port: number
60
- apiKey: string
61
- useSsl?: boolean
62
- baseUrl?: string
63
- activeProfileId: number
64
- activeDirectory: string
65
- activeAnimeProfileId?: number
66
- activeAnimeDirectory?: string
67
- is4k: boolean
68
- isDefault: boolean
69
- enableSeasonFolders: boolean
70
- }
71
-
72
- interface MainSettings {
73
- apiKey: string
74
- applicationTitle?: string
75
- applicationUrl?: string
76
- }
77
-
78
- export class OverseerrClient extends BaseApiClient implements IAutoSetupClient {
79
- protected readonly logPrefix = "OverseerrApi"
80
- private apiKey?: string
81
- private sessionCookie?: string
82
-
83
- constructor(host: string, port: number = 5055, apiKey?: string) {
84
- super(host, port)
85
- this.apiKey = apiKey
86
- }
87
-
88
- /**
89
- * Common headers for Overseerr API requests
90
- */
91
- private getHeaders(): Record<string, string> {
92
- const headers: Record<string, string> = {
93
- "Content-Type": "application/json",
94
- Accept: "application/json",
95
- }
96
- if (this.apiKey) {
97
- headers["X-Api-Key"] = this.apiKey
98
- }
99
- if (this.sessionCookie) {
100
- headers["Cookie"] = this.sessionCookie
101
- }
102
- return headers
103
- }
104
-
105
- /**
106
- * Set API key for authenticated requests
107
- */
108
- setApiKey(apiKey: string): void {
109
- this.apiKey = apiKey
110
- }
111
-
112
- /**
113
- * Check if Overseerr is reachable
114
- */
115
- async isHealthy(): Promise<boolean> {
116
- const response = await this.get<OverseerrStatus>("/api/v1/status")
117
- return response.success
118
- }
119
-
120
- /**
121
- * Check if Overseerr is already configured
122
- */
123
- async isInitialized(): Promise<boolean> {
124
- try {
125
- const response = await fetch(`${this.baseUrl}/api/v1/settings/public`, {
126
- method: "GET",
127
- })
128
- if (!response.ok) return false
129
- const data = await response.json()
130
- return data.initialized === true
131
- } catch {
132
- return false
133
- }
134
- }
135
-
136
- /**
137
- * Authenticate with Overseerr using a Plex token
138
- */
139
- async authenticateWithPlex(plexToken: string): Promise<OverseerrUser | null> {
140
- debugLog("OverseerrApi", "Authenticating with Plex token...")
141
-
142
- try {
143
- const response = await fetch(`${this.baseUrl}/api/v1/auth/plex`, {
144
- method: "POST",
145
- headers: {
146
- "Content-Type": "application/json",
147
- Accept: "application/json",
148
- },
149
- body: JSON.stringify({ authToken: plexToken }),
150
- })
151
-
152
- if (response.ok) {
153
- const setCookie = response.headers.get("set-cookie")
154
- if (setCookie) {
155
- this.sessionCookie = setCookie.split(";")[0]
156
- }
157
- return await response.json()
158
- }
159
- return null
160
- } catch {
161
- return null
162
- }
163
- }
164
-
165
- /**
166
- * Get available Plex servers
167
- */
168
- async getPlexServers(): Promise<PlexDevice[]> {
169
- try {
170
- const response = await fetch(`${this.baseUrl}/api/v1/settings/plex/devices/servers`, {
171
- method: "GET",
172
- headers: this.getHeaders(),
173
- })
174
- if (response.ok) {
175
- return response.json()
176
- }
177
- } catch {
178
- // Ignore
179
- }
180
- return []
181
- }
182
-
183
- /**
184
- * Initialize Overseerr
185
- */
186
- async initialize(): Promise<boolean> {
187
- try {
188
- const response = await fetch(`${this.baseUrl}/api/v1/settings/initialize`, {
189
- method: "POST",
190
- headers: this.getHeaders(),
191
- })
192
- return response.ok
193
- } catch {
194
- return false
195
- }
196
- }
197
-
198
- /**
199
- * Get main settings (includes API key)
200
- */
201
- async getMainSettings(): Promise<MainSettings | null> {
202
- try {
203
- const response = await fetch(`${this.baseUrl}/api/v1/settings/main`, {
204
- method: "GET",
205
- headers: this.getHeaders(),
206
- })
207
- if (response.ok) {
208
- return response.json()
209
- }
210
- } catch {
211
- // Ignore
212
- }
213
- return null
214
- }
215
-
216
- /**
217
- * Set application URL
218
- */
219
- async setApplicationUrl(applicationUrl: string): Promise<boolean> {
220
- try {
221
- const response = await fetch(`${this.baseUrl}/api/v1/settings/main`, {
222
- method: "POST",
223
- headers: this.getHeaders(),
224
- body: JSON.stringify({ applicationUrl }),
225
- })
226
- return response.ok
227
- } catch {
228
- return false
229
- }
230
- }
231
-
232
- /**
233
- * Sync Plex libraries
234
- */
235
- async syncPlexLibraries(): Promise<boolean> {
236
- try {
237
- const response = await fetch(`${this.baseUrl}/api/v1/settings/plex/library?sync=true`, {
238
- method: "GET",
239
- headers: this.getHeaders(),
240
- })
241
- return response.ok
242
- } catch {
243
- return false
244
- }
245
- }
246
-
247
- /**
248
- * Start a full Plex library scan
249
- */
250
- async startPlexScan(): Promise<boolean> {
251
- try {
252
- const response = await fetch(`${this.baseUrl}/api/v1/settings/plex/sync`, {
253
- method: "POST",
254
- headers: this.getHeaders(),
255
- body: JSON.stringify({ start: true }),
256
- })
257
- return response.ok
258
- } catch {
259
- return false
260
- }
261
- }
262
-
263
- /**
264
- * Get Overseerr status
265
- */
266
- async getStatus(): Promise<OverseerrStatus | null> {
267
- const response = await this.get<OverseerrStatus>("/api/v1/status")
268
- return response.data ?? null
269
- }
270
-
271
- /**
272
- * Get current Plex settings
273
- */
274
- async getPlexSettings(): Promise<PlexSettings | null> {
275
- const response = await fetch(`${this.baseUrl}/api/v1/settings/plex`, {
276
- method: "GET",
277
- headers: this.getHeaders(),
278
- })
279
- if (response.ok) {
280
- return response.json()
281
- }
282
- return null
283
- }
284
-
285
- /**
286
- * Update Plex settings
287
- */
288
- async updatePlexSettings(settings: Partial<PlexSettings>): Promise<boolean> {
289
- const response = await fetch(`${this.baseUrl}/api/v1/settings/plex`, {
290
- method: "POST",
291
- headers: this.getHeaders(),
292
- body: JSON.stringify(settings),
293
- })
294
- return response.ok
295
- }
296
-
297
- /**
298
- * Add Radarr server
299
- */
300
- async addRadarrServer(settings: RadarrSettings): Promise<boolean> {
301
- const response = await fetch(`${this.baseUrl}/api/v1/settings/radarr`, {
302
- method: "POST",
303
- headers: this.getHeaders(),
304
- body: JSON.stringify(settings),
305
- })
306
- return response.ok
307
- }
308
-
309
- /**
310
- * Add Sonarr server
311
- */
312
- async addSonarrServer(settings: SonarrSettings): Promise<boolean> {
313
- const response = await fetch(`${this.baseUrl}/api/v1/settings/sonarr`, {
314
- method: "POST",
315
- headers: this.getHeaders(),
316
- body: JSON.stringify(settings),
317
- })
318
- return response.ok
319
- }
320
-
321
- /**
322
- * Run the auto-setup process
323
- */
324
- async setup(options: AutoSetupOptions): Promise<AutoSetupResult> {
325
- try {
326
- const healthy = await this.isHealthy()
327
- if (!healthy) {
328
- return { success: false, message: "Overseerr not reachable" }
329
- }
330
-
331
- const initialized = await this.isInitialized()
332
- if (initialized) {
333
- const settings = await this.getMainSettings()
334
- if (settings?.apiKey) {
335
- return {
336
- success: true,
337
- message: "Already configured",
338
- data: { apiKey: settings.apiKey },
339
- }
340
- }
341
- return { success: true, message: "Already configured" }
342
- }
343
-
344
- const plexToken = options.plexToken || process.env.API_KEY_PLEX
345
- if (!plexToken) {
346
- return {
347
- success: false,
348
- message: "Plex token required (set API_KEY_PLEX env var)",
349
- }
350
- }
351
-
352
- const user = await this.authenticateWithPlex(plexToken)
353
- if (!user) {
354
- return { success: false, message: "Failed to authenticate with Plex" }
355
- }
356
-
357
- const servers = await this.getPlexServers()
358
- if (servers.length > 0) {
359
- const server = servers[0]
360
- const localConn = server.connection.find((c) => c.local) || server.connection[0]
361
- if (localConn) {
362
- const url = new URL(localConn.uri)
363
- await this.updatePlexSettings({
364
- name: server.name,
365
- ip: url.hostname,
366
- port: parseInt(url.port) || 32400,
367
- })
368
- }
369
- }
370
-
371
- await this.syncPlexLibraries()
372
-
373
- const initSuccess = await this.initialize()
374
- if (!initSuccess) {
375
- return { success: false, message: "Failed to initialize Overseerr" }
376
- }
377
-
378
- const settings = await this.getMainSettings()
379
- const apiKey = settings?.apiKey
380
-
381
- await this.startPlexScan()
382
-
383
- return {
384
- success: true,
385
- message: "Overseerr configured successfully",
386
- data: apiKey ? { apiKey } : undefined,
387
- }
388
- } catch (error) {
389
- return { success: false, message: `${error}` }
390
- }
391
- }
392
- }
@@ -1,308 +0,0 @@
1
- /**
2
- * Plex API Client
3
- * Handles Plex Media Server auto-setup including server claiming and library creation
4
- */
5
-
6
- import type { AutoSetupOptions, AutoSetupResult, IAutoSetupClient } from "./auto-setup-types"
7
- import { debugLog } from "~/utils/debug"
8
- import { BaseApiClient } from "./base-api"
9
-
10
- // Plex client identifier for API requests
11
- const PLEX_CLIENT_ID = "easiarr"
12
- const PLEX_PRODUCT = "Easiarr"
13
- const PLEX_VERSION = "1.0.0"
14
- const PLEX_DEVICE = "Server"
15
-
16
- interface PlexLibrarySection {
17
- key: string
18
- type: string
19
- title: string
20
- agent: string
21
- scanner: string
22
- language: string
23
- Location: { id: number; path: string }[]
24
- }
25
-
26
- interface PlexServerInfo {
27
- machineIdentifier: string
28
- version: string
29
- claimed: boolean
30
- }
31
-
32
- export class PlexApiClient extends BaseApiClient implements IAutoSetupClient {
33
- protected readonly logPrefix = "PlexApi"
34
- private token?: string
35
-
36
- constructor(host: string, port: number = 32400, token?: string) {
37
- super(host, port)
38
- this.token = token
39
- }
40
-
41
- /**
42
- * Set the Plex token for authenticated requests
43
- */
44
- setToken(token: string): void {
45
- this.token = token
46
- }
47
-
48
- /**
49
- * Common headers for Plex API requests
50
- */
51
- private getHeaders(): Record<string, string> {
52
- const headers: Record<string, string> = {
53
- Accept: "application/json",
54
- "X-Plex-Client-Identifier": PLEX_CLIENT_ID,
55
- "X-Plex-Product": PLEX_PRODUCT,
56
- "X-Plex-Version": PLEX_VERSION,
57
- "X-Plex-Device": PLEX_DEVICE,
58
- }
59
- if (this.token) {
60
- headers["X-Plex-Token"] = this.token
61
- }
62
- return headers
63
- }
64
-
65
- /**
66
- * Check if Plex server is reachable
67
- */
68
- async isHealthy(): Promise<boolean> {
69
- const response = await this.get<unknown>("/identity", { headers: this.getHeaders() })
70
- return response.success
71
- }
72
-
73
- /**
74
- * Check if server is already claimed (initialized)
75
- */
76
- async isInitialized(): Promise<boolean> {
77
- try {
78
- const info = await this.getServerInfo()
79
- return info.claimed
80
- } catch {
81
- return false
82
- }
83
- }
84
-
85
- /**
86
- * Get server information including claim status
87
- */
88
- async getServerInfo(): Promise<PlexServerInfo> {
89
- const response = await fetch(`${this.baseUrl}/`, {
90
- method: "GET",
91
- headers: this.getHeaders(),
92
- })
93
-
94
- if (!response.ok) {
95
- throw new Error(`Failed to get server info: ${response.status}`)
96
- }
97
-
98
- const data = await response.json()
99
- const container = data.MediaContainer
100
-
101
- return {
102
- machineIdentifier: container.machineIdentifier,
103
- version: container.version,
104
- claimed: container.myPlex === true || !!container.myPlexUsername,
105
- }
106
- }
107
-
108
- /**
109
- * Claim the server using a claim token from plex.tv/claim
110
- */
111
- async claimServer(claimToken: string): Promise<void> {
112
- debugLog("PlexApi", "Claiming server with token...")
113
-
114
- const token = claimToken.startsWith("claim-") ? claimToken : `claim-${claimToken}`
115
-
116
- const response = await this.post<unknown>(`/myplex/claim?token=${token}`, undefined, {
117
- headers: this.getHeaders(),
118
- })
119
-
120
- if (!response.success) {
121
- throw new Error(`Failed to claim server: ${response.status}`)
122
- }
123
-
124
- debugLog("PlexApi", "Server claimed successfully")
125
- }
126
-
127
- /**
128
- * Get list of library sections
129
- */
130
- async getLibrarySections(): Promise<PlexLibrarySection[]> {
131
- const response = await fetch(`${this.baseUrl}/library/sections`, {
132
- method: "GET",
133
- headers: this.getHeaders(),
134
- })
135
-
136
- if (!response.ok) {
137
- throw new Error(`Failed to get library sections: ${response.status}`)
138
- }
139
-
140
- const data = await response.json()
141
- return data.MediaContainer?.Directory || []
142
- }
143
-
144
- /**
145
- * Create a library section
146
- */
147
- async createLibrary(
148
- name: string,
149
- type: "movie" | "show" | "artist",
150
- path: string,
151
- language: string = "en-US"
152
- ): Promise<void> {
153
- debugLog("PlexApi", `Creating library: ${name} (${type}) at ${path}`)
154
-
155
- const agents: Record<string, { agent: string; scanner: string }> = {
156
- movie: { agent: "tv.plex.agents.movie", scanner: "Plex Movie" },
157
- show: { agent: "tv.plex.agents.series", scanner: "Plex TV Series" },
158
- artist: { agent: "tv.plex.agents.music", scanner: "Plex Music" },
159
- }
160
-
161
- const config = agents[type]
162
- if (!config) {
163
- throw new Error(`Unknown library type: ${type}`)
164
- }
165
-
166
- const params = new URLSearchParams({
167
- name,
168
- type,
169
- agent: config.agent,
170
- scanner: config.scanner,
171
- language,
172
- location: path,
173
- })
174
-
175
- const response = await this.post<unknown>(`/library/sections?${params.toString()}`, undefined, {
176
- headers: this.getHeaders(),
177
- })
178
-
179
- if (!response.success) {
180
- throw new Error(`Failed to create library: ${response.status}`)
181
- }
182
-
183
- debugLog("PlexApi", `Library "${name}" created successfully`)
184
- }
185
-
186
- /**
187
- * Check if a library with the given path already exists
188
- */
189
- async libraryExistsForPath(path: string): Promise<boolean> {
190
- const sections = await this.getLibrarySections()
191
- return sections.some((section) => section.Location?.some((loc) => loc.path === path))
192
- }
193
-
194
- /**
195
- * Trigger a library scan for all sections
196
- */
197
- async scanAllLibraries(): Promise<void> {
198
- const sections = await this.getLibrarySections()
199
- for (const section of sections) {
200
- await fetch(`${this.baseUrl}/library/sections/${section.key}/refresh`, {
201
- method: "GET",
202
- headers: this.getHeaders(),
203
- })
204
- }
205
- debugLog("PlexApi", "Triggered scan for all libraries")
206
- }
207
-
208
- /**
209
- * Create libraries based on enabled *arr apps
210
- */
211
- private async createDefaultLibraries(enabledApps?: string[]): Promise<number> {
212
- const libraryMap = [
213
- { app: "radarr", name: "Movies", type: "movie" as const, path: "/data/media/movies" },
214
- { app: "sonarr", name: "TV Shows", type: "show" as const, path: "/data/media/tv" },
215
- { app: "lidarr", name: "Music", type: "artist" as const, path: "/data/media/music" },
216
- ]
217
-
218
- const libraries = enabledApps
219
- ? libraryMap.filter((lib) => enabledApps.includes(lib.app))
220
- : libraryMap
221
-
222
- let librariesCreated = 0
223
- for (const lib of libraries) {
224
- const exists = await this.libraryExistsForPath(lib.path)
225
- if (!exists) {
226
- try {
227
- await this.createLibrary(lib.name, lib.type, lib.path)
228
- librariesCreated++
229
- } catch (e) {
230
- debugLog("PlexApi", `Could not create library ${lib.name}: ${e}`)
231
- }
232
- }
233
- }
234
- return librariesCreated
235
- }
236
-
237
- /**
238
- * Run the auto-setup process for Plex
239
- */
240
- async setup(options: AutoSetupOptions): Promise<AutoSetupResult> {
241
- const { env, plexToken, enabledApps } = options
242
-
243
- const healthy = await this.isHealthy()
244
- if (!healthy) {
245
- return { success: false, message: "Plex server not reachable" }
246
- }
247
-
248
- if (plexToken) {
249
- this.setToken(plexToken)
250
- } else if (env["API_KEY_PLEX"]) {
251
- this.setToken(env["API_KEY_PLEX"])
252
- }
253
-
254
- try {
255
- const serverInfo = await this.getServerInfo()
256
- if (serverInfo.claimed) {
257
- const librariesCreated = await this.createDefaultLibraries(enabledApps)
258
- return {
259
- success: true,
260
- message:
261
- librariesCreated > 0
262
- ? `Already claimed, ${librariesCreated} libraries configured`
263
- : "Already claimed",
264
- data: {
265
- machineIdentifier: serverInfo.machineIdentifier,
266
- version: serverInfo.version,
267
- librariesCreated,
268
- },
269
- }
270
- }
271
- } catch (e) {
272
- const errMsg = String(e)
273
- if (errMsg.includes("401")) {
274
- return {
275
- success: true,
276
- message: "Already claimed - add API_KEY_PLEX to .env to create libraries",
277
- data: { requiresWizard: false },
278
- }
279
- }
280
- }
281
-
282
- const claimToken = env["PLEX_CLAIM"]
283
- if (!claimToken) {
284
- return {
285
- success: false,
286
- message: "No PLEX_CLAIM token. Get one from https://plex.tv/claim (4-min expiry)",
287
- }
288
- }
289
-
290
- try {
291
- await this.claimServer(claimToken)
292
- const serverInfo = await this.getServerInfo()
293
- const librariesCreated = await this.createDefaultLibraries(enabledApps)
294
-
295
- return {
296
- success: true,
297
- message: `Server claimed, ${librariesCreated} libraries configured`,
298
- data: {
299
- machineIdentifier: serverInfo.machineIdentifier,
300
- version: serverInfo.version,
301
- librariesCreated,
302
- },
303
- }
304
- } catch (error) {
305
- return { success: false, message: `${error}` }
306
- }
307
- }
308
- }