@muhammedaksam/easiarr 1.3.5 → 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/README.md +8 -5
- package/dist/index.js +29837 -0
- package/package.json +18 -16
- package/src/VersionInfo.ts +0 -12
- package/src/api/arr-api.ts +0 -508
- package/src/api/auto-setup-types.ts +0 -64
- package/src/api/bazarr-api.ts +0 -356
- package/src/api/cloudflare-api.ts +0 -577
- package/src/api/custom-format-api.ts +0 -212
- package/src/api/grafana-api.ts +0 -314
- package/src/api/heimdall-api.ts +0 -209
- package/src/api/homarr-api.ts +0 -296
- package/src/api/huntarr-api.ts +0 -622
- package/src/api/index.ts +0 -1
- package/src/api/jellyfin-api.ts +0 -473
- package/src/api/jellyseerr-api.ts +0 -660
- package/src/api/maintainerr-api.ts +0 -731
- package/src/api/naming-config.ts +0 -93
- package/src/api/overseerr-api.ts +0 -513
- package/src/api/plex-api.ts +0 -358
- package/src/api/portainer-api.ts +0 -467
- package/src/api/profilarr-api.ts +0 -380
- package/src/api/prowlarr-api.ts +0 -859
- package/src/api/qbittorrent-api.ts +0 -339
- package/src/api/quality-profile-api.ts +0 -269
- package/src/api/tautulli-api.ts +0 -277
- package/src/api/uptime-kuma-api.ts +0 -342
- package/src/apps/categories.ts +0 -14
- package/src/apps/index.ts +0 -2
- package/src/apps/registry.ts +0 -1255
- package/src/compose/caddy-config.ts +0 -129
- package/src/compose/generator.ts +0 -262
- package/src/compose/index.ts +0 -3
- package/src/compose/templates.ts +0 -78
- package/src/compose/traefik-config.ts +0 -185
- package/src/config/bookmarks-generator.ts +0 -149
- package/src/config/defaults.ts +0 -37
- package/src/config/homepage-config.ts +0 -376
- package/src/config/index.ts +0 -3
- package/src/config/manager.ts +0 -149
- package/src/config/recyclarr-config.ts +0 -179
- package/src/config/schema.ts +0 -310
- package/src/config/slskd-config.ts +0 -85
- 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 -326
- package/src/docker/client.ts +0 -291
- package/src/docker/index.ts +0 -1
- package/src/index.ts +0 -77
- package/src/structure/manager.ts +0 -128
- package/src/ui/App.ts +0 -109
- package/src/ui/components/ApplicationSelector.ts +0 -272
- package/src/ui/components/FileEditor.ts +0 -91
- package/src/ui/components/FooterHint.ts +0 -119
- package/src/ui/components/PageLayout.ts +0 -152
- package/src/ui/components/UpdateNotification.ts +0 -101
- package/src/ui/index.ts +0 -1
- package/src/ui/screens/AdvancedSettings.ts +0 -181
- package/src/ui/screens/ApiKeyViewer.ts +0 -500
- package/src/ui/screens/AppConfigurator.ts +0 -731
- package/src/ui/screens/AppManager.ts +0 -276
- package/src/ui/screens/CloudflaredSetup.ts +0 -974
- package/src/ui/screens/ContainerControl.ts +0 -579
- package/src/ui/screens/FullAutoSetup.ts +0 -1719
- package/src/ui/screens/HomepageSetup.ts +0 -306
- package/src/ui/screens/JellyfinSetup.ts +0 -471
- package/src/ui/screens/JellyseerrSetup.ts +0 -612
- package/src/ui/screens/LogsViewer.ts +0 -468
- package/src/ui/screens/MainMenu.ts +0 -288
- package/src/ui/screens/MonitorDashboard.ts +0 -866
- package/src/ui/screens/ProwlarrSetup.ts +0 -623
- package/src/ui/screens/QBittorrentSetup.ts +0 -268
- package/src/ui/screens/QuickSetup.ts +0 -1161
- package/src/ui/screens/RecyclarrSetup.ts +0 -418
- package/src/ui/screens/SecretsEditor.ts +0 -214
- package/src/ui/screens/SettingsScreen.ts +0 -605
- package/src/ui/screens/TRaSHProfileSetup.ts +0 -395
- package/src/ui/screens/index.ts +0 -4
- package/src/util/arch.ts +0 -78
- package/src/utils/browser.ts +0 -26
- package/src/utils/categories.ts +0 -59
- package/src/utils/debug.ts +0 -91
- package/src/utils/env.ts +0 -137
- package/src/utils/logs.ts +0 -118
- package/src/utils/migrations/1765626338_rename_env_variables.ts +0 -101
- package/src/utils/migrations/1765707135_rename_easiarr_status.ts +0 -90
- package/src/utils/migrations/1765732722_remove_cloudflare_dns_api_token.ts +0 -44
- package/src/utils/migrations/1769515650_migrate_to_xdg.ts +0 -107
- package/src/utils/migrations.ts +0 -164
- package/src/utils/password.ts +0 -42
- package/src/utils/unraid.ts +0 -101
- package/src/utils/update-checker.ts +0 -210
- package/src/utils/url-utils.ts +0 -38
package/src/api/jellyfin-api.ts
DELETED
|
@@ -1,473 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Jellyfin API Client
|
|
3
|
-
* Handles setup wizard automation and media library management
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { debugLog } from "../utils/debug"
|
|
7
|
-
import type { IAutoSetupClient, AutoSetupOptions, AutoSetupResult } from "./auto-setup-types"
|
|
8
|
-
|
|
9
|
-
// ==========================================
|
|
10
|
-
// Startup Wizard Types
|
|
11
|
-
// ==========================================
|
|
12
|
-
|
|
13
|
-
export interface StartupConfiguration {
|
|
14
|
-
UICulture?: string
|
|
15
|
-
MetadataCountryCode?: string
|
|
16
|
-
PreferredMetadataLanguage?: string
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export interface StartupUser {
|
|
20
|
-
Name: string
|
|
21
|
-
Password: string
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export interface StartupRemoteAccess {
|
|
25
|
-
EnableRemoteAccess: boolean
|
|
26
|
-
EnableAutomaticPortMapping: boolean
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// ==========================================
|
|
30
|
-
// Library Types
|
|
31
|
-
// ==========================================
|
|
32
|
-
|
|
33
|
-
export interface VirtualFolderInfo {
|
|
34
|
-
Name: string
|
|
35
|
-
Locations: string[]
|
|
36
|
-
CollectionType: LibraryType
|
|
37
|
-
ItemId?: string
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export type LibraryType =
|
|
41
|
-
| "movies"
|
|
42
|
-
| "tvshows"
|
|
43
|
-
| "music"
|
|
44
|
-
| "books"
|
|
45
|
-
| "homevideos"
|
|
46
|
-
| "musicvideos"
|
|
47
|
-
| "photos"
|
|
48
|
-
| "playlists"
|
|
49
|
-
| "boxsets"
|
|
50
|
-
|
|
51
|
-
export interface AddVirtualFolderOptions {
|
|
52
|
-
name: string
|
|
53
|
-
collectionType: LibraryType
|
|
54
|
-
paths: string[]
|
|
55
|
-
refreshLibrary?: boolean
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// ==========================================
|
|
59
|
-
// System Types
|
|
60
|
-
// ==========================================
|
|
61
|
-
|
|
62
|
-
export interface SystemInfo {
|
|
63
|
-
ServerName: string
|
|
64
|
-
Version: string
|
|
65
|
-
Id: string
|
|
66
|
-
OperatingSystem: string
|
|
67
|
-
StartupWizardCompleted: boolean
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// ==========================================
|
|
71
|
-
// User Types
|
|
72
|
-
// ==========================================
|
|
73
|
-
|
|
74
|
-
export interface UserPolicy {
|
|
75
|
-
IsAdministrator: boolean
|
|
76
|
-
IsHidden: boolean
|
|
77
|
-
IsDisabled: boolean
|
|
78
|
-
EnableRemoteAccess: boolean
|
|
79
|
-
AuthenticationProviderId?: string
|
|
80
|
-
PasswordResetProviderId?: string
|
|
81
|
-
[key: string]: unknown // Allow other properties
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
export interface UserDto {
|
|
85
|
-
Id: string
|
|
86
|
-
Name?: string
|
|
87
|
-
ServerId?: string
|
|
88
|
-
HasPassword: boolean
|
|
89
|
-
LastLoginDate?: string
|
|
90
|
-
Policy?: UserPolicy
|
|
91
|
-
[key: string]: unknown // Allow other properties
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
export interface AuthResult {
|
|
95
|
-
AccessToken: string
|
|
96
|
-
ServerId: string
|
|
97
|
-
User: UserDto
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// ==========================================
|
|
101
|
-
// Jellyfin Client
|
|
102
|
-
// ==========================================
|
|
103
|
-
|
|
104
|
-
export class JellyfinClient implements IAutoSetupClient {
|
|
105
|
-
// ==========================================
|
|
106
|
-
// User Management
|
|
107
|
-
// ==========================================
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Get a user's details
|
|
111
|
-
*/
|
|
112
|
-
async getUser(userId: string): Promise<UserDto> {
|
|
113
|
-
return this.request<UserDto>(`/Users/${userId}`)
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Update a user's policy (permissions)
|
|
118
|
-
*/
|
|
119
|
-
async updateUserPolicy(userId: string, policy: Partial<UserPolicy>): Promise<void> {
|
|
120
|
-
await this.request(`/Users/${userId}/Policy`, {
|
|
121
|
-
method: "POST",
|
|
122
|
-
body: JSON.stringify(policy),
|
|
123
|
-
})
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
private baseUrl: string
|
|
127
|
-
private accessToken?: string
|
|
128
|
-
|
|
129
|
-
constructor(host: string, port: number, accessToken?: string) {
|
|
130
|
-
this.baseUrl = `http://${host}:${port}`
|
|
131
|
-
this.accessToken = accessToken
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
private async request<T>(endpoint: string, options: RequestInit = {}): Promise<T> {
|
|
135
|
-
const url = `${this.baseUrl}${endpoint}`
|
|
136
|
-
const headers: Record<string, string> = {
|
|
137
|
-
"Content-Type": "application/json",
|
|
138
|
-
// Jellyfin requires client identification
|
|
139
|
-
"X-Emby-Authorization":
|
|
140
|
-
'MediaBrowser Client="easiarr", Device="Server", DeviceId="easiarr-setup", Version="1.0.0"' +
|
|
141
|
-
(this.accessToken ? `, Token="${this.accessToken}"` : ""),
|
|
142
|
-
...((options.headers as Record<string, string>) || {}),
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
debugLog("JellyfinAPI", `${options.method || "GET"} ${url}`)
|
|
146
|
-
if (options.body) {
|
|
147
|
-
debugLog("JellyfinAPI", `Request Body: ${options.body}`)
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
const response = await fetch(url, { ...options, headers })
|
|
151
|
-
const text = await response.text()
|
|
152
|
-
|
|
153
|
-
debugLog("JellyfinAPI", `Response ${response.status} from ${endpoint}`)
|
|
154
|
-
if (text && text.length < 2000) {
|
|
155
|
-
debugLog("JellyfinAPI", `Response Body: ${text}`)
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
if (!response.ok) {
|
|
159
|
-
throw new Error(`Jellyfin API request failed: ${response.status} ${response.statusText} - ${text}`)
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
if (!text) return {} as T
|
|
163
|
-
return JSON.parse(text) as T
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// ==========================================
|
|
167
|
-
// Setup Wizard Methods (no auth required)
|
|
168
|
-
// ==========================================
|
|
169
|
-
|
|
170
|
-
/**
|
|
171
|
-
* Check if the startup wizard has been completed
|
|
172
|
-
*/
|
|
173
|
-
async isStartupComplete(): Promise<boolean> {
|
|
174
|
-
try {
|
|
175
|
-
const info = await this.request<SystemInfo>("/System/Info/Public")
|
|
176
|
-
return info.StartupWizardCompleted === true
|
|
177
|
-
} catch {
|
|
178
|
-
return false
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* Get current startup configuration
|
|
184
|
-
*/
|
|
185
|
-
async getStartupConfiguration(): Promise<StartupConfiguration> {
|
|
186
|
-
return this.request<StartupConfiguration>("/Startup/Configuration")
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
* Set startup configuration (metadata language, UI culture)
|
|
191
|
-
*/
|
|
192
|
-
async setStartupConfiguration(config: StartupConfiguration): Promise<void> {
|
|
193
|
-
await this.request("/Startup/Configuration", {
|
|
194
|
-
method: "POST",
|
|
195
|
-
body: JSON.stringify(config),
|
|
196
|
-
})
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* Get the first user (initializes user in database)
|
|
201
|
-
*/
|
|
202
|
-
async getFirstUser(): Promise<{ Name: string; Password: string }> {
|
|
203
|
-
return this.request<{ Name: string; Password: string }>("/Startup/FirstUser")
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* Create/update the initial admin user
|
|
208
|
-
* Must call getFirstUser first to initialize the user
|
|
209
|
-
*/
|
|
210
|
-
async createAdminUser(name: string, password: string): Promise<void> {
|
|
211
|
-
// First, get the initial user (this initializes the user in the database)
|
|
212
|
-
await this.getFirstUser()
|
|
213
|
-
|
|
214
|
-
// Then update with our credentials
|
|
215
|
-
const user: StartupUser = { Name: name, Password: password }
|
|
216
|
-
await this.request("/Startup/User", {
|
|
217
|
-
method: "POST",
|
|
218
|
-
body: JSON.stringify(user),
|
|
219
|
-
})
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
/**
|
|
223
|
-
* Configure remote access settings
|
|
224
|
-
*/
|
|
225
|
-
async setRemoteAccess(enableRemote: boolean, enableUPnP: boolean = false): Promise<void> {
|
|
226
|
-
const config: StartupRemoteAccess = {
|
|
227
|
-
EnableRemoteAccess: enableRemote,
|
|
228
|
-
EnableAutomaticPortMapping: enableUPnP,
|
|
229
|
-
}
|
|
230
|
-
await this.request("/Startup/RemoteAccess", {
|
|
231
|
-
method: "POST",
|
|
232
|
-
body: JSON.stringify(config),
|
|
233
|
-
})
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
/**
|
|
237
|
-
* Complete the startup wizard
|
|
238
|
-
*/
|
|
239
|
-
async completeStartup(): Promise<void> {
|
|
240
|
-
await this.request("/Startup/Complete", {
|
|
241
|
-
method: "POST",
|
|
242
|
-
})
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
/**
|
|
246
|
-
* Run the full setup wizard with sensible defaults
|
|
247
|
-
*/
|
|
248
|
-
async runSetupWizard(
|
|
249
|
-
adminName: string,
|
|
250
|
-
adminPassword: string,
|
|
251
|
-
options: {
|
|
252
|
-
uiCulture?: string
|
|
253
|
-
metadataCountry?: string
|
|
254
|
-
metadataLanguage?: string
|
|
255
|
-
enableRemoteAccess?: boolean
|
|
256
|
-
enableUPnP?: boolean
|
|
257
|
-
} = {}
|
|
258
|
-
): Promise<void> {
|
|
259
|
-
const {
|
|
260
|
-
uiCulture = "en-US",
|
|
261
|
-
metadataCountry = "US",
|
|
262
|
-
metadataLanguage = "en",
|
|
263
|
-
enableRemoteAccess = true,
|
|
264
|
-
enableUPnP = false,
|
|
265
|
-
} = options
|
|
266
|
-
|
|
267
|
-
// Step 1: Set UI culture and metadata language
|
|
268
|
-
await this.setStartupConfiguration({
|
|
269
|
-
UICulture: uiCulture,
|
|
270
|
-
MetadataCountryCode: metadataCountry,
|
|
271
|
-
PreferredMetadataLanguage: metadataLanguage,
|
|
272
|
-
})
|
|
273
|
-
|
|
274
|
-
// Step 2: Create admin user
|
|
275
|
-
await this.createAdminUser(adminName, adminPassword)
|
|
276
|
-
|
|
277
|
-
// Step 3: Configure remote access
|
|
278
|
-
await this.setRemoteAccess(enableRemoteAccess, enableUPnP)
|
|
279
|
-
|
|
280
|
-
// Step 4: Complete the wizard
|
|
281
|
-
await this.completeStartup()
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
// ==========================================
|
|
285
|
-
// Authentication (post-setup)
|
|
286
|
-
// ==========================================
|
|
287
|
-
|
|
288
|
-
/**
|
|
289
|
-
* Authenticate with username/password and get access token
|
|
290
|
-
*/
|
|
291
|
-
async authenticate(username: string, password: string): Promise<AuthResult> {
|
|
292
|
-
const result = await this.request<AuthResult>("/Users/AuthenticateByName", {
|
|
293
|
-
method: "POST",
|
|
294
|
-
body: JSON.stringify({
|
|
295
|
-
Username: username,
|
|
296
|
-
Pw: password,
|
|
297
|
-
}),
|
|
298
|
-
})
|
|
299
|
-
|
|
300
|
-
// Store token for subsequent requests
|
|
301
|
-
this.accessToken = result.AccessToken
|
|
302
|
-
return result
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
/**
|
|
306
|
-
* Set access token directly (if already known)
|
|
307
|
-
*/
|
|
308
|
-
setAccessToken(token: string): void {
|
|
309
|
-
this.accessToken = token
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
// ==========================================
|
|
313
|
-
// Library Management (requires auth)
|
|
314
|
-
// ==========================================
|
|
315
|
-
|
|
316
|
-
/**
|
|
317
|
-
* Get all virtual folders (media libraries)
|
|
318
|
-
*/
|
|
319
|
-
async getVirtualFolders(): Promise<VirtualFolderInfo[]> {
|
|
320
|
-
return this.request<VirtualFolderInfo[]>("/Library/VirtualFolders")
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
/**
|
|
324
|
-
* Add a new media library
|
|
325
|
-
*/
|
|
326
|
-
async addVirtualFolder(options: AddVirtualFolderOptions): Promise<void> {
|
|
327
|
-
const params = new URLSearchParams({
|
|
328
|
-
name: options.name,
|
|
329
|
-
collectionType: options.collectionType,
|
|
330
|
-
refreshLibrary: String(options.refreshLibrary ?? true),
|
|
331
|
-
})
|
|
332
|
-
|
|
333
|
-
// Paths need to be added to the body
|
|
334
|
-
await this.request(`/Library/VirtualFolders?${params.toString()}`, {
|
|
335
|
-
method: "POST",
|
|
336
|
-
body: JSON.stringify({
|
|
337
|
-
LibraryOptions: {
|
|
338
|
-
PathInfos: options.paths.map((path) => ({ Path: path })),
|
|
339
|
-
},
|
|
340
|
-
}),
|
|
341
|
-
})
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
/**
|
|
345
|
-
* Add default media libraries based on common media stack paths
|
|
346
|
-
*/
|
|
347
|
-
async addDefaultLibraries(): Promise<void> {
|
|
348
|
-
const defaultLibraries: AddVirtualFolderOptions[] = [
|
|
349
|
-
{ name: "Movies", collectionType: "movies", paths: ["/data/media/movies"] },
|
|
350
|
-
{ name: "TV Shows", collectionType: "tvshows", paths: ["/data/media/tv"] },
|
|
351
|
-
{ name: "Music", collectionType: "music", paths: ["/data/media/music"] },
|
|
352
|
-
]
|
|
353
|
-
|
|
354
|
-
for (const lib of defaultLibraries) {
|
|
355
|
-
try {
|
|
356
|
-
await this.addVirtualFolder(lib)
|
|
357
|
-
} catch (error) {
|
|
358
|
-
// Library might already exist, continue with others
|
|
359
|
-
debugLog("JellyfinAPI", `Failed to add library ${lib.name}: ${error}`)
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
// ==========================================
|
|
365
|
-
// API Key Management (requires auth)
|
|
366
|
-
// ==========================================
|
|
367
|
-
|
|
368
|
-
/**
|
|
369
|
-
* Create an API key for external access (e.g., Homepage widget)
|
|
370
|
-
*/
|
|
371
|
-
async createApiKey(appName: string): Promise<string> {
|
|
372
|
-
await this.request(`/Auth/Keys?app=${encodeURIComponent(appName)}`, {
|
|
373
|
-
method: "POST",
|
|
374
|
-
})
|
|
375
|
-
|
|
376
|
-
// Get all keys and find the one we just created
|
|
377
|
-
const keys = await this.getApiKeys()
|
|
378
|
-
const key = keys.find((k) => k.AppName === appName)
|
|
379
|
-
return key?.AccessToken || ""
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
/**
|
|
383
|
-
* Get all API keys
|
|
384
|
-
*/
|
|
385
|
-
async getApiKeys(): Promise<{ AccessToken: string; AppName: string; DateCreated: string }[]> {
|
|
386
|
-
const result = await this.request<{
|
|
387
|
-
Items: { AccessToken: string; AppName: string; DateCreated: string }[]
|
|
388
|
-
}>("/Auth/Keys")
|
|
389
|
-
return result.Items || []
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
// ==========================================
|
|
393
|
-
// Health Check
|
|
394
|
-
// ==========================================
|
|
395
|
-
|
|
396
|
-
/**
|
|
397
|
-
* Check if Jellyfin is running and accessible
|
|
398
|
-
*/
|
|
399
|
-
async isHealthy(): Promise<boolean> {
|
|
400
|
-
try {
|
|
401
|
-
await this.request<SystemInfo>("/System/Info/Public")
|
|
402
|
-
return true
|
|
403
|
-
} catch {
|
|
404
|
-
return false
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
/**
|
|
409
|
-
* Get public system info (no auth required)
|
|
410
|
-
*/
|
|
411
|
-
async getPublicSystemInfo(): Promise<SystemInfo> {
|
|
412
|
-
return this.request<SystemInfo>("/System/Info/Public")
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
/**
|
|
416
|
-
* Check if already configured (wizard completed)
|
|
417
|
-
*/
|
|
418
|
-
async isInitialized(): Promise<boolean> {
|
|
419
|
-
return this.isStartupComplete()
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
/**
|
|
423
|
-
* Run the auto-setup process for Jellyfin
|
|
424
|
-
*/
|
|
425
|
-
async setup(options: AutoSetupOptions): Promise<AutoSetupResult> {
|
|
426
|
-
const { username, password } = options
|
|
427
|
-
|
|
428
|
-
try {
|
|
429
|
-
// Check if reachable
|
|
430
|
-
const healthy = await this.isHealthy()
|
|
431
|
-
if (!healthy) {
|
|
432
|
-
return { success: false, message: "Jellyfin not reachable" }
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
// Check if wizard already completed
|
|
436
|
-
const initialized = await this.isStartupComplete()
|
|
437
|
-
if (initialized) {
|
|
438
|
-
return {
|
|
439
|
-
success: true,
|
|
440
|
-
message: "Already configured",
|
|
441
|
-
data: { alreadyInitialized: true },
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
// Run the setup wizard
|
|
446
|
-
await this.runSetupWizard(username, password)
|
|
447
|
-
|
|
448
|
-
// Try to authenticate to get access token
|
|
449
|
-
const authResult = await this.authenticate(username, password)
|
|
450
|
-
|
|
451
|
-
// Create API key for Homepage etc.
|
|
452
|
-
let apiKey: string | undefined
|
|
453
|
-
try {
|
|
454
|
-
apiKey = await this.createApiKey("easiarr")
|
|
455
|
-
} catch {
|
|
456
|
-
// API key creation may fail if permission issues
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
return {
|
|
460
|
-
success: true,
|
|
461
|
-
message: "Setup wizard completed",
|
|
462
|
-
data: {
|
|
463
|
-
accessToken: authResult.AccessToken,
|
|
464
|
-
serverId: authResult.ServerId,
|
|
465
|
-
apiKey,
|
|
466
|
-
},
|
|
467
|
-
envUpdates: apiKey ? { API_KEY_JELLYFIN: apiKey } : undefined,
|
|
468
|
-
}
|
|
469
|
-
} catch (error) {
|
|
470
|
-
return { success: false, message: `${error}` }
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
}
|