@muhammedaksam/easiarr 0.4.2 → 0.5.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@muhammedaksam/easiarr",
3
- "version": "0.4.2",
3
+ "version": "0.5.0",
4
4
  "description": "TUI tool for generating docker-compose files for the *arr media ecosystem with 41 apps, TRaSH Guides best practices, VPN routing, and Traefik reverse proxy support",
5
5
  "module": "src/index.ts",
6
6
  "type": "module",
@@ -254,6 +254,40 @@ export class ArrApiClient {
254
254
  async deleteRemotePathMapping(id: number): Promise<void> {
255
255
  await this.request(`/remotepathmapping/${id}`, { method: "DELETE" })
256
256
  }
257
+
258
+ // ==========================================
259
+ // Health Check & Status Methods
260
+ // ==========================================
261
+
262
+ // Get health issues/warnings
263
+ async getHealth(): Promise<HealthResource[]> {
264
+ return this.request<HealthResource[]>("/health")
265
+ }
266
+
267
+ // Get disk space information for all monitored paths
268
+ async getDiskSpace(): Promise<DiskSpaceResource[]> {
269
+ return this.request<DiskSpaceResource[]>("/diskspace")
270
+ }
271
+
272
+ // Get system status (version, OS, runtime, etc.)
273
+ async getSystemStatus(): Promise<SystemResource> {
274
+ return this.request<SystemResource>("/system/status")
275
+ }
276
+
277
+ // Get all items in the download queue
278
+ async getQueueDetails(includeUnknown = true): Promise<QueueResource[]> {
279
+ const params = new URLSearchParams()
280
+ if (includeUnknown) {
281
+ params.set("includeUnknownMovieItems", "true")
282
+ }
283
+ const query = params.toString()
284
+ return this.request<QueueResource[]>(`/queue/details${query ? `?${query}` : ""}`)
285
+ }
286
+
287
+ // Get queue status summary (counts, errors, warnings)
288
+ async getQueueStatus(): Promise<QueueStatusResource> {
289
+ return this.request<QueueStatusResource>("/queue/status")
290
+ }
257
291
  }
258
292
 
259
293
  // Types for Host Config API
@@ -297,3 +331,125 @@ export interface HostConfig {
297
331
  backupRetention: number
298
332
  trustCgnatIpAddresses: boolean
299
333
  }
334
+
335
+ // ==========================================
336
+ // Health Check & Status Types
337
+ // ==========================================
338
+
339
+ // Health check result types
340
+ export type HealthCheckType = "ok" | "notice" | "warning" | "error"
341
+
342
+ export interface HealthResource {
343
+ id?: number
344
+ source: string | null
345
+ type: HealthCheckType
346
+ message: string | null
347
+ wikiUrl: string | null
348
+ }
349
+
350
+ // Disk space types
351
+ export interface DiskSpaceResource {
352
+ id?: number
353
+ path: string | null
354
+ label: string | null
355
+ freeSpace: number // int64
356
+ totalSpace: number // int64
357
+ }
358
+
359
+ // System status types
360
+ export type RuntimeMode = "console" | "service" | "tray"
361
+ export type DatabaseType = "sqLite" | "postgreSQL"
362
+
363
+ export interface SystemResource {
364
+ appName: string | null
365
+ instanceName: string | null
366
+ version: string | null
367
+ buildTime: string | null
368
+ isDebug: boolean
369
+ isProduction: boolean
370
+ isAdmin: boolean
371
+ isUserInteractive: boolean
372
+ startupPath: string | null
373
+ appData: string | null
374
+ osName: string | null
375
+ osVersion: string | null
376
+ isNetCore: boolean
377
+ isLinux: boolean
378
+ isOsx: boolean
379
+ isWindows: boolean
380
+ isDocker: boolean
381
+ mode: RuntimeMode
382
+ branch: string | null
383
+ databaseType: DatabaseType
384
+ databaseVersion: string | null
385
+ authentication: "none" | "basic" | "forms" | "external"
386
+ migrationVersion: number
387
+ urlBase: string | null
388
+ runtimeVersion: string | null
389
+ runtimeName: string | null
390
+ startTime: string | null
391
+ }
392
+
393
+ // Queue types
394
+ export type QueueStatus =
395
+ | "unknown"
396
+ | "queued"
397
+ | "paused"
398
+ | "downloading"
399
+ | "completed"
400
+ | "failed"
401
+ | "warning"
402
+ | "delay"
403
+ | "downloadClientUnavailable"
404
+ | "fallback"
405
+
406
+ export type TrackedDownloadStatus = "ok" | "warning" | "error"
407
+ export type TrackedDownloadState =
408
+ | "downloading"
409
+ | "importBlocked"
410
+ | "importPending"
411
+ | "importing"
412
+ | "imported"
413
+ | "failedPending"
414
+ | "failed"
415
+ | "ignored"
416
+
417
+ export interface QueueStatusMessage {
418
+ title: string | null
419
+ messages: string[] | null
420
+ }
421
+
422
+ export interface QueueResource {
423
+ id?: number
424
+ movieId?: number | null // Radarr
425
+ seriesId?: number | null // Sonarr
426
+ artistId?: number | null // Lidarr
427
+ authorId?: number | null // Readarr
428
+ title: string | null
429
+ size: number
430
+ sizeleft?: number
431
+ timeleft?: string | null
432
+ estimatedCompletionTime: string | null
433
+ added: string | null
434
+ status: QueueStatus
435
+ trackedDownloadStatus: TrackedDownloadStatus
436
+ trackedDownloadState: TrackedDownloadState
437
+ statusMessages?: QueueStatusMessage[] | null
438
+ errorMessage: string | null
439
+ downloadId: string | null
440
+ protocol: "unknown" | "usenet" | "torrent"
441
+ downloadClient: string | null
442
+ indexer: string | null
443
+ outputPath: string | null
444
+ }
445
+
446
+ export interface QueueStatusResource {
447
+ id?: number
448
+ totalCount: number
449
+ count: number
450
+ unknownCount: number
451
+ errors: boolean
452
+ warnings: boolean
453
+ unknownErrors: boolean
454
+ unknownWarnings: boolean
455
+ }
@@ -14,6 +14,33 @@ export interface IndexerProxy {
14
14
  fields: { name: string; value: unknown }[]
15
15
  }
