@muhammedaksam/easiarr 1.1.3 → 1.1.5
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": "1.1.
|
|
3
|
+
"version": "1.1.5",
|
|
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",
|
package/src/api/arr-api.ts
CHANGED
|
@@ -278,7 +278,7 @@ export class ArrApiClient {
|
|
|
278
278
|
})
|
|
279
279
|
}
|
|
280
280
|
|
|
281
|
-
async configureTRaSHNaming(appType: "radarr" | "sonarr"): Promise<void> {
|
|
281
|
+
async configureTRaSHNaming(appType: "radarr" | "sonarr" | "lidarr"): Promise<void> {
|
|
282
282
|
try {
|
|
283
283
|
// 1. Get current configuration to preserve ID and other fields
|
|
284
284
|
const currentConfig = await this.getNamingConfig<NamingConfig & { id?: number }>()
|
package/src/api/naming-config.ts
CHANGED
|
@@ -26,11 +26,22 @@ export interface SonarrNamingConfig {
|
|
|
26
26
|
numberStyle: string
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
export
|
|
29
|
+
export interface LidarrNamingConfig {
|
|
30
|
+
renameTracks: boolean
|
|
31
|
+
replaceIllegalCharacters: boolean
|
|
32
|
+
colonReplacementFormat: "dash" | "spaceDash" | "spaceDashSpace" | "smart" | "delete" | number
|
|
33
|
+
standardTrackFormat: string
|
|
34
|
+
multiDiscTrackFormat: string
|
|
35
|
+
artistFolderFormat: string
|
|
36
|
+
albumFolderFormat: string
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export type NamingConfig = RadarrNamingConfig | SonarrNamingConfig | LidarrNamingConfig
|
|
30
40
|
|
|
31
41
|
// TRaSH Guides Recommended Naming Schemes
|
|
32
42
|
// Source: https://trash-guides.info/Radarr/Radarr-recommended-naming-scheme/
|
|
33
43
|
// Source: https://trash-guides.info/Sonarr/Sonarr-recommended-naming-scheme/
|
|
44
|
+
// Lidarr: https://wiki.servarr.com/lidarr/settings#media-management
|
|
34
45
|
|
|
35
46
|
export const TRASH_NAMING_CONFIG = {
|
|
36
47
|
radarr: {
|
|
@@ -64,4 +75,19 @@ export const TRASH_NAMING_CONFIG = {
|
|
|
64
75
|
separator: " - ",
|
|
65
76
|
numberStyle: "S{season:00}E{episode:00}",
|
|
66
77
|
} as SonarrNamingConfig,
|
|
78
|
+
|
|
79
|
+
lidarr: {
|
|
80
|
+
renameTracks: true,
|
|
81
|
+
replaceIllegalCharacters: true,
|
|
82
|
+
colonReplacementFormat: 4, // 4 = Smart Replace (Dash or Space Dash depending on name)
|
|
83
|
+
// Standard track format: Artist - Album (Year) - Track# - Title
|
|
84
|
+
standardTrackFormat: "{Artist CleanName} - {Album CleanTitle} ({Release Year}) - {track:00} - {Track CleanTitle}",
|
|
85
|
+
// Multi-disc format includes disc number
|
|
86
|
+
multiDiscTrackFormat:
|
|
87
|
+
"{Artist CleanName} - {Album CleanTitle} ({Release Year}) - {medium:00}-{track:00} - {Track CleanTitle}",
|
|
88
|
+
// Artist folder: Artist Name
|
|
89
|
+
artistFolderFormat: "{Artist CleanName}",
|
|
90
|
+
// Album folder: Album Title (Year) [Quality]
|
|
91
|
+
albumFolderFormat: "{Album CleanTitle} ({Release Year})",
|
|
92
|
+
} as LidarrNamingConfig,
|
|
67
93
|
}
|
package/src/api/prowlarr-api.ts
CHANGED
|
@@ -240,11 +240,11 @@ export class ProwlarrClient implements IAutoSetupClient {
|
|
|
240
240
|
|
|
241
241
|
// Sync Profile management (aka App Sync Profile)
|
|
242
242
|
async getSyncProfiles(): Promise<SyncProfile[]> {
|
|
243
|
-
return this.request<SyncProfile[]>("/
|
|
243
|
+
return this.request<SyncProfile[]>("/appprofile")
|
|
244
244
|
}
|
|
245
245
|
|
|
246
246
|
async createSyncProfile(profile: Omit<SyncProfile, "id">): Promise<SyncProfile> {
|
|
247
|
-
return this.request<SyncProfile>("/
|
|
247
|
+
return this.request<SyncProfile>("/appprofile", {
|
|
248
248
|
method: "POST",
|
|
249
249
|
body: JSON.stringify(profile),
|
|
250
250
|
})
|
|
@@ -335,15 +335,55 @@ export class ProwlarrClient implements IAutoSetupClient {
|
|
|
335
335
|
await this.request(`/applications/${id}`, { method: "DELETE" })
|
|
336
336
|
}
|
|
337
337
|
|
|
338
|
+
// Update an existing application
|
|
339
|
+
async updateApplication(
|
|
340
|
+
id: number,
|
|
341
|
+
appType: ArrAppType,
|
|
342
|
+
name: string,
|
|
343
|
+
prowlarrUrl: string,
|
|
344
|
+
appUrl: string,
|
|
345
|
+
appApiKey: string,
|
|
346
|
+
syncLevel: "disabled" | "addOnly" | "fullSync" = "fullSync",
|
|
347
|
+
syncCategories: number[] = [],
|
|
348
|
+
tags: number[] = []
|
|
349
|
+
): Promise<Application> {
|
|
350
|
+
const fields: { name: string; value: unknown }[] = [
|
|
351
|
+
{ name: "prowlarrUrl", value: prowlarrUrl },
|
|
352
|
+
{ name: "baseUrl", value: appUrl },
|
|
353
|
+
{ name: "apiKey", value: appApiKey },
|
|
354
|
+
{ name: "syncCategories", value: syncCategories },
|
|
355
|
+
{ name: "syncRejectBlocklistedTorrentHashesWhileGrabbing", value: false },
|
|
356
|
+
]
|
|
357
|
+
|
|
358
|
+
return this.request<Application>(`/applications/${id}`, {
|
|
359
|
+
method: "PUT",
|
|
360
|
+
body: JSON.stringify({
|
|
361
|
+
id,
|
|
362
|
+
name,
|
|
363
|
+
syncLevel,
|
|
364
|
+
enable: true,
|
|
365
|
+
implementation: appType,
|
|
366
|
+
implementationName: appType,
|
|
367
|
+
configContract: `${appType}Settings`,
|
|
368
|
+
infoLink: `https://wiki.servarr.com/prowlarr/supported#${appType.toLowerCase()}`,
|
|
369
|
+
fields,
|
|
370
|
+
tags,
|
|
371
|
+
}),
|
|
372
|
+
})
|
|
373
|
+
}
|
|
374
|
+
|
|
338
375
|
// Sync all apps - triggers Prowlarr to push indexers to connected apps
|
|
339
376
|
async syncApplications(): Promise<void> {
|
|
340
|
-
await this.request("/
|
|
377
|
+
await this.request("/command", {
|
|
341
378
|
method: "POST",
|
|
342
|
-
body: JSON.stringify({
|
|
379
|
+
body: JSON.stringify({
|
|
380
|
+
name: "ApplicationIndexerSync",
|
|
381
|
+
forceSync: true,
|
|
382
|
+
}),
|
|
343
383
|
})
|
|
344
384
|
}
|
|
345
385
|
|
|
346
|
-
// Add *arr app
|
|
386
|
+
// Add or update *arr app
|
|
347
387
|
async addArrApp(
|
|
348
388
|
appType: ArrAppType,
|
|
349
389
|
host: string,
|
|
@@ -359,10 +399,27 @@ export class ProwlarrClient implements IAutoSetupClient {
|
|
|
359
399
|
// Check if app already exists
|
|
360
400
|
const apps = await this.getApplications()
|
|
361
401
|
const existing = apps.find((a) => a.implementation === appType)
|
|
362
|
-
|
|
363
|
-
|
|
402
|
+
|
|
403
|
+
if (existing && existing.id) {
|
|
404
|
+
// Update existing app with new syncCategories
|
|
405
|
+
debugLog(
|
|
406
|
+
"Prowlarr",
|
|
407
|
+
`Updating existing ${appType} app (id=${existing.id}) with syncCategories: ${JSON.stringify(syncCategories)}`
|
|
408
|
+
)
|
|
409
|
+
return this.updateApplication(
|
|
410
|
+
existing.id,
|
|
411
|
+
appType,
|
|
412
|
+
existing.name,
|
|
413
|
+
prowlarrUrl,
|
|
414
|
+
appUrl,
|
|
415
|
+
apiKey,
|
|
416
|
+
"fullSync",
|
|
417
|
+
syncCategories || [],
|
|
418
|
+
existing.tags || []
|
|
419
|
+
)
|
|
364
420
|
}
|
|
365
421
|
|
|
422
|
+
// Create new app
|
|
366
423
|
return this.addApplication(appType, appType, prowlarrUrl, appUrl, apiKey, "fullSync", syncCategories)
|
|
367
424
|
}
|
|
368
425
|
|
package/src/apps/registry.ts
CHANGED
|
@@ -29,7 +29,7 @@ export const APPS: Record<AppId, AppDefinition> = {
|
|
|
29
29
|
path: "/data/media/movies",
|
|
30
30
|
apiVersion: "v3",
|
|
31
31
|
},
|
|
32
|
-
prowlarrCategoryIds: [2000], // Movies
|
|
32
|
+
prowlarrCategoryIds: [2000, 2010, 2020, 2030, 2040, 2045, 2050, 2060, 2070, 2080, 2090], // Movies + all sub-categories
|
|
33
33
|
homepage: { icon: "radarr.png", widget: "radarr" },
|
|
34
34
|
},
|
|
35
35
|
|
|
@@ -54,7 +54,7 @@ export const APPS: Record<AppId, AppDefinition> = {
|
|
|
54
54
|
path: "/data/media/tv",
|
|
55
55
|
apiVersion: "v3",
|
|
56
56
|
},
|
|
57
|
-
prowlarrCategoryIds: [5000], // TV
|
|
57
|
+
prowlarrCategoryIds: [5000, 5010, 5020, 5030, 5040, 5045, 5050, 5060, 5070, 5080, 5090], // TV + all sub-categories
|
|
58
58
|
homepage: { icon: "sonarr.png", widget: "sonarr" },
|
|
59
59
|
},
|
|
60
60
|
|
|
@@ -77,7 +77,7 @@ export const APPS: Record<AppId, AppDefinition> = {
|
|
|
77
77
|
path: "/data/media/music",
|
|
78
78
|
apiVersion: "v1",
|
|
79
79
|
},
|
|
80
|
-
prowlarrCategoryIds: [3000], // Audio
|
|
80
|
+
prowlarrCategoryIds: [3000, 3010, 3020, 3030, 3040, 3050, 3060], // Audio + all sub-categories
|
|
81
81
|
homepage: { icon: "lidarr.png", widget: "lidarr" },
|
|
82
82
|
},
|
|
83
83
|
|
|
@@ -100,7 +100,7 @@ export const APPS: Record<AppId, AppDefinition> = {
|
|
|
100
100
|
path: "/data/media/books",
|
|
101
101
|
apiVersion: "v1",
|
|
102
102
|
},
|
|
103
|
-
prowlarrCategoryIds: [7000], // Books
|
|
103
|
+
prowlarrCategoryIds: [7000, 7010, 7020, 7030, 7040, 7050, 7060], // Books + all sub-categories
|
|
104
104
|
arch: {
|
|
105
105
|
deprecated: ["arm64", "arm32"],
|
|
106
106
|
warning: "Readarr is deprecated - no ARM64 support (project abandoned by upstream)",
|
|
@@ -172,7 +172,7 @@ export const APPS: Record<AppId, AppDefinition> = {
|
|
|
172
172
|
path: "/data/media/adult",
|
|
173
173
|
apiVersion: "v3",
|
|
174
174
|
},
|
|
175
|
-
prowlarrCategoryIds: [6000], // XXX
|
|
175
|
+
prowlarrCategoryIds: [6000, 6010, 6020, 6030, 6040, 6045, 6050, 6060, 6070, 6080, 6090], // XXX + all sub-categories
|
|
176
176
|
homepage: { icon: "whisparr.png", widget: "sonarr" }, // Uses sonarr widget type
|
|
177
177
|
},
|
|
178
178
|
|
|
@@ -268,7 +268,7 @@ export class FullAutoSetup extends BoxRenderable {
|
|
|
268
268
|
|
|
269
269
|
try {
|
|
270
270
|
const arrApps = this.config.apps.filter((a) => {
|
|
271
|
-
return a.enabled && (a.id === "radarr" || a.id === "sonarr")
|
|
271
|
+
return a.enabled && (a.id === "radarr" || a.id === "sonarr" || a.id === "lidarr")
|
|
272
272
|
})
|
|
273
273
|
|
|
274
274
|
for (const app of arrApps) {
|
|
@@ -282,7 +282,7 @@ export class FullAutoSetup extends BoxRenderable {
|
|
|
282
282
|
const client = new ArrApiClient("localhost", port, apiKey, def.rootFolder?.apiVersion || "v3")
|
|
283
283
|
|
|
284
284
|
try {
|
|
285
|
-
await client.configureTRaSHNaming(app.id as "radarr" | "sonarr")
|
|
285
|
+
await client.configureTRaSHNaming(app.id as "radarr" | "sonarr" | "lidarr")
|
|
286
286
|
debugLog("FullAutoSetup", `Configured naming for ${app.id}`)
|
|
287
287
|
} catch (e) {
|
|
288
288
|
debugLog("FullAutoSetup", `Failed to configure naming for ${app.id}: ${e}`)
|
|
@@ -302,7 +302,7 @@ export class FullAutoSetup extends BoxRenderable {
|
|
|
302
302
|
|
|
303
303
|
try {
|
|
304
304
|
const arrApps = this.config.apps.filter((a) => {
|
|
305
|
-
return a.enabled && (a.id === "radarr" || a.id === "sonarr")
|
|
305
|
+
return a.enabled && (a.id === "radarr" || a.id === "sonarr" || a.id === "lidarr")
|
|
306
306
|
})
|
|
307
307
|
|
|
308
308
|
for (const app of arrApps) {
|
|
@@ -313,10 +313,11 @@ export class FullAutoSetup extends BoxRenderable {
|
|
|
313
313
|
if (!def) continue
|
|
314
314
|
|
|
315
315
|
const port = app.port || def.defaultPort
|
|
316
|
-
const
|
|
316
|
+
const apiVersion = def.rootFolder?.apiVersion || "v3"
|
|
317
|
+
const client = new QualityProfileClient("localhost", port, apiKey, apiVersion)
|
|
317
318
|
|
|
318
319
|
try {
|
|
319
|
-
await client.updateTrashQualityDefinitions(app.id as "radarr" | "sonarr")
|
|
320
|
+
await client.updateTrashQualityDefinitions(app.id as "radarr" | "sonarr" | "lidarr")
|
|
320
321
|
debugLog("FullAutoSetup", `Configured quality settings for ${app.id}`)
|
|
321
322
|
} catch (e) {
|
|
322
323
|
debugLog("FullAutoSetup", `Failed to configure quality settings for ${app.id}: ${e}`)
|
|
@@ -507,7 +508,7 @@ export class FullAutoSetup extends BoxRenderable {
|
|
|
507
508
|
const port = app.port || def?.defaultPort || 7878
|
|
508
509
|
|
|
509
510
|
try {
|
|
510
|
-
await prowlarr.addArrApp(appType, app.id, port, appApiKey, "prowlarr", prowlarrPort)
|
|
511
|
+
await prowlarr.addArrApp(appType, app.id, port, appApiKey, "prowlarr", prowlarrPort, def?.prowlarrCategoryIds)
|
|
511
512
|
} catch {
|
|
512
513
|
// Skip - may already exist
|
|
513
514
|
}
|