@muhammedaksam/easiarr 1.1.4 → 1.1.6

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.4",
3
+ "version": "1.1.6",
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",
@@ -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[]>("/appsyncprofile")
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>("/appsyncprofile", {
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("/applications/action/sync", {
377
+ await this.request("/command", {
341
378
  method: "POST",
342
- body: JSON.stringify({}), // API requires non-empty body
379
+ body: JSON.stringify({
380
+ name: "ApplicationIndexerSync",
381
+ forceSync: true,
382
+ }),
343
383
  })
344
384
  }
345
385
 
346
- // Add *arr app with auto-detection
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
- if (existing) {
363
- return existing
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
 
@@ -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
 
@@ -320,8 +320,9 @@ export const APPS: Record<AppId, AppDefinition> = {
320
320
  category: "downloader",
321
321
  defaultPort: 5030,
322
322
  image: "slskd/slskd",
323
- puid: 13016,
324
- pgid: 13000,
323
+ puid: 0, // Uses Docker user: directive instead
324
+ pgid: 0,
325
+ useDockerUser: true, // Use Docker's user: directive for file permissions
325
326
  volumes: (root) => [`${root}/config/slskd:/app`, `${root}/data:/data`],
326
327
  secondaryPorts: ["5031:5031", "50300:50300"],
327
328
  environment: {
@@ -25,6 +25,8 @@ export interface ComposeService {
25
25
  devices?: string[]
26
26
  cap_add?: string[]
27
27
  command?: string
28
+ /** Docker user directive (e.g., "1000:1000") for apps that don't support PUID/PGID */
29
+ user?: string
28
30
  }
29
31
 
30
32
  export interface ComposeFile {
@@ -157,6 +159,15 @@ function buildService(appDef: ReturnType<typeof getApp>, appConfig: AppConfig, c
157
159
  // Add command (e.g., cloudflared)
158
160
  if (appDef.command) service.command = appDef.command
159
161
 
162
+ // Use Docker user directive for apps that don't support PUID/PGID (e.g., slskd)
163
+ if (appDef.useDockerUser) {
164
+ service.user = "${PUID}:${PGID}"
165
+ // Remove PUID/PGID env vars since they're not used
166
+ delete service.environment.PUID
167
+ delete service.environment.PGID
168
+ delete service.environment.UMASK
169
+ }
170
+
160
171
  // Plex uses network_mode: host
161
172
  if (appDef.id === "plex") {
162
173
  service.network_mode = "host"
@@ -60,6 +60,11 @@ export function generateServiceYaml(name: string, service: ComposeService): stri
60
60
  }
61
61
  }
62
62
 
63
+ // User directive (for apps like slskd that don't support PUID/PGID)
64
+ if (service.user) {
65
+ yaml += ` user: ${service.user}\n`
66
+ }
67
+
63
68
  yaml += ` restart: ${service.restart}\n\n`
64
69
 
65
70
  return yaml
@@ -210,6 +210,8 @@ export interface AppDefinition {
210
210
  homepage?: HomepageMeta
211
211
  /** Auto-setup capability metadata */
212
212
  autoSetup?: AutoSetupCapability
213
+ /** Use Docker's user: directive instead of PUID/PGID env vars (e.g., slskd) */
214
+ useDockerUser?: boolean
213
215
  }
214
216
 
215
217
  /** Auto-setup capability for an app */
@@ -508,7 +508,7 @@ export class FullAutoSetup extends BoxRenderable {
508
508
  const port = app.port || def?.defaultPort || 7878
509
509
 
510
510
  try {
511
- await prowlarr.addArrApp(appType, app.id, port, appApiKey, "prowlarr", prowlarrPort)
511
+ await prowlarr.addArrApp(appType, app.id, port, appApiKey, "prowlarr", prowlarrPort, def?.prowlarrCategoryIds)
512
512
  } catch {
513
513
  // Skip - may already exist
514
514
  }