@muhammedaksam/easiarr 1.4.0 → 1.6.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.
Files changed (109) hide show
  1. package/dist/index.js +29750 -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,488 +0,0 @@
1
- /**
2
- * Prowlarr API Client
3
- * Manages Indexer Proxies, Sync Profiles, and FlareSolverr integration
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
- export interface IndexerProxy {
11
- id?: number
12
- name: string
13
- tags: number[]
14
- implementation: string
15
- configContract: string
16
- fields: { name: string; value: unknown }[]
17
- }
18
-
19
- export interface ProwlarrIndexerSchema {
20
- id?: number
21
- name: string
22
- implementation: string
23
- configContract: string
24
- fields: { name: string; value?: unknown }[]
25
- tags: number[]
26
- enable: boolean
27
- privacy: "public" | "private" | "semi-private"
28
- protocol: "torrent" | "usenet"
29
- priority: number
30
- capabilities?: {
31
- categories: { id: number; name: string; subCategories?: { id: number; name: string }[] }[]
32
- }
33
- }
34
-
35
- export interface ProwlarrIndexer {
36
- id?: number
37
- name: string
38
- fields: { name: string; value?: unknown }[]
39
- tags: number[]
40
- enable: boolean
41
- protocol: string
42
- implementation: string
43
- configContract: string
44
- }
45
-
46
- export interface SyncProfile {
47
- id?: number
48
- name: string
49
- enableRss: boolean
50
- enableInteractiveSearch: boolean
51
- enableAutomaticSearch: boolean
52
- minimumSeeders: number
53
- }
54
-
55
- export interface Tag {
56
- id: number
57
- label: string
58
- }
59
-
60
- export interface Application {
61
- id?: number
62
- name: string
63
- syncLevel: "disabled" | "addOnly" | "fullSync"
64
- implementation: string
65
- configContract: string
66
- fields: { name: string; value: unknown }[]
67
- tags: number[]
68
- }
69
-
70
- export type ArrAppType = "Radarr" | "Sonarr" | "Lidarr" | "Readarr" | "Whisparr" | "Mylar"
71
-
72
- export class ProwlarrClient extends BaseApiClient implements IAutoSetupClient {
73
- protected readonly logPrefix = "Prowlarr"
74
- private apiKey: string
75
-
76
- constructor(host: string, port: number, apiKey: string) {
77
- super(host, port)
78
- this.apiKey = apiKey
79
- }
80
-
81
- private async request<T>(endpoint: string, options: RequestInit = {}): Promise<T> {
82
- const url = `${this.baseUrl}/api/v1${endpoint}`
83
- const headers: Record<string, string> = {
84
- "X-Api-Key": this.apiKey,
85
- "Content-Type": "application/json",
86
- ...((options.headers as Record<string, string>) || {}),
87
- }
88
-
89
- debugLog("Prowlarr", `${options.method || "GET"} ${url}`)
90
-
91
- const response = await fetch(url, { ...options, headers })
92
- const text = await response.text()
93
-
94
- if (!response.ok) {
95
- throw new Error(`Prowlarr API request failed: ${response.status} ${response.statusText}`)
96
- }
97
-
98
- if (!text) return {} as T
99
- return JSON.parse(text) as T
100
- }
101
-
102
- // Health check
103
- async isHealthy(): Promise<boolean> {
104
- try {
105
- await this.request("/health")
106
- return true
107
- } catch {
108
- return false
109
- }
110
- }
111
-
112
- // Tag management
113
- async getTags(): Promise<Tag[]> {
114
- return this.request<Tag[]>("/tag")
115
- }
116
-
117
- async createTag(label: string): Promise<Tag> {
118
- return this.request<Tag>("/tag", {
119
- method: "POST",
120
- body: JSON.stringify({ label }),
121
- })
122
- }
123
-
124
- async getOrCreateTag(label: string): Promise<Tag> {
125
- const tags = await this.getTags()
126
- const existing = tags.find((t) => t.label.toLowerCase() === label.toLowerCase())
127
- if (existing) return existing
128
- return this.createTag(label)
129
- }
130
-
131
- // Indexer Proxy management
132
- async getIndexerProxies(): Promise<IndexerProxy[]> {
133
- return this.request<IndexerProxy[]>("/indexerproxy")
134
- }
135
-
136
- async addHttpProxy(
137
- name: string,
138
- host: string,
139
- port: number,
140
- tags: number[] = [],
141
- username?: string,
142
- password?: string
143
- ): Promise<IndexerProxy> {
144
- const fields: { name: string; value: unknown }[] = [
145
- { name: "host", value: host },
146
- { name: "port", value: port },
147
- { name: "username", value: username || "" },
148
- { name: "password", value: password || "" },
149
- ]
150
-
151
- return this.request<IndexerProxy>("/indexerproxy", {
152
- method: "POST",
153
- body: JSON.stringify({
154
- name,
155
- tags,
156
- implementation: "Http",
157
- configContract: "HttpSettings",
158
- fields,
159
- }),
160
- })
161
- }
162
-
163
- async addSocks5Proxy(
164
- name: string,
165
- host: string,
166
- port: number,
167
- tags: number[] = [],
168
- username?: string,
169
- password?: string
170
- ): Promise<IndexerProxy> {
171
- const fields: { name: string; value: unknown }[] = [
172
- { name: "host", value: host },
173
- { name: "port", value: port },
174
- { name: "username", value: username || "" },
175
- { name: "password", value: password || "" },
176
- ]
177
-
178
- return this.request<IndexerProxy>("/indexerproxy", {
179
- method: "POST",
180
- body: JSON.stringify({
181
- name,
182
- tags,
183
- implementation: "Socks5",
184
- configContract: "Socks5Settings",
185
- fields,
186
- }),
187
- })
188
- }
189
-
190
- async addFlareSolverr(
191
- name: string,
192
- host: string,
193
- tags: number[] = [],
194
- requestTimeout = 60
195
- ): Promise<IndexerProxy> {
196
- const fields: { name: string; value: unknown }[] = [
197
- { name: "host", value: host },
198
- { name: "requestTimeout", value: requestTimeout },
199
- ]
200
-
201
- return this.request<IndexerProxy>("/indexerproxy", {
202
- method: "POST",
203
- body: JSON.stringify({
204
- name,
205
- tags,
206
- implementation: "FlareSolverr",
207
- configContract: "FlareSolverrSettings",
208
- fields,
209
- }),
210
- })
211
- }
212
-
213
- async deleteIndexerProxy(id: number): Promise<void> {
214
- await this.request(`/indexerproxy/${id}`, { method: "DELETE" })
215
- }
216
-
217
- // Indexer Management
218
- async getIndexerSchemas(): Promise<ProwlarrIndexerSchema[]> {
219
- return this.request<ProwlarrIndexerSchema[]>("/indexer/schema")
220
- }
221
-
222
- async getIndexers(): Promise<ProwlarrIndexer[]> {
223
- return this.request<ProwlarrIndexer[]>("/indexer")
224
- }
225
-
226
- async createIndexer(indexer: ProwlarrIndexerSchema): Promise<ProwlarrIndexer> {
227
- const payload = {
228
- ...indexer,
229
- id: undefined,
230
- appProfileId: 1,
231
- }
232
- return this.request<ProwlarrIndexer>("/indexer", {
233
- method: "POST",
234
- body: JSON.stringify(payload),
235
- })
236
- }
237
-
238
- // Sync Profile management
239
- async getSyncProfiles(): Promise<SyncProfile[]> {
240
- return this.request<SyncProfile[]>("/appprofile")
241
- }
242
-
243
- async createSyncProfile(profile: Omit<SyncProfile, "id">): Promise<SyncProfile> {
244
- return this.request<SyncProfile>("/appprofile", {
245
- method: "POST",
246
- body: JSON.stringify(profile),
247
- })
248
- }
249
-
250
- async createLimitedAPISyncProfiles(): Promise<{
251
- automatic: SyncProfile
252
- interactive: SyncProfile
253
- }> {
254
- const existingProfiles = await this.getSyncProfiles()
255
- const findByName = (name: string) => existingProfiles.find((p) => p.name === name)
256
-
257
- let automatic = findByName("Automatic Search")
258
- if (!automatic) {
259
- automatic = await this.createSyncProfile({
260
- name: "Automatic Search",
261
- enableRss: false,
262
- enableInteractiveSearch: true,
263
- enableAutomaticSearch: true,
264
- minimumSeeders: 1,
265
- })
266
- }
267
-
268
- let interactive = findByName("Interactive Search")
269
- if (!interactive) {
270
- interactive = await this.createSyncProfile({
271
- name: "Interactive Search",
272
- enableRss: false,
273
- enableInteractiveSearch: true,
274
- enableAutomaticSearch: false,
275
- minimumSeeders: 1,
276
- })
277
- }
278
-
279
- return { automatic, interactive }
280
- }
281
-
282
- async configureFlareSolverr(flareSolverrHost: string): Promise<void> {
283
- const tag = await this.getOrCreateTag("flaresolverr")
284
- const proxies = await this.getIndexerProxies()
285
- const existingFS = proxies.find((p) => p.implementation === "FlareSolverr")
286
-
287
- if (!existingFS) {
288
- await this.addFlareSolverr("FlareSolverr", flareSolverrHost, [tag.id])
289
- }
290
- }
291
-
292
- // Application management
293
- async getApplications(): Promise<Application[]> {
294
- return this.request<Application[]>("/applications")
295
- }
296
-
297
- async addApplication(
298
- appType: ArrAppType,
299
- name: string,
300
- prowlarrUrl: string,
301
- appUrl: string,
302
- appApiKey: string,
303
- syncLevel: "disabled" | "addOnly" | "fullSync" = "fullSync",
304
- syncCategories: number[] = []
305
- ): Promise<Application> {
306
- const fields: { name: string; value: unknown }[] = [
307
- { name: "prowlarrUrl", value: prowlarrUrl },
308
- { name: "baseUrl", value: appUrl },
309
- { name: "apiKey", value: appApiKey },
310
- { name: "syncCategories", value: syncCategories },
311
- ]
312
-
313
- return this.request<Application>("/applications", {
314
- method: "POST",
315
- body: JSON.stringify({
316
- name,
317
- syncLevel,
318
- implementation: appType,
319
- configContract: `${appType}Settings`,
320
- fields,
321
- tags: [],
322
- }),
323
- })
324
- }
325
-
326
- async deleteApplication(id: number): Promise<void> {
327
- await this.request(`/applications/${id}`, { method: "DELETE" })
328
- }
329
-
330
- async updateApplication(
331
- id: number,
332
- appType: ArrAppType,
333
- name: string,
334
- prowlarrUrl: string,
335
- appUrl: string,
336
- appApiKey: string,
337
- syncLevel: "disabled" | "addOnly" | "fullSync" = "fullSync",
338
- syncCategories: number[] = [],
339
- tags: number[] = []
340
- ): Promise<Application> {
341
- const fields: { name: string; value: unknown }[] = [
342
- { name: "prowlarrUrl", value: prowlarrUrl },
343
- { name: "baseUrl", value: appUrl },
344
- { name: "apiKey", value: appApiKey },
345
- { name: "syncCategories", value: syncCategories },
346
- { name: "syncRejectBlocklistedTorrentHashesWhileGrabbing", value: false },
347
- ]
348
-
349
- return this.request<Application>(`/applications/${id}`, {
350
- method: "PUT",
351
- body: JSON.stringify({
352
- id,
353
- name,
354
- syncLevel,
355
- enable: true,
356
- implementation: appType,
357
- implementationName: appType,
358
- configContract: `${appType}Settings`,
359
- infoLink: `https://wiki.servarr.com/prowlarr/supported#${appType.toLowerCase()}`,
360
- fields,
361
- tags,
362
- }),
363
- })
364
- }
365
-
366
- async syncApplications(): Promise<void> {
367
- await this.request("/command", {
368
- method: "POST",
369
- body: JSON.stringify({
370
- name: "ApplicationIndexerSync",
371
- forceSync: true,
372
- }),
373
- })
374
- }
375
-
376
- async addArrApp(
377
- appType: ArrAppType,
378
- host: string,
379
- port: number,
380
- apiKey: string,
381
- prowlarrHost: string,
382
- prowlarrPort: number,
383
- syncCategories?: number[]
384
- ): Promise<Application> {
385
- const prowlarrUrl = `http://${prowlarrHost}:${prowlarrPort}`
386
- const appUrl = `http://${host}:${port}`
387
-
388
- const apps = await this.getApplications()
389
- const existing = apps.find((a) => a.implementation === appType)
390
-
391
- if (existing && existing.id) {
392
- return this.updateApplication(
393
- existing.id,
394
- appType,
395
- existing.name,
396
- prowlarrUrl,
397
- appUrl,
398
- apiKey,
399
- "fullSync",
400
- syncCategories || [],
401
- existing.tags || []
402
- )
403
- }
404
-
405
- return this.addApplication(
406
- appType,
407
- appType,
408
- prowlarrUrl,
409
- appUrl,
410
- apiKey,
411
- "fullSync",
412
- syncCategories
413
- )
414
- }
415
-
416
- async isInitialized(): Promise<boolean> {
417
- try {
418
- const indexers = await this.getIndexers()
419
- return indexers.length > 0
420
- } catch {
421
- return false
422
- }
423
- }
424
-
425
- async setup(_options: AutoSetupOptions): Promise<AutoSetupResult> {
426
- try {
427
- const healthy = await this.isHealthy()
428
- if (!healthy) {
429
- return { success: false, message: "Prowlarr not reachable" }
430
- }
431
-
432
- const indexers = await this.getIndexers()
433
- const apps = await this.getApplications()
434
- const proxies = await this.getIndexerProxies()
435
-
436
- return {
437
- success: true,
438
- message: indexers.length > 0 ? "Configured" : "Ready for indexer setup",
439
- data: {
440
- indexerCount: indexers.length,
441
- appCount: apps.length,
442
- proxyCount: proxies.length,
443
- },
444
- }
445
- } catch (error) {
446
- return { success: false, message: `${error}` }
447
- }
448
- }
449
- }
450
-
451
- export const PROWLARR_CATEGORIES = [
452
- { id: 1000, name: "Console", subCategories: [] as { id: number; name: string }[] },
453
- {
454
- id: 2000,
455
- name: "Movies",
456
- subCategories: [
457
- { id: 2010, name: "Movies/Foreign" },
458
- { id: 2020, name: "Movies/Other" },
459
- { id: 2030, name: "Movies/SD" },
460
- { id: 2040, name: "Movies/HD" },
461
- { id: 2045, name: "Movies/UHD" },
462
- { id: 2050, name: "Movies/BluRay" },
463
- { id: 2060, name: "Movies/3D" },
464
- { id: 2070, name: "Movies/DVD" },
465
- { id: 2080, name: "Movies/WEB-DL" },
466
- ],
467
- },
468
- { id: 3000, name: "Audio", subCategories: [] as { id: number; name: string }[] },
469
- { id: 4000, name: "PC", subCategories: [] as { id: number; name: string }[] },
470
- {
471
- id: 5000,
472
- name: "TV",
473
- subCategories: [
474
- { id: 5010, name: "TV/WEB-DL" },
475
- { id: 5020, name: "TV/Foreign" },
476
- { id: 5030, name: "TV/SD" },
477
- { id: 5040, name: "TV/HD" },
478
- { id: 5045, name: "TV/UHD" },
479
- { id: 5050, name: "TV/Other" },
480
- { id: 5060, name: "TV/Sport" },
481
- { id: 5070, name: "TV/Anime" },
482
- { id: 5080, name: "TV/Documentary" },
483
- ],
484
- },
485
- { id: 6000, name: "XXX", subCategories: [] as { id: number; name: string }[] },
486
- { id: 7000, name: "Books", subCategories: [] as { id: number; name: string }[] },
487
- { id: 8000, name: "Other", subCategories: [] as { id: number; name: string }[] },
488
- ]