@muhammedaksam/easiarr 1.4.0 → 1.4.1
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/dist/index.js +29837 -0
- package/package.json +15 -15
- package/src/VersionInfo.ts +0 -12
- package/src/api/arr-api.ts +0 -521
- package/src/api/auto-setup-types.ts +0 -64
- package/src/api/base-api.ts +0 -216
- package/src/api/bazarr-api.ts +0 -297
- package/src/api/cloudflare-api.ts +0 -493
- package/src/api/custom-format-api.ts +0 -224
- package/src/api/grafana-api.ts +0 -230
- package/src/api/heimdall-api.ts +0 -141
- package/src/api/homarr-api.ts +0 -209
- package/src/api/huntarr-api.ts +0 -457
- package/src/api/index.ts +0 -9
- package/src/api/jellyfin-api.ts +0 -389
- package/src/api/jellyseerr-api.ts +0 -559
- package/src/api/maintainerr-api.ts +0 -461
- package/src/api/naming-config.ts +0 -101
- package/src/api/overseerr-api.ts +0 -392
- package/src/api/plex-api.ts +0 -308
- package/src/api/portainer-api.ts +0 -374
- package/src/api/profilarr-api.ts +0 -391
- package/src/api/prowlarr-api.ts +0 -488
- package/src/api/qbittorrent-api.ts +0 -300
- package/src/api/quality-profile-api.ts +0 -272
- package/src/api/tautulli-api.ts +0 -244
- package/src/api/uptime-kuma-api.ts +0 -343
- package/src/apps/categories.ts +0 -14
- package/src/apps/index.ts +0 -2
- package/src/apps/registry.ts +0 -1275
- package/src/compose/caddy-config.ts +0 -130
- package/src/compose/generator.ts +0 -287
- package/src/compose/index.ts +0 -7
- package/src/compose/templates.ts +0 -78
- package/src/compose/traefik-config.ts +0 -187
- package/src/config/bookmarks-generator.ts +0 -160
- package/src/config/defaults.ts +0 -37
- package/src/config/homepage-config.ts +0 -381
- package/src/config/index.ts +0 -6
- package/src/config/manager.ts +0 -150
- package/src/config/recyclarr-config.ts +0 -181
- package/src/config/schema.ts +0 -310
- package/src/config/slskd-config.ts +0 -86
- package/src/config/soularr-config.ts +0 -105
- package/src/config/trash-quality-definitions.ts +0 -213
- package/src/data/lidarr-custom-formats.ts +0 -127
- package/src/data/trash-profiles.ts +0 -333
- package/src/docker/client.ts +0 -312
- package/src/docker/index.ts +0 -18
- package/src/index.ts +0 -77
- package/src/setup/actions/arr-common.ts +0 -307
- package/src/setup/actions/dashboards.ts +0 -79
- package/src/setup/actions/download-clients.ts +0 -54
- package/src/setup/actions/jellyseerr.ts +0 -331
- package/src/setup/actions/media-servers.ts +0 -68
- package/src/setup/actions/monitoring.ts +0 -120
- package/src/setup/actions/prowlarr.ts +0 -137
- package/src/setup/actions/utilities.ts +0 -490
- package/src/setup/index.ts +0 -78
- package/src/setup/types.ts +0 -144
- package/src/structure/manager.ts +0 -129
- package/src/ui/App.ts +0 -111
- package/src/ui/components/ApplicationSelector.ts +0 -275
- package/src/ui/components/CredentialsForm.ts +0 -180
- package/src/ui/components/DownloadClientForm.ts +0 -114
- package/src/ui/components/FileEditor.ts +0 -91
- package/src/ui/components/FooterHint.ts +0 -119
- package/src/ui/components/PageLayout.ts +0 -160
- package/src/ui/components/UpdateNotification.ts +0 -102
- package/src/ui/index.ts +0 -7
- package/src/ui/screens/AdvancedSettings.ts +0 -188
- package/src/ui/screens/ApiKeyViewer.ts +0 -511
- package/src/ui/screens/AppConfigurator.ts +0 -543
- package/src/ui/screens/AppManager.ts +0 -284
- package/src/ui/screens/ContainerControl.ts +0 -581
- package/src/ui/screens/LogsViewer.ts +0 -478
- package/src/ui/screens/MainMenu.ts +0 -299
- package/src/ui/screens/MonitorDashboard.ts +0 -881
- package/src/ui/screens/SecretsEditor.ts +0 -216
- package/src/ui/screens/SettingsScreen.ts +0 -633
- package/src/ui/screens/index.ts +0 -22
- package/src/ui/screens/setup/BaseAppSetupScreen.ts +0 -375
- package/src/ui/screens/setup/CloudflaredSetup.ts +0 -460
- package/src/ui/screens/setup/FullAutoSetup.ts +0 -766
- package/src/ui/screens/setup/HomepageSetup.ts +0 -165
- package/src/ui/screens/setup/JellyfinSetup.ts +0 -328
- package/src/ui/screens/setup/JellyseerrSetup.ts +0 -242
- package/src/ui/screens/setup/ProwlarrSetup.ts +0 -500
- package/src/ui/screens/setup/QBittorrentSetup.ts +0 -221
- package/src/ui/screens/setup/QuickSetup.ts +0 -1169
- package/src/ui/screens/setup/RecyclarrSetup.ts +0 -270
- package/src/ui/screens/setup/TRaSHProfileSetup.ts +0 -381
- package/src/utils/arch.ts +0 -78
- package/src/utils/browser.ts +0 -27
- package/src/utils/categories.ts +0 -61
- package/src/utils/debug.ts +0 -84
- package/src/utils/env.ts +0 -138
- package/src/utils/index.ts +0 -8
- package/src/utils/logs.ts +0 -119
- package/src/utils/migrations/1765626338_rename_env_variables.ts +0 -102
- package/src/utils/migrations/1765707135_rename_easiarr_status.ts +0 -91
- package/src/utils/migrations/1765732722_remove_cloudflare_dns_api_token.ts +0 -45
- package/src/utils/migrations/1769515650_migrate_to_xdg.ts +0 -114
- package/src/utils/migrations.ts +0 -165
- package/src/utils/password.ts +0 -42
- package/src/utils/paths.ts +0 -33
- package/src/utils/unraid.ts +0 -103
- package/src/utils/update-checker.ts +0 -210
- package/src/utils/url-utils.ts +0 -38
|
@@ -1,500 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Prowlarr Setup Screen
|
|
3
|
-
* Configures Prowlarr integration with *arr apps, FlareSolverr, and proxies
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { CliRenderer, KeyEvent, TextNodeRenderable, TextRenderable } from "@opentui/core"
|
|
7
|
-
|
|
8
|
-
import type { MenuItem, SetupResult } from "~/ui/screens/setup/BaseAppSetupScreen"
|
|
9
|
-
import {
|
|
10
|
-
ArrAppType,
|
|
11
|
-
PROWLARR_CATEGORIES,
|
|
12
|
-
ProwlarrClient,
|
|
13
|
-
ProwlarrIndexerSchema,
|
|
14
|
-
} from "~/api/prowlarr-api"
|
|
15
|
-
import { getApp } from "~/apps/registry"
|
|
16
|
-
import { EasiarrConfig } from "~/config/schema"
|
|
17
|
-
import { BaseAppSetupScreen } from "~/ui/screens/setup/BaseAppSetupScreen"
|
|
18
|
-
import { readEnvSync } from "~/utils/env"
|
|
19
|
-
|
|
20
|
-
const ARR_APP_TYPES: Record<string, ArrAppType> = {
|
|
21
|
-
radarr: "Radarr",
|
|
22
|
-
sonarr: "Sonarr",
|
|
23
|
-
lidarr: "Lidarr",
|
|
24
|
-
readarr: "Readarr",
|
|
25
|
-
whisparr: "Whisparr",
|
|
26
|
-
mylar3: "Mylar",
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export class ProwlarrSetup extends BaseAppSetupScreen {
|
|
30
|
-
private prowlarrClient: ProwlarrClient | null = null
|
|
31
|
-
private availableIndexers: ProwlarrIndexerSchema[] = []
|
|
32
|
-
private selectedIndexers: Set<number> = new Set()
|
|
33
|
-
private listScrollOffset = 0
|
|
34
|
-
private isSelectingIndexers = false
|
|
35
|
-
private healthChecked = false
|
|
36
|
-
private healthMessage = ""
|
|
37
|
-
|
|
38
|
-
constructor(cliRenderer: CliRenderer, config: EasiarrConfig, onBack: () => void) {
|
|
39
|
-
super(cliRenderer, config, onBack)
|
|
40
|
-
this.initProwlarrClient()
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
getTitle(): string {
|
|
44
|
-
return "Prowlarr Setup"
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
getStepInfo(): string {
|
|
48
|
-
return "Configure indexer sync and proxies"
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
getMenuItems(): MenuItem[] {
|
|
52
|
-
return [
|
|
53
|
-
{
|
|
54
|
-
name: "🔗 Sync *arr Apps",
|
|
55
|
-
description: "Connect Radarr/Sonarr/etc to Prowlarr",
|
|
56
|
-
action: () => this.syncArrApps(),
|
|
57
|
-
},
|
|
58
|
-
{
|
|
59
|
-
name: "🛡️ Setup FlareSolverr",
|
|
60
|
-
description: "Add Cloudflare bypass proxy",
|
|
61
|
-
action: () => this.setupFlareSolverr(),
|
|
62
|
-
},
|
|
63
|
-
{
|
|
64
|
-
name: "🔍 Add Public Indexers",
|
|
65
|
-
description: "Search and add public trackers",
|
|
66
|
-
action: () => this.searchIndexers(),
|
|
67
|
-
},
|
|
68
|
-
{
|
|
69
|
-
name: "📊 Create Sync Profiles",
|
|
70
|
-
description: "Limited API indexer profiles",
|
|
71
|
-
action: () => this.createSyncProfiles(),
|
|
72
|
-
},
|
|
73
|
-
{
|
|
74
|
-
name: "↩️ Back",
|
|
75
|
-
description: "Return to main menu",
|
|
76
|
-
action: () => this.cleanup(),
|
|
77
|
-
},
|
|
78
|
-
]
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// ============================================
|
|
82
|
-
// CUSTOM KEY HANDLING
|
|
83
|
-
// ============================================
|
|
84
|
-
|
|
85
|
-
protected handleCustomKeys(key: KeyEvent): boolean {
|
|
86
|
-
if (this.isSelectingIndexers && this.results.length === 0) {
|
|
87
|
-
if (key.name === "up") {
|
|
88
|
-
this.menuIndex = Math.max(0, this.menuIndex - 1)
|
|
89
|
-
if (this.menuIndex < this.listScrollOffset) {
|
|
90
|
-
this.listScrollOffset = this.menuIndex
|
|
91
|
-
}
|
|
92
|
-
this.refreshContent()
|
|
93
|
-
} else if (key.name === "down") {
|
|
94
|
-
this.menuIndex = Math.min(this.availableIndexers.length - 1, this.menuIndex + 1)
|
|
95
|
-
const visibleItems = 15
|
|
96
|
-
if (this.menuIndex >= this.listScrollOffset + visibleItems) {
|
|
97
|
-
this.listScrollOffset = this.menuIndex - visibleItems + 1
|
|
98
|
-
}
|
|
99
|
-
this.refreshContent()
|
|
100
|
-
} else if (key.name === "space") {
|
|
101
|
-
if (this.selectedIndexers.has(this.menuIndex)) {
|
|
102
|
-
this.selectedIndexers.delete(this.menuIndex)
|
|
103
|
-
} else {
|
|
104
|
-
this.selectedIndexers.add(this.menuIndex)
|
|
105
|
-
}
|
|
106
|
-
this.refreshContent()
|
|
107
|
-
} else if (key.name === "return") {
|
|
108
|
-
this.addSelectedIndexers()
|
|
109
|
-
} else if (key.name === "escape") {
|
|
110
|
-
this.isSelectingIndexers = false
|
|
111
|
-
this.currentStep = "menu"
|
|
112
|
-
this.menuIndex = 0
|
|
113
|
-
this.refreshContent()
|
|
114
|
-
}
|
|
115
|
-
return true
|
|
116
|
-
}
|
|
117
|
-
return false
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// ============================================
|
|
121
|
-
// CUSTOM RENDERING
|
|
122
|
-
// ============================================
|
|
123
|
-
|
|
124
|
-
protected renderCustomContent(): boolean {
|
|
125
|
-
if (this.currentStep === "menu") {
|
|
126
|
-
// Show warning if no API key
|
|
127
|
-
if (!this.prowlarrClient) {
|
|
128
|
-
this.contentBox.add(
|
|
129
|
-
new TextRenderable(this.cliRenderer, {
|
|
130
|
-
content: "⚠️ Prowlarr API key not found!\nRun 'Extract API Keys' first.\n\n",
|
|
131
|
-
fg: "#ff5555",
|
|
132
|
-
})
|
|
133
|
-
)
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// Render normal menu
|
|
137
|
-
this.contentBox.add(
|
|
138
|
-
new TextRenderable(this.cliRenderer, {
|
|
139
|
-
content: "Select an action:\n\n",
|
|
140
|
-
fg: "#aaaaaa",
|
|
141
|
-
})
|
|
142
|
-
)
|
|
143
|
-
|
|
144
|
-
this.getMenuItems().forEach((item, idx) => {
|
|
145
|
-
const pointer = idx === this.menuIndex ? "→ " : " "
|
|
146
|
-
const fg = idx === this.menuIndex ? "#50fa7b" : "#8be9fd"
|
|
147
|
-
|
|
148
|
-
this.contentBox.add(
|
|
149
|
-
new TextRenderable(this.cliRenderer, {
|
|
150
|
-
content: `${pointer}${item.name}\n`,
|
|
151
|
-
fg,
|
|
152
|
-
})
|
|
153
|
-
)
|
|
154
|
-
this.contentBox.add(
|
|
155
|
-
new TextRenderable(this.cliRenderer, {
|
|
156
|
-
content: ` ${item.description}\n\n`,
|
|
157
|
-
fg: "#6272a4",
|
|
158
|
-
})
|
|
159
|
-
)
|
|
160
|
-
})
|
|
161
|
-
|
|
162
|
-
return true
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
if (this.isSelectingIndexers && this.results.length === 0) {
|
|
166
|
-
this.renderIndexerSelection()
|
|
167
|
-
return true
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
return false // Default results rendering
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
private renderIndexerSelection(): void {
|
|
174
|
-
const visibleHeight = 15
|
|
175
|
-
const endIndex = Math.min(this.availableIndexers.length, this.listScrollOffset + visibleHeight)
|
|
176
|
-
const items = this.availableIndexers.slice(this.listScrollOffset, endIndex)
|
|
177
|
-
|
|
178
|
-
// Calculate active category IDs from selected apps
|
|
179
|
-
const activeCategoryIds = new Set<number>()
|
|
180
|
-
this.config.apps.forEach((app) => {
|
|
181
|
-
const def = getApp(app.id)
|
|
182
|
-
def?.prowlarrCategoryIds?.forEach((id) => activeCategoryIds.add(id))
|
|
183
|
-
})
|
|
184
|
-
|
|
185
|
-
this.contentBox.add(
|
|
186
|
-
new TextRenderable(this.cliRenderer, {
|
|
187
|
-
content: `Select Indexers (Space to toggle, Enter to add):\n\n`,
|
|
188
|
-
fg: "#f1fa8c",
|
|
189
|
-
})
|
|
190
|
-
)
|
|
191
|
-
|
|
192
|
-
items.forEach((idx, i) => {
|
|
193
|
-
const realIndex = this.listScrollOffset + i
|
|
194
|
-
const isSelected = this.selectedIndexers.has(realIndex)
|
|
195
|
-
const isCurrent = realIndex === this.menuIndex
|
|
196
|
-
|
|
197
|
-
const check = isSelected ? "[x]" : "[ ]"
|
|
198
|
-
const pointer = isCurrent ? "→" : " "
|
|
199
|
-
|
|
200
|
-
const cats = idx.capabilities?.categories || []
|
|
201
|
-
const groups = new Map<string, boolean>()
|
|
202
|
-
|
|
203
|
-
const checkRel = (min: number, max: number) =>
|
|
204
|
-
[...activeCategoryIds].some((id) => id >= min && id < max)
|
|
205
|
-
|
|
206
|
-
const categoryColors: Record<string, { active: string; inactive: string }> = {
|
|
207
|
-
Movies: { active: "#00ffff", inactive: "#008b8b" },
|
|
208
|
-
TV: { active: "#ff00ff", inactive: "#8b008b" },
|
|
209
|
-
Audio: { active: "#00ff00", inactive: "#006400" },
|
|
210
|
-
Books: { active: "#50fa7b", inactive: "#00008b" },
|
|
211
|
-
XXX: { active: "#ff5555", inactive: "#8b0000" },
|
|
212
|
-
PC: { active: "#f8f8f2", inactive: "#6272a4" },
|
|
213
|
-
Console: { active: "#f1fa8c", inactive: "#8b8000" },
|
|
214
|
-
Other: { active: "#aaaaaa", inactive: "#555555" },
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
cats.forEach((c) => {
|
|
218
|
-
const id = c.id
|
|
219
|
-
let name = ""
|
|
220
|
-
let isRel = false
|
|
221
|
-
|
|
222
|
-
const parentCat = PROWLARR_CATEGORIES.find((pc) => {
|
|
223
|
-
if (pc.id === id) return true
|
|
224
|
-
if (pc.subCategories?.some((sub) => sub.id === id)) return true
|
|
225
|
-
const rangeStart = Math.floor(pc.id / 1000) * 1000
|
|
226
|
-
return id >= rangeStart && id < rangeStart + 1000
|
|
227
|
-
})
|
|
228
|
-
|
|
229
|
-
if (parentCat) {
|
|
230
|
-
name = parentCat.name
|
|
231
|
-
const rangeStart = Math.floor(parentCat.id / 1000) * 1000
|
|
232
|
-
isRel = checkRel(rangeStart, rangeStart + 1000)
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
if (name) {
|
|
236
|
-
groups.set(name, groups.get(name) || isRel)
|
|
237
|
-
}
|
|
238
|
-
})
|
|
239
|
-
|
|
240
|
-
const line = new TextRenderable(this.cliRenderer, { content: "" })
|
|
241
|
-
line.add(`${pointer} ${check} ${idx.name} `)
|
|
242
|
-
|
|
243
|
-
const addBadge = (name: string) => {
|
|
244
|
-
if (groups.has(name)) {
|
|
245
|
-
const isRel = groups.get(name)
|
|
246
|
-
const colors = categoryColors[name] || categoryColors["Other"]
|
|
247
|
-
const color = isRel ? colors.active : colors.inactive
|
|
248
|
-
|
|
249
|
-
const badge = new TextNodeRenderable({ fg: color })
|
|
250
|
-
badge.add(`[${name}] `)
|
|
251
|
-
if (isRel) {
|
|
252
|
-
badge.attributes = 1
|
|
253
|
-
}
|
|
254
|
-
line.add(badge)
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
PROWLARR_CATEGORIES.forEach((cat) => {
|
|
259
|
-
addBadge(cat.name)
|
|
260
|
-
})
|
|
261
|
-
|
|
262
|
-
line.add("\n")
|
|
263
|
-
line.fg = isCurrent ? "#ffffff" : isSelected ? "#50fa7b" : "#aaaaaa"
|
|
264
|
-
|
|
265
|
-
this.contentBox.add(line)
|
|
266
|
-
})
|
|
267
|
-
|
|
268
|
-
const remaining = this.availableIndexers.length - endIndex
|
|
269
|
-
if (remaining > 0) {
|
|
270
|
-
this.contentBox.add(
|
|
271
|
-
new TextRenderable(this.cliRenderer, {
|
|
272
|
-
content: `... and ${remaining} more`,
|
|
273
|
-
fg: "#6272a4",
|
|
274
|
-
})
|
|
275
|
-
)
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
// ============================================
|
|
280
|
-
// PRIVATE METHODS
|
|
281
|
-
// ============================================
|
|
282
|
-
|
|
283
|
-
private initProwlarrClient(): void {
|
|
284
|
-
const env = readEnvSync()
|
|
285
|
-
const apiKey = env["API_KEY_PROWLARR"]
|
|
286
|
-
if (apiKey) {
|
|
287
|
-
const prowlarrConfig = this.config.apps.find((a) => a.id === "prowlarr")
|
|
288
|
-
const port = prowlarrConfig?.port || 9696
|
|
289
|
-
this.prowlarrClient = new ProwlarrClient("localhost", port, apiKey)
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
private updateResult(name: string, status: SetupResult["status"], message?: string): void {
|
|
294
|
-
const result = this.results.find((r) => r.name === name)
|
|
295
|
-
if (result) {
|
|
296
|
-
result.status = status
|
|
297
|
-
if (message) result.message = message
|
|
298
|
-
}
|
|
299
|
-
this.refreshContent()
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
// ============================================
|
|
303
|
-
// MENU ACTIONS
|
|
304
|
-
// ============================================
|
|
305
|
-
|
|
306
|
-
private async syncArrApps(): Promise<void> {
|
|
307
|
-
if (!this.prowlarrClient) {
|
|
308
|
-
this.results = [{ name: "Prowlarr", status: "error", message: "API key not found" }]
|
|
309
|
-
this.currentStep = "done"
|
|
310
|
-
this.refreshContent()
|
|
311
|
-
return
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
this.currentStep = "running"
|
|
315
|
-
this.results = []
|
|
316
|
-
|
|
317
|
-
const arrApps = this.config.apps.filter(
|
|
318
|
-
(a) => a.enabled && Object.keys(ARR_APP_TYPES).includes(a.id)
|
|
319
|
-
)
|
|
320
|
-
const env = readEnvSync()
|
|
321
|
-
const prowlarrConfig = this.config.apps.find((a) => a.id === "prowlarr")
|
|
322
|
-
const prowlarrPort = prowlarrConfig?.port || 9696
|
|
323
|
-
|
|
324
|
-
for (const app of arrApps) {
|
|
325
|
-
const appType = ARR_APP_TYPES[app.id]
|
|
326
|
-
if (!appType) continue
|
|
327
|
-
|
|
328
|
-
this.results.push({ name: app.id, status: "configuring" })
|
|
329
|
-
this.refreshContent()
|
|
330
|
-
|
|
331
|
-
try {
|
|
332
|
-
const apiKey = env[`API_KEY_${app.id.toUpperCase()}`]
|
|
333
|
-
if (!apiKey) {
|
|
334
|
-
this.updateResult(app.id, "skipped", "No API key")
|
|
335
|
-
continue
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
const appDef = getApp(app.id)
|
|
339
|
-
const port = app.port || appDef?.defaultPort || 7878
|
|
340
|
-
|
|
341
|
-
await this.prowlarrClient.addArrApp(
|
|
342
|
-
appType,
|
|
343
|
-
app.id,
|
|
344
|
-
port,
|
|
345
|
-
apiKey,
|
|
346
|
-
"prowlarr",
|
|
347
|
-
prowlarrPort,
|
|
348
|
-
appDef?.prowlarrCategoryIds
|
|
349
|
-
)
|
|
350
|
-
|
|
351
|
-
this.updateResult(app.id, "success")
|
|
352
|
-
} catch (error) {
|
|
353
|
-
this.updateResult(app.id, "error", error instanceof Error ? error.message : "Unknown error")
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
// Trigger sync
|
|
358
|
-
try {
|
|
359
|
-
await this.prowlarrClient.syncApplications()
|
|
360
|
-
} catch {
|
|
361
|
-
// Sync might fail if no apps, that's ok
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
this.currentStep = "done"
|
|
365
|
-
this.refreshContent()
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
private async setupFlareSolverr(): Promise<void> {
|
|
369
|
-
if (!this.prowlarrClient) {
|
|
370
|
-
this.results = [{ name: "Prowlarr", status: "error", message: "API key not found" }]
|
|
371
|
-
this.currentStep = "done"
|
|
372
|
-
this.refreshContent()
|
|
373
|
-
return
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
this.currentStep = "running"
|
|
377
|
-
this.results = [{ name: "FlareSolverr", status: "configuring" }]
|
|
378
|
-
this.refreshContent()
|
|
379
|
-
|
|
380
|
-
try {
|
|
381
|
-
const fsConfig = this.config.apps.find((a) => a.id === "flaresolverr")
|
|
382
|
-
if (!fsConfig?.enabled) {
|
|
383
|
-
this.updateResult("FlareSolverr", "skipped", "FlareSolverr not enabled in config")
|
|
384
|
-
} else {
|
|
385
|
-
const fsPort = fsConfig.port || 8191
|
|
386
|
-
await this.prowlarrClient.configureFlareSolverr(`http://flaresolverr:${fsPort}`)
|
|
387
|
-
this.updateResult("FlareSolverr", "success", "Proxy added with 'flaresolverr' tag")
|
|
388
|
-
}
|
|
389
|
-
} catch (error) {
|
|
390
|
-
this.updateResult(
|
|
391
|
-
"FlareSolverr",
|
|
392
|
-
"error",
|
|
393
|
-
error instanceof Error ? error.message : "Unknown error"
|
|
394
|
-
)
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
this.currentStep = "done"
|
|
398
|
-
this.refreshContent()
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
private async createSyncProfiles(): Promise<void> {
|
|
402
|
-
if (!this.prowlarrClient) {
|
|
403
|
-
this.results = [{ name: "Prowlarr", status: "error", message: "API key not found" }]
|
|
404
|
-
this.currentStep = "done"
|
|
405
|
-
this.refreshContent()
|
|
406
|
-
return
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
this.currentStep = "running"
|
|
410
|
-
this.results = [
|
|
411
|
-
{ name: "Automatic Search", status: "configuring" },
|
|
412
|
-
{ name: "Interactive Search", status: "configuring" },
|
|
413
|
-
]
|
|
414
|
-
this.refreshContent()
|
|
415
|
-
|
|
416
|
-
try {
|
|
417
|
-
await this.prowlarrClient.createLimitedAPISyncProfiles()
|
|
418
|
-
this.updateResult("Automatic Search", "success", "RSS disabled, auto+interactive enabled")
|
|
419
|
-
this.updateResult("Interactive Search", "success", "RSS+auto disabled, interactive only")
|
|
420
|
-
} catch (error) {
|
|
421
|
-
const msg = error instanceof Error ? error.message : "Unknown error"
|
|
422
|
-
this.results.forEach((r) => {
|
|
423
|
-
r.status = "error"
|
|
424
|
-
r.message = msg
|
|
425
|
-
})
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
this.currentStep = "done"
|
|
429
|
-
this.refreshContent()
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
private async searchIndexers(): Promise<void> {
|
|
433
|
-
if (!this.prowlarrClient) return
|
|
434
|
-
|
|
435
|
-
this.isSelectingIndexers = true
|
|
436
|
-
this.results = [{ name: "Fetching indexers...", status: "configuring" }]
|
|
437
|
-
this.refreshContent()
|
|
438
|
-
|
|
439
|
-
try {
|
|
440
|
-
const schemas = await this.prowlarrClient.getIndexerSchemas()
|
|
441
|
-
this.availableIndexers = schemas
|
|
442
|
-
.filter((i) => i.privacy === "public" && i.enable)
|
|
443
|
-
.sort((a, b) => {
|
|
444
|
-
const infoA = (a.capabilities?.categories || []).length
|
|
445
|
-
const infoB = (b.capabilities?.categories || []).length
|
|
446
|
-
return infoB - infoA
|
|
447
|
-
})
|
|
448
|
-
|
|
449
|
-
this.selectedIndexers.clear()
|
|
450
|
-
this.menuIndex = 0
|
|
451
|
-
this.listScrollOffset = 0
|
|
452
|
-
this.results = []
|
|
453
|
-
} catch (error) {
|
|
454
|
-
this.results = [{ name: "Error", status: "error", message: String(error) }]
|
|
455
|
-
this.isSelectingIndexers = false
|
|
456
|
-
this.currentStep = "done"
|
|
457
|
-
}
|
|
458
|
-
this.refreshContent()
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
private async addSelectedIndexers(): Promise<void> {
|
|
462
|
-
const toAdd = Array.from(this.selectedIndexers).map((idx) => this.availableIndexers[idx])
|
|
463
|
-
if (toAdd.length === 0) return
|
|
464
|
-
|
|
465
|
-
this.results = toAdd.map((i) => ({ name: i.name, status: "pending" as const }))
|
|
466
|
-
this.refreshContent()
|
|
467
|
-
|
|
468
|
-
for (const indexer of toAdd) {
|
|
469
|
-
this.updateResult(indexer.name, "configuring")
|
|
470
|
-
|
|
471
|
-
try {
|
|
472
|
-
if (!this.prowlarrClient) throw new Error("No client")
|
|
473
|
-
|
|
474
|
-
const tags = await this.prowlarrClient.getTags()
|
|
475
|
-
const fsTag = tags.find((t) => t.label.toLowerCase() === "flaresolverr")
|
|
476
|
-
|
|
477
|
-
if (fsTag) {
|
|
478
|
-
indexer.tags = indexer.tags || []
|
|
479
|
-
if (!indexer.tags.includes(fsTag.id)) {
|
|
480
|
-
indexer.tags.push(fsTag.id)
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
await this.prowlarrClient.createIndexer(indexer)
|
|
485
|
-
const extra = fsTag ? " + FlareSolverr" : ""
|
|
486
|
-
this.updateResult(
|
|
487
|
-
indexer.name,
|
|
488
|
-
"success",
|
|
489
|
-
`Added with ${indexer.capabilities?.categories?.length || 0} categories${extra}`
|
|
490
|
-
)
|
|
491
|
-
} catch (e) {
|
|
492
|
-
this.updateResult(indexer.name, "error", String(e))
|
|
493
|
-
}
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
this.isSelectingIndexers = false
|
|
497
|
-
this.currentStep = "done"
|
|
498
|
-
this.refreshContent()
|
|
499
|
-
}
|
|
500
|
-
}
|