@muhammedaksam/easiarr 0.3.2 → 0.3.4

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.3.2",
3
+ "version": "0.3.4",
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",
@@ -17,6 +17,7 @@ export interface RootFolder {
17
17
  // Options for adding root folder (some apps like Lidarr need extra fields)
18
18
  export interface AddRootFolderOptions {
19
19
  path: string
20
+ name?: string // Required for Lidarr
20
21
  defaultMetadataProfileId?: number
21
22
  defaultQualityProfileId?: number
22
23
  }
@@ -188,13 +188,13 @@ export class ProwlarrClient {
188
188
  await this.request(`/indexerproxy/${id}`, { method: "DELETE" })
189
189
  }
190
190
 
191
- // Sync Profile management
191
+ // Sync Profile management (aka App Sync Profile)
192
192
  async getSyncProfiles(): Promise<SyncProfile[]> {
193
- return this.request<SyncProfile[]>("/syncprofile")
193
+ return this.request<SyncProfile[]>("/appsyncprofile")
194
194
  }
195
195
 
196
196
  async createSyncProfile(profile: Omit<SyncProfile, "id">): Promise<SyncProfile> {
197
- return this.request<SyncProfile>("/syncprofile", {
197
+ return this.request<SyncProfile>("/appsyncprofile", {
198
198
  method: "POST",
199
199
  body: JSON.stringify(profile),
200
200
  })
@@ -260,11 +260,21 @@ export class ProwlarrClient {
260
260
  appApiKey: string,
261
261
  syncLevel: "disabled" | "addOnly" | "fullSync" = "fullSync"
262
262
  ): 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
+
263
273
  const fields: { name: string; value: unknown }[] = [
264
274
  { name: "prowlarrUrl", value: prowlarrUrl },
265
275
  { name: "baseUrl", value: appUrl },
266
276
  { name: "apiKey", value: appApiKey },
267
- { name: "syncCategories", value: [] },
277
+ { name: "syncCategories", value: syncCategoriesMap[appType] || [] },
268
278
  ]
269
279
 
270
280
  return this.request<Application>("/applications", {
@@ -286,7 +296,10 @@ export class ProwlarrClient {
286
296
 
287
297
  // Sync all apps - triggers Prowlarr to push indexers to connected apps
288
298
  async syncApplications(): Promise<void> {
289
- await this.request("/applications/action/sync", { method: "POST" })
299
+ await this.request("/applications/action/sync", {
300
+ method: "POST",
301
+ body: JSON.stringify({}), // API requires non-empty body
302
+ })
290
303
  }
291
304
 
292
305
  // Add *arr app with auto-detection
@@ -4,6 +4,8 @@
4
4
  * API docs: https://github.com/qbittorrent/qBittorrent/wiki/WebUI-API-(qBittorrent-4.1)
5
5
  */
6
6
 
7
+ import { debugLog } from "../utils/debug"
8
+
7
9
  export interface QBittorrentPreferences {
8
10
  save_path?: string
9
11
  temp_path_enabled?: boolean
@@ -172,7 +174,10 @@ export class QBittorrentClient {
172
174
  * @param categories - Array of {name, savePath} for each enabled *arr app
173
175
  */
174
176
  async configureTRaSHCompliant(categories: QBittorrentCategory[] = []): Promise<void> {
177
+ debugLog("qBittorrent", "Configuring TRaSH-compliant settings")
178
+
175
179
  // 1. Set global preferences
180
+ debugLog("qBittorrent", "Setting save_path to /data/torrents")
176
181
  await this.setPreferences({
177
182
  save_path: "/data/torrents",
178
183
  temp_path_enabled: false,
@@ -183,6 +188,7 @@ export class QBittorrentClient {
183
188
 
184
189
  // 2. Create categories for each enabled media type
185
190
  for (const cat of categories) {
191
+ debugLog("qBittorrent", `Creating category: ${cat.name} -> ${cat.savePath}`)
186
192
  try {
187
193
  await this.createCategory(cat.name, cat.savePath)
188
194
  } catch {
@@ -194,5 +200,6 @@ export class QBittorrentClient {
194
200
  }
195
201
  }
196
202
  }
203
+ debugLog("qBittorrent", "TRaSH configuration complete")
197
204
  }
198
205
  }
@@ -252,7 +252,8 @@ export class AppConfigurator extends BoxRenderable {
252
252
 
253
253
  const appDef = getApp(appId as AppId)
254
254
  const port = appConfig.port || appDef?.defaultPort || 9696
255
- const client = new ArrApiClient("localhost", port, apiKey)
255
+ // Prowlarr uses v1 API, not v3
256
+ const client = new ArrApiClient("localhost", port, apiKey, "v1")
256
257
 
257
258
  try {
258
259
  await client.updateHostConfig(this.globalUsername, this.globalPassword, this.overrideExisting)
@@ -313,12 +314,13 @@ export class AppConfigurator extends BoxRenderable {
313
314
  throw new Error("Already configured")
314
315
  }
315
316
 
316
- // Add root folder - Lidarr requires profile IDs
317
+ // Add root folder - Lidarr requires profile IDs and name
317
318
  if (appId === "lidarr") {
318
319
  const metadataProfiles = await client.getMetadataProfiles()
319
320
  const qualityProfiles = await client.getQualityProfiles()
320
321
  await client.addRootFolder({
321
322
  path: appDef.rootFolder.path,
323
+ name: "Music", // Required by Lidarr
322
324
  defaultMetadataProfileId: metadataProfiles[0]?.id || 1,
323
325
  defaultQualityProfileId: qualityProfiles[0]?.id || 1,
324
326
  })
@@ -560,6 +562,12 @@ export class AppConfigurator extends BoxRenderable {
560
562
  const client = new ArrApiClient("localhost", port, apiKey, appDef.rootFolder.apiVersion)
561
563
 
562
564
  try {
565
+ // Check if download client already exists
566
+ const existingClients = await client.getDownloadClients()
567
+ const clientName = type === "qbittorrent" ? "qBittorrent" : "SABnzbd"
568
+ const alreadyExists = existingClients.some((c) => c.name === clientName)
569
+ if (alreadyExists) continue
570
+
563
571
  if (type === "qbittorrent") {
564
572
  const config = createQBittorrentConfig(this.qbHost, this.qbPort, this.qbUser, this.qbPass, appConfig.id)
565
573
  await client.addDownloadClient(config)