@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,165 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Homepage Setup Screen
|
|
3
|
-
* Configure Homepage dashboard with enabled apps
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { CliRenderer, KeyEvent, TextRenderable } from "@opentui/core"
|
|
7
|
-
|
|
8
|
-
import type { MenuItem } from "~/ui/screens/setup/BaseAppSetupScreen"
|
|
9
|
-
import { getApp } from "~/apps/registry"
|
|
10
|
-
import { generateServicesYaml, saveHomepageConfig } from "~/config/homepage-config"
|
|
11
|
-
import { EasiarrConfig } from "~/config/schema"
|
|
12
|
-
import { BaseAppSetupScreen } from "~/ui/screens/setup/BaseAppSetupScreen"
|
|
13
|
-
|
|
14
|
-
export class HomepageSetup extends BaseAppSetupScreen {
|
|
15
|
-
private previewContent = ""
|
|
16
|
-
private isPreviewMode = false
|
|
17
|
-
|
|
18
|
-
constructor(cliRenderer: CliRenderer, config: EasiarrConfig, onBack: () => void) {
|
|
19
|
-
super(cliRenderer, config, onBack)
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
getTitle(): string {
|
|
23
|
-
return "Homepage Setup"
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
getStepInfo(): string {
|
|
27
|
-
return "Configure Dashboard"
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
getMenuItems(): MenuItem[] {
|
|
31
|
-
return [
|
|
32
|
-
{
|
|
33
|
-
name: "📊 Generate Services",
|
|
34
|
-
description: "Create services.yaml with all enabled apps",
|
|
35
|
-
action: () => this.generateServices(),
|
|
36
|
-
},
|
|
37
|
-
{
|
|
38
|
-
name: "👁️ Preview Config",
|
|
39
|
-
description: "Preview generated services.yaml",
|
|
40
|
-
action: () => this.previewServices(),
|
|
41
|
-
},
|
|
42
|
-
{
|
|
43
|
-
name: "📋 Show Enabled Apps",
|
|
44
|
-
description: "List apps that will be added to Homepage",
|
|
45
|
-
action: () => this.showEnabledApps(),
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
name: "↩️ Back",
|
|
49
|
-
description: "Return to main menu",
|
|
50
|
-
action: () => this.cleanup(),
|
|
51
|
-
},
|
|
52
|
-
]
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
protected getMenuDescription(): string {
|
|
56
|
-
return "Configure Homepage dashboard with your enabled apps:"
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// ============================================
|
|
60
|
-
// CUSTOM KEY HANDLING - Handle preview step
|
|
61
|
-
// ============================================
|
|
62
|
-
|
|
63
|
-
protected handleCustomKeys(key: KeyEvent): boolean {
|
|
64
|
-
if (this.isPreviewMode) {
|
|
65
|
-
if (key.name === "return" || key.name === "escape") {
|
|
66
|
-
this.isPreviewMode = false
|
|
67
|
-
this.refreshContent()
|
|
68
|
-
}
|
|
69
|
-
return true // Handled
|
|
70
|
-
}
|
|
71
|
-
return false
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// ============================================
|
|
75
|
-
// CUSTOM RENDERING - Preview step
|
|
76
|
-
// ============================================
|
|
77
|
-
|
|
78
|
-
protected renderCustomContent(): boolean {
|
|
79
|
-
if (this.isPreviewMode) {
|
|
80
|
-
this.renderPreview()
|
|
81
|
-
return true
|
|
82
|
-
}
|
|
83
|
-
return false // Use default for menu/results
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
private renderPreview(): void {
|
|
87
|
-
this.contentBox.add(
|
|
88
|
-
new TextRenderable(this.cliRenderer, {
|
|
89
|
-
content: "Preview: services.yaml\n",
|
|
90
|
-
fg: "#50fa7b",
|
|
91
|
-
})
|
|
92
|
-
)
|
|
93
|
-
this.contentBox.add(
|
|
94
|
-
new TextRenderable(this.cliRenderer, {
|
|
95
|
-
content: "─".repeat(40) + "\n",
|
|
96
|
-
fg: "#555555",
|
|
97
|
-
})
|
|
98
|
-
)
|
|
99
|
-
|
|
100
|
-
// Show preview (truncated)
|
|
101
|
-
const lines = this.previewContent.split("\n").slice(0, 30)
|
|
102
|
-
for (const line of lines) {
|
|
103
|
-
this.contentBox.add(
|
|
104
|
-
new TextRenderable(this.cliRenderer, {
|
|
105
|
-
content: line + "\n",
|
|
106
|
-
fg: line.startsWith("#") ? "#6272a4" : line.endsWith(":") ? "#8be9fd" : "#f8f8f2",
|
|
107
|
-
})
|
|
108
|
-
)
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
if (this.previewContent.split("\n").length > 30) {
|
|
112
|
-
this.contentBox.add(
|
|
113
|
-
new TextRenderable(this.cliRenderer, {
|
|
114
|
-
content: "\n... (truncated)\n",
|
|
115
|
-
fg: "#6272a4",
|
|
116
|
-
})
|
|
117
|
-
)
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
this.contentBox.add(
|
|
121
|
-
new TextRenderable(this.cliRenderer, {
|
|
122
|
-
content: "\nPress Enter or Esc to go back",
|
|
123
|
-
fg: "#6272a4",
|
|
124
|
-
})
|
|
125
|
-
)
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// ============================================
|
|
129
|
-
// MENU ACTIONS
|
|
130
|
-
// ============================================
|
|
131
|
-
|
|
132
|
-
private async generateServices(): Promise<void> {
|
|
133
|
-
await this.runAction("services.yaml", async () => {
|
|
134
|
-
const paths = await saveHomepageConfig(this.config)
|
|
135
|
-
return { success: true, message: `Saved to ${paths.services}` }
|
|
136
|
-
})
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
private async previewServices(): Promise<void> {
|
|
140
|
-
this.previewContent = await generateServicesYaml(this.config)
|
|
141
|
-
this.isPreviewMode = true
|
|
142
|
-
this.refreshContent()
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
private showEnabledApps(): void {
|
|
146
|
-
const apps = this.config.apps.filter((a) => a.enabled && a.id !== "homepage")
|
|
147
|
-
|
|
148
|
-
this.results = apps.map((app) => {
|
|
149
|
-
const def = getApp(app.id)
|
|
150
|
-
const hasWidget = def?.homepage?.widget ? "📊" : "📌"
|
|
151
|
-
return {
|
|
152
|
-
name: `${hasWidget} ${def?.name || app.id}`,
|
|
153
|
-
status: "success" as const,
|
|
154
|
-
message: def?.description,
|
|
155
|
-
}
|
|
156
|
-
})
|
|
157
|
-
|
|
158
|
-
if (this.results.length === 0) {
|
|
159
|
-
this.results = [{ name: "No apps enabled", status: "skipped", message: "Enable apps first" }]
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
this.currentStep = "done"
|
|
163
|
-
this.refreshContent()
|
|
164
|
-
}
|
|
165
|
-
}
|
|
@@ -1,328 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Jellyfin Setup Screen
|
|
3
|
-
* Automates the Jellyfin setup wizard via API
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { CliRenderer, TextRenderable } from "@opentui/core"
|
|
7
|
-
|
|
8
|
-
import type { MenuItem, SetupResult } from "~/ui/screens/setup/BaseAppSetupScreen"
|
|
9
|
-
import { JellyfinClient } from "~/api/jellyfin-api"
|
|
10
|
-
import { EasiarrConfig } from "~/config/schema"
|
|
11
|
-
import { BaseAppSetupScreen } from "~/ui/screens/setup/BaseAppSetupScreen"
|
|
12
|
-
import { readEnvSync, writeEnvSync } from "~/utils/env"
|
|
13
|
-
|
|
14
|
-
export class JellyfinSetup extends BaseAppSetupScreen {
|
|
15
|
-
private jellyfinClient: JellyfinClient | null = null
|
|
16
|
-
private healthChecked = false
|
|
17
|
-
private healthMessage = ""
|
|
18
|
-
private healthFg = "#aaaaaa"
|
|
19
|
-
|
|
20
|
-
constructor(cliRenderer: CliRenderer, config: EasiarrConfig, onBack: () => void) {
|
|
21
|
-
super(cliRenderer, config, onBack)
|
|
22
|
-
this.initJellyfinClient()
|
|
23
|
-
this.checkHealth()
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
getTitle(): string {
|
|
27
|
-
return "Jellyfin Setup"
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
getStepInfo(): string {
|
|
31
|
-
return "Configure Jellyfin via API"
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
getMenuItems(): MenuItem[] {
|
|
35
|
-
return [
|
|
36
|
-
{
|
|
37
|
-
name: "🚀 Run Setup Wizard",
|
|
38
|
-
description: "Create admin user and complete initial setup",
|
|
39
|
-
action: () => this.runSetupWizard(),
|
|
40
|
-
},
|
|
41
|
-
{
|
|
42
|
-
name: "📚 Add Default Libraries",
|
|
43
|
-
description: "Add Movies, TV Shows, Music libraries",
|
|
44
|
-
action: () => this.addDefaultLibraries(),
|
|
45
|
-
},
|
|
46
|
-
{
|
|
47
|
-
name: "🔑 Generate API Key",
|
|
48
|
-
description: "Create API key for Homepage widget",
|
|
49
|
-
action: () => this.generateApiKey(),
|
|
50
|
-
},
|
|
51
|
-
{
|
|
52
|
-
name: "↩️ Back",
|
|
53
|
-
description: "Return to main menu",
|
|
54
|
-
action: () => this.cleanup(),
|
|
55
|
-
},
|
|
56
|
-
]
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// ============================================
|
|
60
|
-
// CUSTOM RENDERING - Show health status
|
|
61
|
-
// ============================================
|
|
62
|
-
|
|
63
|
-
protected renderCustomContent(): boolean {
|
|
64
|
-
if (this.currentStep === "menu") {
|
|
65
|
-
// Show health status before menu
|
|
66
|
-
if (this.healthChecked && this.healthMessage) {
|
|
67
|
-
this.contentBox.add(
|
|
68
|
-
new TextRenderable(this.cliRenderer, {
|
|
69
|
-
content: this.healthMessage + "\n\n",
|
|
70
|
-
fg: this.healthFg,
|
|
71
|
-
})
|
|
72
|
-
)
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// Render menu normally
|
|
76
|
-
this.contentBox.add(
|
|
77
|
-
new TextRenderable(this.cliRenderer, {
|
|
78
|
-
content: "Select an action:\n\n",
|
|
79
|
-
fg: "#aaaaaa",
|
|
80
|
-
})
|
|
81
|
-
)
|
|
82
|
-
|
|
83
|
-
this.getMenuItems().forEach((item, idx) => {
|
|
84
|
-
const pointer = idx === this.menuIndex ? "→ " : " "
|
|
85
|
-
const fg = idx === this.menuIndex ? "#50fa7b" : "#8be9fd"
|
|
86
|
-
|
|
87
|
-
this.contentBox.add(
|
|
88
|
-
new TextRenderable(this.cliRenderer, {
|
|
89
|
-
content: `${pointer}${item.name}\n`,
|
|
90
|
-
fg,
|
|
91
|
-
})
|
|
92
|
-
)
|
|
93
|
-
this.contentBox.add(
|
|
94
|
-
new TextRenderable(this.cliRenderer, {
|
|
95
|
-
content: ` ${item.description}\n\n`,
|
|
96
|
-
fg: "#6272a4",
|
|
97
|
-
})
|
|
98
|
-
)
|
|
99
|
-
})
|
|
100
|
-
|
|
101
|
-
return true
|
|
102
|
-
}
|
|
103
|
-
return false // Use default for results
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// ============================================
|
|
107
|
-
// PRIVATE METHODS
|
|
108
|
-
// ============================================
|
|
109
|
-
|
|
110
|
-
private initJellyfinClient(): void {
|
|
111
|
-
const jellyfinConfig = this.config.apps.find((a) => a.id === "jellyfin")
|
|
112
|
-
if (jellyfinConfig?.enabled) {
|
|
113
|
-
const port = jellyfinConfig.port || 8096
|
|
114
|
-
this.jellyfinClient = new JellyfinClient("localhost", port)
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
private async checkHealth(): Promise<void> {
|
|
119
|
-
if (!this.jellyfinClient) {
|
|
120
|
-
this.healthMessage = "⚠️ Jellyfin not enabled in config!"
|
|
121
|
-
this.healthFg = "#ff5555"
|
|
122
|
-
this.healthChecked = true
|
|
123
|
-
this.refreshContent()
|
|
124
|
-
return
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
try {
|
|
128
|
-
const isHealthy = await this.jellyfinClient.isHealthy()
|
|
129
|
-
const isComplete = isHealthy ? await this.jellyfinClient.isStartupComplete() : false
|
|
130
|
-
|
|
131
|
-
if (!isHealthy) {
|
|
132
|
-
this.healthMessage = "⚠️ Jellyfin is not reachable. Make sure the container is running."
|
|
133
|
-
this.healthFg = "#ffb86c"
|
|
134
|
-
} else if (!isComplete) {
|
|
135
|
-
this.healthMessage = "✨ Jellyfin needs initial setup. Run 'Setup Wizard' to configure."
|
|
136
|
-
this.healthFg = "#50fa7b"
|
|
137
|
-
} else {
|
|
138
|
-
this.healthMessage = "✓ Jellyfin is running and configured."
|
|
139
|
-
this.healthFg = "#50fa7b"
|
|
140
|
-
}
|
|
141
|
-
} catch {
|
|
142
|
-
// Ignore health check errors
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
this.healthChecked = true
|
|
146
|
-
this.refreshContent()
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
private updateResult(index: number, status: SetupResult["status"], message?: string): void {
|
|
150
|
-
if (this.results[index]) {
|
|
151
|
-
this.results[index].status = status
|
|
152
|
-
if (message) this.results[index].message = message
|
|
153
|
-
}
|
|
154
|
-
this.refreshContent()
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// ============================================
|
|
158
|
-
// MENU ACTIONS
|
|
159
|
-
// ============================================
|
|
160
|
-
|
|
161
|
-
private async runSetupWizard(): Promise<void> {
|
|
162
|
-
if (!this.jellyfinClient) {
|
|
163
|
-
this.results = [{ name: "Jellyfin", status: "error", message: "Not enabled in config" }]
|
|
164
|
-
this.currentStep = "done"
|
|
165
|
-
this.refreshContent()
|
|
166
|
-
return
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
this.currentStep = "running"
|
|
170
|
-
this.results = [
|
|
171
|
-
{ name: "Check status", status: "configuring" },
|
|
172
|
-
{ name: "Set metadata language", status: "pending" },
|
|
173
|
-
{ name: "Create admin user", status: "pending" },
|
|
174
|
-
{ name: "Configure remote access", status: "pending" },
|
|
175
|
-
{ name: "Complete wizard", status: "pending" },
|
|
176
|
-
]
|
|
177
|
-
this.refreshContent()
|
|
178
|
-
|
|
179
|
-
try {
|
|
180
|
-
// Step 1: Check if already set up
|
|
181
|
-
const isComplete = await this.jellyfinClient.isStartupComplete()
|
|
182
|
-
if (isComplete) {
|
|
183
|
-
this.updateResult(0, "skipped", "Already configured")
|
|
184
|
-
this.results
|
|
185
|
-
.slice(1)
|
|
186
|
-
.forEach((_, i) => this.updateResult(i + 1, "skipped", "Wizard already completed"))
|
|
187
|
-
this.currentStep = "done"
|
|
188
|
-
this.refreshContent()
|
|
189
|
-
return
|
|
190
|
-
}
|
|
191
|
-
this.updateResult(0, "success", "Wizard needed")
|
|
192
|
-
|
|
193
|
-
// Step 2: Set metadata language
|
|
194
|
-
this.updateResult(1, "configuring")
|
|
195
|
-
await this.jellyfinClient.setStartupConfiguration({
|
|
196
|
-
UICulture: "en-US",
|
|
197
|
-
MetadataCountryCode: "US",
|
|
198
|
-
PreferredMetadataLanguage: "en",
|
|
199
|
-
})
|
|
200
|
-
this.updateResult(1, "success")
|
|
201
|
-
|
|
202
|
-
// Step 3: Create admin user
|
|
203
|
-
this.updateResult(2, "configuring")
|
|
204
|
-
const env = readEnvSync()
|
|
205
|
-
const username = env["USERNAME_GLOBAL"] || "admin"
|
|
206
|
-
const password = env["PASSWORD_GLOBAL"] || "Ch4ng3m3!1234securityReasons"
|
|
207
|
-
await this.jellyfinClient.createAdminUser(username, password)
|
|
208
|
-
this.updateResult(2, "success", `User: ${username}`)
|
|
209
|
-
|
|
210
|
-
// Step 4: Configure remote access
|
|
211
|
-
this.updateResult(3, "configuring")
|
|
212
|
-
await this.jellyfinClient.setRemoteAccess(true, false)
|
|
213
|
-
this.updateResult(3, "success")
|
|
214
|
-
|
|
215
|
-
// Step 5: Complete wizard
|
|
216
|
-
this.updateResult(4, "configuring")
|
|
217
|
-
await this.jellyfinClient.completeStartup()
|
|
218
|
-
this.updateResult(4, "success")
|
|
219
|
-
} catch (error) {
|
|
220
|
-
const current = this.results.findIndex((r) => r.status === "configuring")
|
|
221
|
-
if (current >= 0) {
|
|
222
|
-
this.updateResult(current, "error", error instanceof Error ? error.message : String(error))
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
this.currentStep = "done"
|
|
227
|
-
this.refreshContent()
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
private async addDefaultLibraries(): Promise<void> {
|
|
231
|
-
if (!this.jellyfinClient) {
|
|
232
|
-
this.results = [{ name: "Jellyfin", status: "error", message: "Not enabled in config" }]
|
|
233
|
-
this.currentStep = "done"
|
|
234
|
-
this.refreshContent()
|
|
235
|
-
return
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
this.currentStep = "running"
|
|
239
|
-
this.results = [
|
|
240
|
-
{ name: "Authenticate", status: "configuring" },
|
|
241
|
-
{ name: "Movies", status: "pending" },
|
|
242
|
-
{ name: "TV Shows", status: "pending" },
|
|
243
|
-
{ name: "Music", status: "pending" },
|
|
244
|
-
]
|
|
245
|
-
this.refreshContent()
|
|
246
|
-
|
|
247
|
-
try {
|
|
248
|
-
// Authenticate first
|
|
249
|
-
const env = readEnvSync()
|
|
250
|
-
const username = env["USERNAME_GLOBAL"] || "admin"
|
|
251
|
-
const password = env["PASSWORD_GLOBAL"] || "Ch4ng3m3!1234securityReasons"
|
|
252
|
-
await this.jellyfinClient.authenticate(username, password)
|
|
253
|
-
this.updateResult(0, "success")
|
|
254
|
-
|
|
255
|
-
// Add libraries
|
|
256
|
-
const libraries = [
|
|
257
|
-
{ name: "Movies", collectionType: "movies" as const, paths: ["/data/media/movies"] },
|
|
258
|
-
{ name: "TV Shows", collectionType: "tvshows" as const, paths: ["/data/media/tv"] },
|
|
259
|
-
{ name: "Music", collectionType: "music" as const, paths: ["/data/media/music"] },
|
|
260
|
-
]
|
|
261
|
-
|
|
262
|
-
for (let i = 0; i < libraries.length; i++) {
|
|
263
|
-
const lib = libraries[i]
|
|
264
|
-
this.updateResult(i + 1, "configuring")
|
|
265
|
-
|
|
266
|
-
try {
|
|
267
|
-
await this.jellyfinClient.addVirtualFolder(lib)
|
|
268
|
-
this.updateResult(i + 1, "success", lib.paths[0])
|
|
269
|
-
} catch (error) {
|
|
270
|
-
this.updateResult(i + 1, "error", error instanceof Error ? error.message : String(error))
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
} catch (error) {
|
|
274
|
-
this.updateResult(0, "error", error instanceof Error ? error.message : String(error))
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
this.currentStep = "done"
|
|
278
|
-
this.refreshContent()
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
private async generateApiKey(): Promise<void> {
|
|
282
|
-
if (!this.jellyfinClient) {
|
|
283
|
-
this.results = [{ name: "Jellyfin", status: "error", message: "Not enabled in config" }]
|
|
284
|
-
this.currentStep = "done"
|
|
285
|
-
this.refreshContent()
|
|
286
|
-
return
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
this.currentStep = "running"
|
|
290
|
-
this.results = [
|
|
291
|
-
{ name: "Authenticate", status: "configuring" },
|
|
292
|
-
{ name: "Generate API Key", status: "pending" },
|
|
293
|
-
{ name: "Save to .env", status: "pending" },
|
|
294
|
-
]
|
|
295
|
-
this.refreshContent()
|
|
296
|
-
|
|
297
|
-
try {
|
|
298
|
-
// Authenticate first
|
|
299
|
-
const env = readEnvSync()
|
|
300
|
-
const username = env["USERNAME_GLOBAL"] || "admin"
|
|
301
|
-
const password = env["PASSWORD_GLOBAL"] || "Ch4ng3m3!1234securityReasons"
|
|
302
|
-
await this.jellyfinClient.authenticate(username, password)
|
|
303
|
-
this.updateResult(0, "success")
|
|
304
|
-
|
|
305
|
-
// Generate API key
|
|
306
|
-
this.updateResult(1, "configuring")
|
|
307
|
-
const apiKey = await this.jellyfinClient.createApiKey("easiarr")
|
|
308
|
-
if (!apiKey) {
|
|
309
|
-
throw new Error("Failed to create API key")
|
|
310
|
-
}
|
|
311
|
-
this.updateResult(1, "success", `Key: ${apiKey.substring(0, 8)}...`)
|
|
312
|
-
|
|
313
|
-
// Save to .env
|
|
314
|
-
this.updateResult(2, "configuring")
|
|
315
|
-
env["API_KEY_JELLYFIN"] = apiKey
|
|
316
|
-
writeEnvSync(env)
|
|
317
|
-
this.updateResult(2, "success", "Saved as API_KEY_JELLYFIN")
|
|
318
|
-
} catch (error) {
|
|
319
|
-
const current = this.results.findIndex((r) => r.status === "configuring")
|
|
320
|
-
if (current >= 0) {
|
|
321
|
-
this.updateResult(current, "error", error instanceof Error ? error.message : String(error))
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
this.currentStep = "done"
|
|
326
|
-
this.refreshContent()
|
|
327
|
-
}
|
|
328
|
-
}
|