@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.
Files changed (109) hide show
  1. package/dist/index.js +29837 -0
  2. package/package.json +15 -15
  3. package/src/VersionInfo.ts +0 -12
  4. package/src/api/arr-api.ts +0 -521
  5. package/src/api/auto-setup-types.ts +0 -64
  6. package/src/api/base-api.ts +0 -216
  7. package/src/api/bazarr-api.ts +0 -297
  8. package/src/api/cloudflare-api.ts +0 -493
  9. package/src/api/custom-format-api.ts +0 -224
  10. package/src/api/grafana-api.ts +0 -230
  11. package/src/api/heimdall-api.ts +0 -141
  12. package/src/api/homarr-api.ts +0 -209
  13. package/src/api/huntarr-api.ts +0 -457
  14. package/src/api/index.ts +0 -9
  15. package/src/api/jellyfin-api.ts +0 -389
  16. package/src/api/jellyseerr-api.ts +0 -559
  17. package/src/api/maintainerr-api.ts +0 -461
  18. package/src/api/naming-config.ts +0 -101
  19. package/src/api/overseerr-api.ts +0 -392
  20. package/src/api/plex-api.ts +0 -308
  21. package/src/api/portainer-api.ts +0 -374
  22. package/src/api/profilarr-api.ts +0 -391
  23. package/src/api/prowlarr-api.ts +0 -488
  24. package/src/api/qbittorrent-api.ts +0 -300
  25. package/src/api/quality-profile-api.ts +0 -272
  26. package/src/api/tautulli-api.ts +0 -244
  27. package/src/api/uptime-kuma-api.ts +0 -343
  28. package/src/apps/categories.ts +0 -14
  29. package/src/apps/index.ts +0 -2
  30. package/src/apps/registry.ts +0 -1275
  31. package/src/compose/caddy-config.ts +0 -130
  32. package/src/compose/generator.ts +0 -287
  33. package/src/compose/index.ts +0 -7
  34. package/src/compose/templates.ts +0 -78
  35. package/src/compose/traefik-config.ts +0 -187
  36. package/src/config/bookmarks-generator.ts +0 -160
  37. package/src/config/defaults.ts +0 -37
  38. package/src/config/homepage-config.ts +0 -381
  39. package/src/config/index.ts +0 -6
  40. package/src/config/manager.ts +0 -150
  41. package/src/config/recyclarr-config.ts +0 -181
  42. package/src/config/schema.ts +0 -310
  43. package/src/config/slskd-config.ts +0 -86
  44. package/src/config/soularr-config.ts +0 -105
  45. package/src/config/trash-quality-definitions.ts +0 -213
  46. package/src/data/lidarr-custom-formats.ts +0 -127
  47. package/src/data/trash-profiles.ts +0 -333
  48. package/src/docker/client.ts +0 -312
  49. package/src/docker/index.ts +0 -18
  50. package/src/index.ts +0 -77
  51. package/src/setup/actions/arr-common.ts +0 -307
  52. package/src/setup/actions/dashboards.ts +0 -79
  53. package/src/setup/actions/download-clients.ts +0 -54
  54. package/src/setup/actions/jellyseerr.ts +0 -331
  55. package/src/setup/actions/media-servers.ts +0 -68
  56. package/src/setup/actions/monitoring.ts +0 -120
  57. package/src/setup/actions/prowlarr.ts +0 -137
  58. package/src/setup/actions/utilities.ts +0 -490
  59. package/src/setup/index.ts +0 -78
  60. package/src/setup/types.ts +0 -144
  61. package/src/structure/manager.ts +0 -129
  62. package/src/ui/App.ts +0 -111
  63. package/src/ui/components/ApplicationSelector.ts +0 -275
  64. package/src/ui/components/CredentialsForm.ts +0 -180
  65. package/src/ui/components/DownloadClientForm.ts +0 -114
  66. package/src/ui/components/FileEditor.ts +0 -91
  67. package/src/ui/components/FooterHint.ts +0 -119
  68. package/src/ui/components/PageLayout.ts +0 -160
  69. package/src/ui/components/UpdateNotification.ts +0 -102
  70. package/src/ui/index.ts +0 -7
  71. package/src/ui/screens/AdvancedSettings.ts +0 -188
  72. package/src/ui/screens/ApiKeyViewer.ts +0 -511
  73. package/src/ui/screens/AppConfigurator.ts +0 -543
  74. package/src/ui/screens/AppManager.ts +0 -284
  75. package/src/ui/screens/ContainerControl.ts +0 -581
  76. package/src/ui/screens/LogsViewer.ts +0 -478
  77. package/src/ui/screens/MainMenu.ts +0 -299
  78. package/src/ui/screens/MonitorDashboard.ts +0 -881
  79. package/src/ui/screens/SecretsEditor.ts +0 -216
  80. package/src/ui/screens/SettingsScreen.ts +0 -633
  81. package/src/ui/screens/index.ts +0 -22
  82. package/src/ui/screens/setup/BaseAppSetupScreen.ts +0 -375
  83. package/src/ui/screens/setup/CloudflaredSetup.ts +0 -460
  84. package/src/ui/screens/setup/FullAutoSetup.ts +0 -766
  85. package/src/ui/screens/setup/HomepageSetup.ts +0 -165
  86. package/src/ui/screens/setup/JellyfinSetup.ts +0 -328
  87. package/src/ui/screens/setup/JellyseerrSetup.ts +0 -242
  88. package/src/ui/screens/setup/ProwlarrSetup.ts +0 -500
  89. package/src/ui/screens/setup/QBittorrentSetup.ts +0 -221
  90. package/src/ui/screens/setup/QuickSetup.ts +0 -1169
  91. package/src/ui/screens/setup/RecyclarrSetup.ts +0 -270
  92. package/src/ui/screens/setup/TRaSHProfileSetup.ts +0 -381
  93. package/src/utils/arch.ts +0 -78
  94. package/src/utils/browser.ts +0 -27
  95. package/src/utils/categories.ts +0 -61
  96. package/src/utils/debug.ts +0 -84
  97. package/src/utils/env.ts +0 -138
  98. package/src/utils/index.ts +0 -8
  99. package/src/utils/logs.ts +0 -119
  100. package/src/utils/migrations/1765626338_rename_env_variables.ts +0 -102
  101. package/src/utils/migrations/1765707135_rename_easiarr_status.ts +0 -91
  102. package/src/utils/migrations/1765732722_remove_cloudflare_dns_api_token.ts +0 -45
  103. package/src/utils/migrations/1769515650_migrate_to_xdg.ts +0 -114
  104. package/src/utils/migrations.ts +0 -165
  105. package/src/utils/password.ts +0 -42
  106. package/src/utils/paths.ts +0 -33
  107. package/src/utils/unraid.ts +0 -103
  108. package/src/utils/update-checker.ts +0 -210
  109. package/src/utils/url-utils.ts +0 -38