16
16
 
17
+ export interface ProwlarrIndexerSchema {
18
+ id?: number
19
+ name: string
20
+ implementation: string
21
+ configContract: string
22
+ fields: { name: string; value?: unknown }[]
23
+ tags: number[]
24
+ enable: boolean
25
+ privacy: "public" | "private" | "semi-private"
26
+ protocol: "torrent" | "usenet"
27
+ priority: number
28
+ capabilities?: {
29
+ categories: { id: number; name: string; subCategories?: { id: number; name: string }[] }[]
30
+ }
31
+ }
32
+
33
+ export interface ProwlarrIndexer {
34
+ id?: number
35
+ name: string
36
+ fields: { name: string; value?: unknown }[]
37
+ tags: number[]
38
+ enable: boolean
39
+ protocol: string
40
+ implementation: string
41
+ configContract: string
42
+ }
43
+
17
44
  export interface SyncProfile {
18
45
  id?: number
19
46
  name: string
@@ -188,6 +215,28 @@ export class ProwlarrClient {
188
215
  await this.request(`/indexerproxy/${id}`, { method: "DELETE" })
189
216
  }
190
217
 
218
+ // Indexer Management
219
+ async getIndexerSchemas(): Promise<ProwlarrIndexerSchema[]> {
220
+ return this.request<ProwlarrIndexerSchema[]>("/indexer/schema")
221
+ }
222
+
223
+ async getIndexers(): Promise<ProwlarrIndexer[]> {
224
+ return this.request<ProwlarrIndexer[]>("/indexer")
225
+ }
226
+
227
+ async createIndexer(indexer: ProwlarrIndexerSchema): Promise<ProwlarrIndexer> {
228
+ // Ensure required fields are set
229
+ const payload = {
230
+ ...indexer,
231
+ id: undefined, // Create new
232
+ appProfileId: 1, // Default profile
233
+ }
234
+ return this.request<ProwlarrIndexer>("/indexer", {
235
+ method: "POST",
236
+ body: JSON.stringify(payload),
237
+ })
238
+ }
239
+
191
240
  // Sync Profile management (aka App Sync Profile)
192
241
  async getSyncProfiles(): Promise<SyncProfile[]> {
193
242
  return this.request<SyncProfile[]>("/appsyncprofile")
@@ -258,23 +307,14 @@ export class ProwlarrClient {
258
307
  prowlarrUrl: string,
259
308
  appUrl: string,
260
309
  appApiKey: string,
261
- syncLevel: "disabled" | "addOnly" | "fullSync" = "fullSync"
310
+ syncLevel: "disabled" | "addOnly" | "fullSync" = "fullSync",
311
+ syncCategories: number[] = []
262
312
  ): Promise<Application> {
263
- // Default sync categories for each app type
264
- // Radarr: Movies (2000, 2010, etc), Sonarr: TV (5000, 5010, etc)
265
- // Lidarr: Audio (3000), Readarr: Books (7000, 8010)
266
- const syncCategoriesMap: Record<ArrAppType, number[]> = {
267
- Radarr: [2000, 2010, 2020, 2030, 2040, 2045, 2050, 2060, 2070, 2080],
268
- Sonarr: [5000, 5010, 5020, 5030, 5040, 5045, 5050, 5060, 5070, 5080],
269
- Lidarr: [3000, 3010, 3020, 3030, 3040],
270
- Readarr: [7000, 7010, 7020, 7030, 8000, 8010, 8020],
271
- }
272
-
273
313
  const fields: { name: string; value: unknown }[] = [
274
314
  { name: "prowlarrUrl", value: prowlarrUrl },
275
315
  { name: "baseUrl", value: appUrl },
276
316
  { name: "apiKey", value: appApiKey },
277
- { name: "syncCategories", value: syncCategoriesMap[appType] || [] },
317
+ { name: "syncCategories", value: syncCategories },
278
318
  ]
279
319
 
280
320
  return this.request<Application>("/applications", {
@@ -309,7 +349,8 @@ export class ProwlarrClient {
309
349
  port: number,
310
350
  apiKey: string,
311
351
  prowlarrHost: string,
312
- prowlarrPort: number
352
+ prowlarrPort: number,
353
+ syncCategories?: number[]
313
354
  ): Promise<Application> {
314
355
  const prowlarrUrl = `http://${prowlarrHost}:${prowlarrPort}`
315
356
  const appUrl = `http://${host}:${port}`
@@ -321,6 +362,398 @@ export class ProwlarrClient {
321
362
  return existing
322
363
  }
323
364
 
324
- return this.addApplication(appType, appType, prowlarrUrl, appUrl, apiKey)
365
+ return this.addApplication(appType, appType, prowlarrUrl, appUrl, apiKey, "fullSync", syncCategories)
325
366
  }
326
367
  }
368
+
369
+ export const PROWLARR_CATEGORIES = [
370
+ {
371
+ id: 1000,
372
+ name: "Console",
373
+ subCategories: [
374
+ {
375
+ id: 1010,
376
+ name: "Console/NDS",
377
+ subCategories: [],
378
+ },
379
+ {
380
+ id: 1020,
381
+ name: "Console/PSP",
382
+ subCategories: [],
383
+ },
384
+ {
385
+ id: 1030,
386
+ name: "Console/Wii",
387
+ subCategories: [],
388
+ },
389
+ {
390
+ id: 1040,
391
+ name: "Console/XBox",
392
+ subCategories: [],
393
+ },
394
+ {
395
+ id: 1050,
396
+ name: "Console/XBox 360",
397
+ subCategories: [],
398
+ },
399
+ {
400
+ id: 1060,
401
+ name: "Console/Wiiware",
402
+ subCategories: [],
403
+ },
404
+ {
405
+ id: 1070,
406
+ name: "Console/XBox 360 DLC",
407
+ subCategories: [],
408
+ },
409
+ {
410
+ id: 1080,
411
+ name: "Console/PS3",
412
+ subCategories: [],
413
+ },
414
+ {
415
+ id: 1090,
416
+ name: "Console/Other",
417
+ subCategories: [],
418
+ },
419
+ {
420
+ id: 1110,
421
+ name: "Console/3DS",
422
+ subCategories: [],
423
+ },
424
+ {
425
+ id: 1120,
426
+ name: "Console/PS Vita",
427
+ subCategories: [],
428
+ },
429
+ {
430
+ id: 1130,
431
+ name: "Console/WiiU",
432
+ subCategories: [],
433
+ },
434
+ {
435
+ id: 1140,
436
+ name: "Console/XBox One",
437
+ subCategories: [],
438
+ },
439
+ {
440
+ id: 1180,
441
+ name: "Console/PS4",
442
+ subCategories: [],
443
+ },
444
+ ],
445
+ },
446
+ {
447
+ id: 2000,
448
+ name: "Movies",
449
+ subCategories: [
450
+ {
451
+ id: 2010,
452
+ name: "Movies/Foreign",
453
+ subCategories: [],
454
+ },
455
+ {
456
+ id: 2020,
457
+ name: "Movies/Other",
458
+ subCategories: [],
459
+ },
460
+ {
461
+ id: 2030,
462
+ name: "Movies/SD",
463
+ subCategories: [],
464
+ },
465
+ {
466
+ id: 2040,
467
+ name: "Movies/HD",
468
+ subCategories: [],
469
+ },
470
+ {
471
+ id: 2045,
472
+ name: "Movies/UHD",
473
+ subCategories: [],
474
+ },
475
+ {
476
+ id: 2050,
477
+ name: "Movies/BluRay",
478
+ subCategories: [],
479
+ },
480
+ {
481
+ id: 2060,
482
+ name: "Movies/3D",
483
+ subCategories: [],
484
+ },
485
+ {
486
+ id: 2070,
487
+ name: "Movies/DVD",
488
+ subCategories: [],
489
+ },
490
+ {
491
+ id: 2080,
492
+ name: "Movies/WEB-DL",
493
+ subCategories: [],
494
+ },
495
+ {
496
+ id: 2090,
497
+ name: "Movies/x265",
498
+ subCategories: [],
499
+ },
500
+ ],
501
+ },
502
+ {
503
+ id: 3000,
504
+ name: "Audio",
505
+ subCategories: [
506
+ {
507
+ id: 3010,
508
+ name: "Audio/MP3",
509
+ subCategories: [],
510
+ },
511
+ {
512
+ id: 3020,
513
+ name: "Audio/Video",
514
+ subCategories: [],
515
+ },
516
+ {
517
+ id: 3030,
518
+ name: "Audio/Audiobook",
519
+ subCategories: [],
520
+ },
521
+ {
522
+ id: 3040,
523
+ name: "Audio/Lossless",
524
+ subCategories: [],
525
+ },
526
+ {
527
+ id: 3050,
528
+ name: "Audio/Other",
529
+ subCategories: [],
530
+ },
531
+ {
532
+ id: 3060,
533
+ name: "Audio/Foreign",
534
+ subCategories: [],
535
+ },
536
+ ],
537
+ },
538
+ {
539
+ id: 4000,
540
+ name: "PC",
541
+ subCategories: [
542
+ {
543
+ id: 4010,
544
+ name: "PC/0day",
545
+ subCategories: [],
546
+ },
547
+ {
548
+ id: 4020,
549
+ name: "PC/ISO",
550
+ subCategories: [],
551
+ },
552
+ {
553
+ id: 4030,
554
+ name: "PC/Mac",
555
+ subCategories: [],
556
+ },
557
+ {
558
+ id: 4040,
559
+ name: "PC/Mobile-Other",
560
+ subCategories: [],
561
+ },
562
+ {
563
+ id: 4050,
564
+ name: "PC/Games",
565
+ subCategories: [],
566
+ },
567
+ {
568
+ id: 4060,
569
+ name: "PC/Mobile-iOS",
570
+ subCategories: [],
571
+ },
572
+ {
573
+ id: 4070,
574
+ name: "PC/Mobile-Android",
575
+ subCategories: [],
576
+ },
577
+ ],
578
+ },
579
+ {
580
+ id: 5000,
581
+ name: "TV",
582
+ subCategories: [
583
+ {
584
+ id: 5010,
585
+ name: "TV/WEB-DL",
586
+ subCategories: [],
587
+ },
588
+ {
589
+ id: 5020,
590
+ name: "TV/Foreign",
591
+ subCategories: [],
592
+ },
593
+ {
594
+ id: 5030,
595
+ name: "TV/SD",
596
+ subCategories: [],
597
+ },
598
+ {
599
+ id: 5040,
600
+ name: "TV/HD",
601
+ subCategories: [],
602
+ },
603
+ {
604
+ id: 5045,
605
+ name: "TV/UHD",
606
+ subCategories: [],
607
+ },
608
+ {
609
+ id: 5050,
610
+ name: "TV/Other",
611
+ subCategories: [],
612
+ },
613
+ {
614
+ id: 5060,
615
+ name: "TV/Sport",
616
+ subCategories: [],
617
+ },
618
+ {
619
+ id: 5070,
620
+ name: "TV/Anime",
621
+ subCategories: [],
622
+ },
623
+ {
624
+ id: 5080,
625
+ name: "TV/Documentary",
626
+ subCategories: [],
627
+ },
628
+ {
629
+ id: 5090,
630
+ name: "TV/x265",
631
+ subCategories: [],
632
+ },
633
+ ],
634
+ },
635
+ {
636
+ id: 6000,
637
+ name: "XXX",
638
+ subCategories: [
639
+ {
640
+ id: 6010,
641
+ name: "XXX/DVD",
642
+ subCategories: [],
643
+ },
644
+ {
645
+ id: 6020,
646
+ name: "XXX/WMV",
647
+ subCategories: [],
648
+ },
649
+ {
650
+ id: 6030,
651
+ name: "XXX/XviD",
652
+ subCategories: [],
653
+ },
654
+ {
655
+ id: 6040,
656
+ name: "XXX/x264",
657
+ subCategories: [],
658
+ },
659
+ {
660
+ id: 6045,
661
+ name: "XXX/UHD",
662
+ subCategories: [],
663
+ },
664
+ {
665
+ id: 6050,
666
+ name: "XXX/Pack",
667
+ subCategories: [],
668
+ },
669
+ {
670
+ id: 6060,
671
+ name: "XXX/ImageSet",
672
+ subCategories: [],
673
+ },
674
+ {
675
+ id: 6070,
676
+ name: "XXX/Other",
677
+ subCategories: [],
678
+ },
679
+ {
680
+ id: 6080,
681
+ name: "XXX/SD",
682
+ subCategories: [],
683
+ },
684
+ {
685
+ id: 6090,
686
+ name: "XXX/WEB-DL",
687
+ subCategories: [],
688
+ },
689
+ ],
690
+ },
691
+ {
692
+ id: 7000,
693
+ name: "Books",
694
+ subCategories: [
695
+ {
696
+ id: 7010,
697
+ name: "Books/Mags",
698
+ subCategories: [],
699
+ },
700
+ {
701
+ id: 7020,
702
+ name: "Books/EBook",
703
+ subCategories: [],
704
+ },
705
+ {
706
+ id: 7030,
707
+ name: "Books/Comics",
708
+ subCategories: [],
709
+ },
710
+ {
711
+ id: 7040,
712
+ name: "Books/Technical",
713
+ subCategories: [],
714
+ },
715
+ {
716
+ id: 7050,
717
+ name: "Books/Other",
718
+ subCategories: [],
719
+ },
720
+ {
721
+ id: 7060,
722
+ name: "Books/Foreign",
723
+ subCategories: [],
724
+ },
725
+ ],
726
+ },
727
+ {
728
+ id: 8000,
729
+ name: "Other",
730
+ subCategories: [
731
+ {
732
+ id: 8010,
733
+ name: "Other/Misc",
734
+ subCategories: [],
735
+ },
736
+ {
737
+ id: 8020,
738
+ name: "Other/Hashed",
739
+ subCategories: [],
740
+ },
741
+ ],
742
+ },
743
+ {
744
+ id: 0,
745
+ name: "Other",
746
+ subCategories: [
747
+ {
748
+ id: 10,
749
+ name: "Other/Misc",
750
+ subCategories: [],
751
+ },
752
+ {
753
+ id: 20,
754
+ name: "Other/Hashed",
755
+ subCategories: [],
756
+ },
757
+ ],
758
+ },
759
+ ]
@@ -29,6 +29,7 @@ export const APPS: Record<AppId, AppDefinition> = {
29
29
  path: "/data/media/movies",
30
30
  apiVersion: "v3",
31
31
  },
32
+ prowlarrCategoryIds: [2000, 2010, 2020, 2030, 2040, 2045, 2050, 2060, 2070, 2080, 2090],
32
33
  },
33
34
 
34
35
  sonarr: {
@@ -52,6 +53,7 @@ export const APPS: Record<AppId, AppDefinition> = {
52
53
  path: "/data/media/tv",
53
54
  apiVersion: "v3",
54
55
  },
56
+ prowlarrCategoryIds: [5000, 5010, 5020, 5030, 5040, 5045, 5050, 5060, 5070, 5080, 5090],
55
57
  },
56
58
 
57
59
  lidarr: {
@@ -73,6 +75,7 @@ export const APPS: Record<AppId, AppDefinition> = {
73
75
  path: "/data/media/music",
74
76
  apiVersion: "v1",
75
77
  },
78
+ prowlarrCategoryIds: [3000, 3010, 3020, 3030, 3040, 3050, 3060],
76
79
  },
77
80
 
78
81
  readarr: {
@@ -94,6 +97,7 @@ export const APPS: Record<AppId, AppDefinition> = {
94
97
  path: "/data/media/books",
95
98
  apiVersion: "v1",
96
99
  },
100
+ prowlarrCategoryIds: [7000, 7010, 7020, 7030, 7040, 7050, 7060],
97
101
  arch: {
98
102
  deprecated: ["arm64", "arm32"],
99
103
  warning: "Readarr is deprecated - no ARM64 support (project abandoned by upstream)",
@@ -161,6 +165,7 @@ export const APPS: Record<AppId, AppDefinition> = {
161
165
  path: "/data/media/adult",
162
166
  apiVersion: "v3",
163
167
  },
168
+ prowlarrCategoryIds: [6000, 6010, 6020, 6030, 6040, 6045, 6050, 6060, 6070, 6080, 6090],
164
169
  },
165
170
 
166
171
  audiobookshelf: {