@muhammedaksam/easiarr 1.1.0 → 1.1.2

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.0",
3
+ "version": "1.1.2",
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",
@@ -46,6 +46,7 @@ export interface RemotePathMapping {
46
46
 
47
47
  import type { AppId } from "../config/schema"
48
48
  import { getCategoryForApp, getCategoryFieldName } from "../utils/categories"
49
+ import { TRASH_NAMING_CONFIG, type NamingConfig } from "./naming-config"
49
50
 
50
51
  // qBittorrent download client config
51
52
  export function createQBittorrentConfig(
@@ -239,11 +240,63 @@ export class ArrApiClient {
239
240
  })
240
241
  }
241
242
 
243
+ /**
244
+ * Set application URL for external access (e.g., from Jellyseerr/dashboard links)
245
+ * URL will be used when generating external links in the app
246
+ */
247
+ async setApplicationUrl(applicationUrl: string): Promise<HostConfig> {
248
+ const currentConfig = await this.getHostConfig()
249
+
250
+ const updatedConfig: HostConfig = {
251
+ ...currentConfig,
252
+ applicationUrl,
253
+ }
254
+
255
+ debugLog("ArrAPI", `Setting applicationUrl to: ${applicationUrl}`)
256
+
257
+ return this.request<HostConfig>("/config/host", {
258
+ method: "PUT",
259
+ body: JSON.stringify(updatedConfig),
260
+ })
261
+ }
262
+
242
263
  // Remote Path Mapping methods - for Docker path translation
264
+
243
265
  async getRemotePathMappings(): Promise<RemotePathMapping[]> {
244
266
  return this.request<RemotePathMapping[]>("/remotepathmapping")
245
267
  }
246
268
 
269
+ // Naming Configuration methods
270
+ async getNamingConfig<T extends NamingConfig>(): Promise<T> {
271
+ return this.request<T>("/config/naming")
272
+ }
273
+
274
+ async updateNamingConfig<T extends NamingConfig>(config: T): Promise<T> {
275
+ return this.request<T>("/config/naming", {
276
+ method: "PUT",
277
+ body: JSON.stringify(config),
278
+ })
279
+ }
280
+
281
+ async configureTRaSHNaming(appType: "radarr" | "sonarr"): Promise<void> {
282
+ try {
283
+ // 1. Get current configuration to preserve ID and other fields
284
+ const currentConfig = await this.getNamingConfig<NamingConfig & { id?: number }>()
285
+
286
+ // 2. Merge with TRaSH defaults
287
+ const trashConfig = TRASH_NAMING_CONFIG[appType]
288
+ const newConfig = {
289
+ ...currentConfig,
290
+ ...trashConfig,
291
+ }
292
+
293
+ // 3. Update configuration
294
+ await this.updateNamingConfig(newConfig)
295
+ } catch (e) {
296
+ throw new Error(`Failed to configure naming: ${e}`)
297
+ }
298
+ }
299
+
247
300
  async addRemotePathMapping(host: string, remotePath: string, localPath: string): Promise<RemotePathMapping> {
248
301
  return this.request<RemotePathMapping>("/remotepathmapping", {
249
302
  method: "POST",
@@ -6,6 +6,19 @@
6
6
  import { debugLog } from "../utils/debug"
7
7
  import type { IAutoSetupClient, AutoSetupOptions, AutoSetupResult } from "./auto-setup-types"
8
8
 
9
+ /**
10
+ * Bazarr Language Profile Structure
11
+ */
12
+ export interface BazarrLanguageProfile {
13
+ name: string
14
+ cutoff: string
15
+ languages: {
16
+ code: string
17
+ forced: boolean
18
+ hi: boolean
19
+ }[]
20
+ }
21
+
9
22
  /**
10
23
  * Bazarr System Settings (partial - auth related fields)
11
24
  */
@@ -232,6 +245,76 @@ export class BazarrApiClient implements IAutoSetupClient {
232
245
  }
233
246
  }
234
247
 
248
+ /**
249
+ * Configure General Settings (TRaSH Recommended)
250
+ */
251
+ async configureGeneralSettings(): Promise<boolean> {
252
+ try {
253
+ debugLog("Bazarr", "Configuring general settings")
254
+ await this.postForm("/system/settings", {
255
+ "settings-subtitles-use_embedded_subtitles": "true",
256
+ "settings-subtitles-autosearch": "true",
257
+ "settings-subtitles-path_mapping": "", // Empty = Alongside Media File
258
+ })
259
+ return true
260
+ } catch (e) {
261
+ debugLog("Bazarr", `Failed to configure general settings: ${e}`)
262
+ return false
263
+ }
264
+ }
265
+
266
+ /**
267
+ * Get all language profiles
268
+ */
269
+ async getLanguageProfiles(): Promise<BazarrLanguageProfile[]> {
270
+ return this.get<BazarrLanguageProfile[]>("/system/languages/profiles")
271
+ }
272
+
273
+ /**
274
+ * Configure Default Language Profile
275
+ * Creates an english profile if it doesn't exist
276
+ */
277
+ async configureDefaultLanguageProfile(name = "English", language = "en"): Promise<boolean> {
278
+ try {
279
+ debugLog("Bazarr", `Configuring language profile: ${name}`)
280
+
281
+ // Get existing profiles to check and to preserve them
282
+ const profiles = (await this.getLanguageProfiles()) || []
283
+ const existing = profiles.find((p) => p.name === name)
284
+
285
+ if (existing) {
286
+ debugLog("Bazarr", `Language profile '${name}' already exists`)
287
+ return true
288
+ }
289
+
290
+ const newProfile: BazarrLanguageProfile = {
291
+ name: name,
292
+ cutoff: language,
293
+ languages: [
294
+ {
295
+ code: language,
296
+ forced: false,
297
+ hi: false,
298
+ },
299
+ ],
300
+ }
301
+
302
+ profiles.push(newProfile)
303
+
304
+ // Update settings with the new list (serialized as JSON)
305
+ // Note: Bazarr expects 'languages_profiles' as a JSON string in the form data
306
+ await this.postForm("/system/settings", {
307
+ languages_profiles: JSON.stringify(profiles),
308
+ })
309
+
310
+ debugLog("Bazarr", `Created language profile: ${name}`)
311
+ return true
312
+ } catch (e) {
313
+ debugLog("Bazarr", `Failed to configure language profile: ${e}`)
314
+ return false
315
+ }
316
+ }
317
+
235
318
  /**
236
319
  * Run the auto-setup process for Bazarr
237
320
  */