@muhammedaksam/easiarr 1.1.1 → 1.1.3
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 +1 -1
- package/src/api/arr-api.ts +53 -0
- package/src/api/bazarr-api.ts +83 -0
- package/src/api/jellyseerr-api.ts +82 -8
- package/src/api/naming-config.ts +67 -0
- package/src/api/overseerr-api.ts +24 -0
- package/src/api/qbittorrent-api.ts +58 -0
- package/src/api/quality-profile-api.ts +69 -5
- package/src/apps/registry.ts +71 -1
- package/src/config/homepage-config.ts +28 -8
- package/src/config/schema.ts +2 -0
- package/src/config/slskd-config.ts +85 -0
- package/src/config/soularr-config.ts +105 -0
- package/src/config/trash-quality-definitions.ts +213 -0
- package/src/data/lidarr-custom-formats.ts +127 -0
- package/src/data/trash-profiles.ts +77 -3
- package/src/ui/screens/FullAutoSetup.ts +296 -21
- package/src/ui/screens/TRaSHProfileSetup.ts +39 -20
- package/src/utils/url-utils.ts +38 -0
|
@@ -7,9 +7,11 @@ import { BoxRenderable, CliRenderer, TextRenderable, KeyEvent } from "@opentui/c
|
|
|
7
7
|
import { createPageLayout } from "../components/PageLayout"
|
|
8
8
|
import { EasiarrConfig, AppId } from "../../config/schema"
|
|
9
9
|
import { getApp } from "../../apps/registry"
|
|
10
|
+
import { ArrApiClient } from "../../api/arr-api"
|
|
10
11
|
import { QualityProfileClient } from "../../api/quality-profile-api"
|
|
11
12
|
import { CustomFormatClient, getCFNamesForCategories } from "../../api/custom-format-api"
|
|
12
13
|
import { getPresetsForApp, TRaSHProfilePreset } from "../../data/trash-profiles"
|
|
14
|
+
import { LIDARR_CUSTOM_FORMATS } from "../../data/lidarr-custom-formats"
|
|
13
15
|
import { readEnvSync } from "../../utils/env"
|
|
14
16
|
import { debugLog } from "../../utils/debug"
|
|
15
17
|
|
|
@@ -18,6 +20,7 @@ interface SetupResult {
|
|
|
18
20
|
appName: string
|
|
19
21
|
profile: string
|
|
20
22
|
cfCount: number
|
|
23
|
+
namingConfigured: boolean
|
|
21
24
|
status: "pending" | "configuring" | "success" | "error"
|
|
22
25
|
message?: string
|
|
23
26
|
}
|
|
@@ -61,12 +64,14 @@ export class TRaSHProfileSetup extends BoxRenderable {
|
|
|
61
64
|
this.pageContainer = pageContainer
|
|
62
65
|
|
|
63
66
|
// Get enabled *arr apps that support quality profiles
|
|
64
|
-
this.availableApps = config.apps
|
|
67
|
+
this.availableApps = config.apps
|
|
68
|
+
.filter((a) => a.enabled && ["radarr", "sonarr", "lidarr"].includes(a.id))
|
|
69
|
+
.map((a) => a.id)
|
|
65
70
|
|
|
66
71
|
// Initialize selections
|
|
67
72
|
this.availableApps.forEach((id) => {
|
|
68
73
|
this.selectedApps.set(id, true)
|
|
69
|
-
const presets = getPresetsForApp(id as "radarr" | "sonarr")
|
|
74
|
+
const presets = getPresetsForApp(id as "radarr" | "sonarr" | "lidarr")
|
|
70
75
|
if (presets.length > 0) {
|
|
71
76
|
this.selectedProfiles.set(id, presets[0].id)
|
|
72
77
|
}
|
|
@@ -134,7 +139,7 @@ export class TRaSHProfileSetup extends BoxRenderable {
|
|
|
134
139
|
private handleSelectProfilesKeys(key: KeyEvent): void {
|
|
135
140
|
const selectedAppIds = this.availableApps.filter((id) => this.selectedApps.get(id))
|
|
136
141
|
const app = selectedAppIds[this.currentIndex]
|
|
137
|
-
const presets = getPresetsForApp(app as "radarr" | "sonarr")
|
|
142
|
+
const presets = getPresetsForApp(app as "radarr" | "sonarr" | "lidarr")
|
|
138
143
|
|
|
139
144
|
if (key.name === "up") {
|
|
140
145
|
const current = this.selectedProfiles.get(app)
|
|
@@ -176,7 +181,7 @@ export class TRaSHProfileSetup extends BoxRenderable {
|
|
|
176
181
|
for (const appId of selectedAppIds) {
|
|
177
182
|
const appDef = getApp(appId)
|
|
178
183
|
const profileId = this.selectedProfiles.get(appId)
|
|
179
|
-
const preset = getPresetsForApp(appId as "radarr" | "sonarr").find((p) => p.id === profileId)
|
|
184
|
+
const preset = getPresetsForApp(appId as "radarr" | "sonarr" | "lidarr").find((p) => p.id === profileId)
|
|
180
185
|
|
|
181
186
|
if (!appDef || !preset) continue
|
|
182
187
|
|
|
@@ -185,6 +190,7 @@ export class TRaSHProfileSetup extends BoxRenderable {
|
|
|
185
190
|
appName: appDef.name,
|
|
186
191
|
profile: preset.name,
|
|
187
192
|
cfCount: 0,
|
|
193
|
+
namingConfigured: false,
|
|
188
194
|
status: "configuring",
|
|
189
195
|
})
|
|
190
196
|
this.refreshContent()
|
|
@@ -195,6 +201,7 @@ export class TRaSHProfileSetup extends BoxRenderable {
|
|
|
195
201
|
if (result) {
|
|
196
202
|
result.status = "success"
|
|
197
203
|
result.cfCount = Object.keys(preset.cfScores).length
|
|
204
|
+
result.namingConfigured = true
|
|
198
205
|
}
|
|
199
206
|
} catch (error) {
|
|
200
207
|
const result = this.results.find((r) => r.appId === appId)
|
|
@@ -219,24 +226,36 @@ export class TRaSHProfileSetup extends BoxRenderable {
|
|
|
219
226
|
if (!apiKey) throw new Error("API key not found - run Extract API Keys first")
|
|
220
227
|
|
|
221
228
|
const port = this.config.apps.find((a) => a.id === appId)?.port || appDef.defaultPort
|
|
222
|
-
const
|
|
223
|
-
const
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
if (
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
229
|
+
const apiVersion = appDef.rootFolder?.apiVersion || "v3"
|
|
230
|
+
const qpClient = new QualityProfileClient("localhost", port, apiKey, apiVersion)
|
|
231
|
+
const cfClient = new CustomFormatClient("localhost", port, apiKey, apiVersion)
|
|
232
|
+
|
|
233
|
+
// Import Custom Formats - Lidarr uses Davo's guide formats, Radarr/Sonarr use TRaSH
|
|
234
|
+
if (appId === "lidarr") {
|
|
235
|
+
// Import Lidarr custom formats from Davo's Community Guide
|
|
236
|
+
await cfClient.importCustomFormats(LIDARR_CUSTOM_FORMATS)
|
|
237
|
+
} else {
|
|
238
|
+
// Import TRaSH custom formats for Radarr/Sonarr
|
|
239
|
+
const cfCategories = ["unwanted", "misc"]
|
|
240
|
+
if (preset.id.includes("uhd") || preset.id.includes("2160")) {
|
|
241
|
+
cfCategories.push("hdr")
|
|
242
|
+
}
|
|
243
|
+
if (preset.id.includes("remux")) {
|
|
244
|
+
cfCategories.push("audio")
|
|
245
|
+
}
|
|
246
|
+
const cfNames = getCFNamesForCategories(appId as "radarr" | "sonarr", cfCategories)
|
|
247
|
+
const { cfs } = await CustomFormatClient.fetchTRaSHCustomFormats(appId as "radarr" | "sonarr", cfNames)
|
|
248
|
+
await cfClient.importCustomFormats(cfs)
|
|
232
249
|
}
|
|
233
250
|
|
|
234
|
-
const cfNames = getCFNamesForCategories(appId as "radarr" | "sonarr", cfCategories)
|
|
235
|
-
const { cfs } = await CustomFormatClient.fetchTRaSHCustomFormats(appId as "radarr" | "sonarr", cfNames)
|
|
236
|
-
await cfClient.importCustomFormats(cfs)
|
|
237
|
-
|
|
238
251
|
// Create quality profile
|
|
239
252
|
await qpClient.createTRaSHProfile(preset.name, preset.cutoffQuality, preset.allowedQualities, preset.cfScores)
|
|
253
|
+
|
|
254
|
+
// Configure naming scheme (skip for Lidarr - different API structure)
|
|
255
|
+
if (appId !== "lidarr") {
|
|
256
|
+
const arrClient = new ArrApiClient("localhost", port, apiKey, apiVersion)
|
|
257
|
+
await arrClient.configureTRaSHNaming(appId as "radarr" | "sonarr")
|
|
258
|
+
}
|
|
240
259
|
}
|
|
241
260
|
|
|
242
261
|
private refreshContent(): void {
|
|
@@ -293,7 +312,7 @@ export class TRaSHProfileSetup extends BoxRenderable {
|
|
|
293
312
|
|
|
294
313
|
selectedAppIds.forEach((appId, appIdx) => {
|
|
295
314
|
const app = getApp(appId)
|
|
296
|
-
const presets = getPresetsForApp(appId as "radarr" | "sonarr")
|
|
315
|
+
const presets = getPresetsForApp(appId as "radarr" | "sonarr" | "lidarr")
|
|
297
316
|
const selectedPresetId = this.selectedProfiles.get(appId)
|
|
298
317
|
const isCurrent = appIdx === this.currentIndex
|
|
299
318
|
|
|
@@ -349,7 +368,7 @@ export class TRaSHProfileSetup extends BoxRenderable {
|
|
|
349
368
|
|
|
350
369
|
let content = `${status} ${result.appName}: ${result.profile}`
|
|
351
370
|
if (result.status === "success") {
|
|
352
|
-
content += ` (${result.cfCount} CF scores)`
|
|
371
|
+
content += ` (${result.cfCount} CF scores, naming configured)`
|
|
353
372
|
}
|
|
354
373
|
if (result.message) {
|
|
355
374
|
content += ` - ${result.message}`
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* URL Utilities
|
|
3
|
+
* Functions for generating app URLs based on Traefik configuration
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { EasiarrConfig } from "../config/schema"
|
|
7
|
+
import { getLocalIp } from "./env"
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Get the local URL for an app (http://LOCAL_IP:PORT)
|
|
11
|
+
*/
|
|
12
|
+
export function getLocalAppUrl(port: number): string {
|
|
13
|
+
const localIp = getLocalIp()
|
|
14
|
+
return `http://${localIp}:${port}`
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Get the external URL for an app (https://APP.DOMAIN)
|
|
19
|
+
* Returns null if Traefik is not enabled
|
|
20
|
+
*/
|
|
21
|
+
export function getExternalAppUrl(appId: string, config: EasiarrConfig): string | null {
|
|
22
|
+
if (!config.traefik?.enabled || !config.traefik.domain) {
|
|
23
|
+
return null
|
|
24
|
+
}
|
|
25
|
+
return `https://${appId}.${config.traefik.domain}`
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Get the appropriate applicationUrl based on Traefik status
|
|
30
|
+
* Returns external URL if Traefik enabled, otherwise local URL
|
|
31
|
+
*/
|
|
32
|
+
export function getApplicationUrl(appId: string, port: number, config: EasiarrConfig): string {
|
|
33
|
+
const externalUrl = getExternalAppUrl(appId, config)
|
|
34
|
+
if (externalUrl) {
|
|
35
|
+
return externalUrl
|
|
36
|
+
}
|
|
37
|
+
return getLocalAppUrl(port)
|
|
38
|
+
}
|