@@ -1,389 +0,0 @@
1
- /**
2
- * Jellyfin API Client
3
- * Handles setup wizard automation and media library management
4
- */
5
-
6
- import type { AutoSetupOptions, AutoSetupResult, IAutoSetupClient } from "./auto-setup-types"
7
- import { debugLog } from "~/utils/debug"
8
- import { BaseApiClient } from "./base-api"
9
-
10
- // ==========================================
11
- // Startup Wizard Types
12
- // ==========================================
13
-
14
- export interface StartupConfiguration {
15
- UICulture?: string
16
- MetadataCountryCode?: string
17
- PreferredMetadataLanguage?: string
18
- }
19
-
20
- export interface StartupUser {
21
- Name: string
22
- Password: string
23
- }
24
-
25
- export interface StartupRemoteAccess {
26
- EnableRemoteAccess: boolean
27
- EnableAutomaticPortMapping: boolean
28
- }
29
-
30
- // ==========================================
31
- // Library Types
32
- // ==========================================
33
-
34
- export interface VirtualFolderInfo {
35
- Name: string
36
- Locations: string[]
37
- CollectionType: LibraryType
38
- ItemId?: string
39
- }
40
-
41
- export type LibraryType =
42
- | "movies"
43
- | "tvshows"
44
- | "music"
45
- | "books"
46
- | "homevideos"
47
- | "musicvideos"
48
- | "photos"
49
- | "playlists"
50
- | "boxsets"
51
-
52
- export interface AddVirtualFolderOptions {
53
- name: string
54
- collectionType: LibraryType
55
- paths: string[]
56
- refreshLibrary?: boolean
57
- }
58
-
59
- // ==========================================
60
- // System Types
61
- // ==========================================
62
-
63
- export interface SystemInfo {
64
- ServerName: string
65
- Version: string
66
- Id: string
67
- OperatingSystem: string
68
- StartupWizardCompleted: boolean
69
- }
70
-
71
- // ==========================================
72
- // User Types
73
- // ==========================================
74
-
75
- export interface UserPolicy {
76
- IsAdministrator: boolean
77
- IsHidden: boolean
78
- IsDisabled: boolean
79
- EnableRemoteAccess: boolean
80
- AuthenticationProviderId?: string
81
- PasswordResetProviderId?: string
82
- [key: string]: unknown
83
- }
84
-
85
- export interface UserDto {
86
- Id: string
87
- Name?: string
88
- ServerId?: string
89
- HasPassword: boolean
90
- LastLoginDate?: string
91
- Policy?: UserPolicy
92
- [key: string]: unknown
93
- }
94
-
95
- export interface AuthResult {
96
- AccessToken: string
97
- ServerId: string
98
- User: UserDto
99
- }
100
-
101
- // ==========================================
102
- // Jellyfin Client
103
- // ==========================================
104
-
105
- export class JellyfinClient extends BaseApiClient implements IAutoSetupClient {
106
- protected readonly logPrefix = "JellyfinAPI"
107
- private accessToken?: string
108
-
109
- constructor(host: string, port: number, accessToken?: string) {
110
- super(host, port)
111
- this.accessToken = accessToken
112
- }
113
-
114
- /**
115
- * Jellyfin requires special authorization header
116
- */
117
- private getAuthHeader(): string {
118
- return (
119
- 'MediaBrowser Client="easiarr", Device="Server", DeviceId="easiarr-setup", Version="1.0.0"' +
120
- (this.accessToken ? `, Token="${this.accessToken}"` : "")
121
- )
122
- }
123
-
124
- /**
125
- * Make a request to Jellyfin API
126
- */
127
- private async request<T>(endpoint: string, options: RequestInit = {}): Promise<T> {
128
- const url = `${this.baseUrl}${endpoint}`
129
- const headers: Record<string, string> = {
130
- "Content-Type": "application/json",
131
- "X-Emby-Authorization": this.getAuthHeader(),
132
- ...((options.headers as Record<string, string>) || {}),
133
- }
134
-
135
- debugLog("JellyfinAPI", `${options.method || "GET"} ${url}`)
136
-
137
- const response = await fetch(url, { ...options, headers })
138
- const text = await response.text()
139
-
140
- if (!response.ok) {
141
- throw new Error(`Jellyfin API request failed: ${response.status} ${response.statusText}`)
142
- }
143
-
144
- if (!text) return {} as T
145
- return JSON.parse(text) as T
146
- }
147
-
148
- // ==========================================
149
- // User Management
150
- // ==========================================
151
-
152
- async getUser(userId: string): Promise<UserDto> {
153
- return this.request<UserDto>(`/Users/${userId}`)
154
- }
155
-
156
- async updateUserPolicy(userId: string, policy: Partial<UserPolicy>): Promise<void> {
157
- await this.request(`/Users/${userId}/Policy`, {
158
- method: "POST",
159
- body: JSON.stringify(policy),
160
- })
161
- }
162
-
163
- // ==========================================
164
- // Setup Wizard Methods
165
- // ==========================================
166
-
167
- async isStartupComplete(): Promise<boolean> {
168
- try {
169
- const info = await this.request<SystemInfo>("/System/Info/Public")
170
- return info.StartupWizardCompleted === true
171
- } catch {
172
- return false
173
- }
174
- }
175
-
176
- async getStartupConfiguration(): Promise<StartupConfiguration> {
177
- return this.request<StartupConfiguration>("/Startup/Configuration")
178
- }
179
-
180
- async setStartupConfiguration(config: StartupConfiguration): Promise<void> {
181
- await this.request("/Startup/Configuration", {
182
- method: "POST",
183
- body: JSON.stringify(config),
184
- })
185
- }
186
-
187
- async getFirstUser(): Promise<{ Name: string; Password: string }> {
188
- return this.request<{ Name: string; Password: string }>("/Startup/FirstUser")
189
- }
190
-
191
- async createAdminUser(name: string, password: string): Promise<void> {
192
- await this.getFirstUser()
193
- const user: StartupUser = { Name: name, Password: password }
194
- await this.request("/Startup/User", {
195
- method: "POST",
196
- body: JSON.stringify(user),
197
- })
198
- }
199
-
200
- async setRemoteAccess(enableRemote: boolean, enableUPnP: boolean = false): Promise<void> {
201
- const config: StartupRemoteAccess = {
202
- EnableRemoteAccess: enableRemote,
203
- EnableAutomaticPortMapping: enableUPnP,
204
- }
205
- await this.request("/Startup/RemoteAccess", {
206
- method: "POST",
207
- body: JSON.stringify(config),
208
- })
209
- }
210
-
211
- async completeStartup(): Promise<void> {
212
- await this.request("/Startup/Complete", { method: "POST" })
213
- }
214
-
215
- async runSetupWizard(
216
- adminName: string,
217
- adminPassword: string,
218
- options: {
219
- uiCulture?: string
220
- metadataCountry?: string
221
- metadataLanguage?: string
222
- enableRemoteAccess?: boolean
223
- enableUPnP?: boolean
224
- } = {}
225
- ): Promise<void> {
226
- const {
227
- uiCulture = "en-US",
228
- metadataCountry = "US",
229
- metadataLanguage = "en",
230
- enableRemoteAccess = true,
231
- enableUPnP = false,
232
- } = options
233
-
234
- await this.setStartupConfiguration({
235
- UICulture: uiCulture,
236
- MetadataCountryCode: metadataCountry,
237
- PreferredMetadataLanguage: metadataLanguage,
238
- })
239
-
240
- await this.createAdminUser(adminName, adminPassword)
241
- await this.setRemoteAccess(enableRemoteAccess, enableUPnP)
242
- await this.completeStartup()
243
- }
244
-
245
- // ==========================================
246
- // Authentication
247
- // ==========================================
248
-
249
- async authenticate(username: string, password: string): Promise<AuthResult> {
250
- const result = await this.request<AuthResult>("/Users/AuthenticateByName", {
251
- method: "POST",
252
- body: JSON.stringify({ Username: username, Pw: password }),
253
- })
254
- this.accessToken = result.AccessToken
255
- return result
256
- }
257
-
258
- setAccessToken(token: string): void {
259
- this.accessToken = token
260
- }
261
-
262
- // ==========================================
263
- // Library Management
264
- // ==========================================
265
-
266
- async getVirtualFolders(): Promise<VirtualFolderInfo[]> {
267
- return this.request<VirtualFolderInfo[]>("/Library/VirtualFolders")
268
- }
269
-
270
- async addVirtualFolder(options: AddVirtualFolderOptions): Promise<void> {
271
- const params = new URLSearchParams({
272
- name: options.name,
273
- collectionType: options.collectionType,
274
- refreshLibrary: String(options.refreshLibrary ?? true),
275
- })
276
-
277
- await this.request(`/Library/VirtualFolders?${params.toString()}`, {
278
- method: "POST",
279
- body: JSON.stringify({
280
- LibraryOptions: {
281
- PathInfos: options.paths.map((path) => ({ Path: path })),
282
- },
283
- }),
284
- })
285
- }
286
-
287
- async addDefaultLibraries(): Promise<void> {
288
- const defaultLibraries: AddVirtualFolderOptions[] = [
289
- { name: "Movies", collectionType: "movies", paths: ["/data/media/movies"] },
290
- { name: "TV Shows", collectionType: "tvshows", paths: ["/data/media/tv"] },
291
- { name: "Music", collectionType: "music", paths: ["/data/media/music"] },
292
- ]
293
-
294
- for (const lib of defaultLibraries) {
295
- try {
296
- await this.addVirtualFolder(lib)
297
- } catch (error) {
298
- debugLog("JellyfinAPI", `Failed to add library ${lib.name}: ${error}`)
299
- }
300
- }
301
- }
302
-
303
- // ==========================================
304
- // API Key Management
305
- // ==========================================
306
-
307
- async createApiKey(appName: string): Promise<string> {
308
- await this.request(`/Auth/Keys?app=${encodeURIComponent(appName)}`, { method: "POST" })
309
-
310
- const keys = await this.getApiKeys()
311
- const key = keys.find((k) => k.AppName === appName)
312
- return key?.AccessToken || ""
313
- }
314
-
315
- async getApiKeys(): Promise<{ AccessToken: string; AppName: string; DateCreated: string }[]> {
316
- const result = await this.request<{
317
- Items: { AccessToken: string; AppName: string; DateCreated: string }[]
318
- }>("/Auth/Keys")
319
- return result.Items || []
320
- }
321
-
322
- // ==========================================
323
- // Health Check
324
- // ==========================================
325
-
326
- async isHealthy(): Promise<boolean> {
327
- try {
328
- await this.request<SystemInfo>("/System/Info/Public")
329
- return true
330
- } catch {
331
- return false
332
- }
333
- }
334
-
335
- async getPublicSystemInfo(): Promise<SystemInfo> {
336
- return this.request<SystemInfo>("/System/Info/Public")
337
- }
338
-
339
- async isInitialized(): Promise<boolean> {
340
- return this.isStartupComplete()
341
- }
342
-
343
- // ==========================================
344
- // Auto-Setup
345
- // ==========================================
346
-
347
- async setup(options: AutoSetupOptions): Promise<AutoSetupResult> {
348
- const { username, password } = options
349
-
350
- try {
351
- const healthy = await this.isHealthy()
352
- if (!healthy) {
353
- return { success: false, message: "Jellyfin not reachable" }
354
- }
355
-
356
- const initialized = await this.isStartupComplete()
357
- if (initialized) {
358
- return {
359
- success: true,
360
- message: "Already configured",
361
- data: { alreadyInitialized: true },
362
- }
363
- }
364
-
365
- await this.runSetupWizard(username, password)
366
- const authResult = await this.authenticate(username, password)
367
-
368
- let apiKey: string | undefined
369
- try {
370
- apiKey = await this.createApiKey("easiarr")
371
- } catch {
372
- // API key creation may fail
373
- }
374
-
375
- return {
376
- success: true,
377
- message: "Setup wizard completed",
378
- data: {
379
- accessToken: authResult.AccessToken,
380
- serverId: authResult.ServerId,
381
- apiKey,
382
- },
383
- envUpdates: apiKey ? { API_KEY_JELLYFIN: apiKey } : undefined,
384
- }
385
- } catch (error) {
386
- return { success: false, message: `${error}` }
387
- }
388
- }
389
- }