@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,216 +0,0 @@
1
- /**
2
- * Base API Client
3
- * Abstract class providing shared fetch, logging, and error handling for all API clients.
4
- */
5
-
6
- import { debugLog } from "~/utils/debug"
7
-
8
- export interface ApiResponse<T> {
9
- success: boolean
10
- data?: T
11
- error?: string
12
- status?: number
13
- }
14
-
15
- export abstract class BaseApiClient {
16
- protected host: string
17
- protected port: number
18
- protected abstract readonly logPrefix: string
19
-
20
- constructor(host: string, port: number) {
21
- this.host = host
22
- this.port = port
23
- }
24
-
25
- /**
26
- * Base URL for the API (override in subclass if protocol differs)
27
- */
28
- protected get baseUrl(): string {
29
- return `http://${this.host}:${this.port}`
30
- }
31
-
32
- /**
33
- * Build API URL for a given endpoint
34
- */
35
- protected buildUrl(endpoint: string, basePath: string = ""): string {
36
- return `${this.baseUrl}${basePath}${endpoint}`
37
- }
38
-
39
- /**
40
- * Generic GET request with logging and error handling
41
- */
42
- protected async get<T>(
43
- endpoint: string,
44
- options: { basePath?: string; headers?: Record<string, string> } = {}
45
- ): Promise<ApiResponse<T>> {
46
- const url = this.buildUrl(endpoint, options.basePath)
47
- try {
48
- debugLog(this.logPrefix, `GET ${endpoint}`)
49
- const response = await fetch(url, {
50
- method: "GET",
51
- headers: options.headers,
52
- })
53
- if (!response.ok) {
54
- debugLog(this.logPrefix, `GET ${endpoint} failed: ${response.status}`)
55
- return { success: false, status: response.status }
56
- }
57
- const data = (await response.json()) as T
58
- return { success: true, data }
59
- } catch (error) {
60
- debugLog(this.logPrefix, `GET ${endpoint} error: ${error}`)
61
- return { success: false, error: String(error) }
62
- }
63
- }
64
-
65
- /**
66
- * Generic GET request returning plain text
67
- */
68
- protected async getText(
69
- endpoint: string,
70
- options: { basePath?: string; headers?: Record<string, string> } = {}
71
- ): Promise<ApiResponse<string>> {
72
- const url = this.buildUrl(endpoint, options.basePath)
73
- try {
74
- debugLog(this.logPrefix, `GET (text) ${endpoint}`)
75
- const response = await fetch(url, {
76
- method: "GET",
77
- headers: options.headers,
78
- })
79
- if (!response.ok) {
80
- debugLog(this.logPrefix, `GET ${endpoint} failed: ${response.status}`)
81
- return { success: false, status: response.status }
82
- }
83
- const data = await response.text()
84
- return { success: true, data }
85
- } catch (error) {
86
- debugLog(this.logPrefix, `GET ${endpoint} error: ${error}`)
87
- return { success: false, error: String(error) }
88
- }
89
- }
90
-
91
- /**
92
- * Generic POST request with logging and error handling
93
- */
94
- protected async post<T, B = unknown>(
95
- endpoint: string,
96
- body?: B,
97
- options: { basePath?: string; headers?: Record<string, string> } = {}
98
- ): Promise<ApiResponse<T>> {
99
- const url = this.buildUrl(endpoint, options.basePath)
100
- try {
101
- debugLog(this.logPrefix, `POST ${endpoint}`)
102
- const response = await fetch(url, {
103
- method: "POST",
104
- headers: {
105
- "Content-Type": "application/json",
106
- ...options.headers,
107
- },
108
- body: body ? JSON.stringify(body) : undefined,
109
- })
110
- if (!response.ok) {
111
- debugLog(this.logPrefix, `POST ${endpoint} failed: ${response.status}`)
112
- return { success: false, status: response.status }
113
- }
114
- // Handle empty responses
115
- const text = await response.text()
116
- if (!text) {
117
- return { success: true }
118
- }
119
- try {
120
- const data = JSON.parse(text) as T
121
- return { success: true, data }
122
- } catch {
123
- // Response was not JSON
124
- return { success: true }
125
- }
126
- } catch (error) {
127
- debugLog(this.logPrefix, `POST ${endpoint} error: ${error}`)
128
- return { success: false, error: String(error) }
129
- }
130
- }
131
-
132
- /**
133
- * Generic PUT request with logging and error handling
134
- */
135
- protected async put<T, B = unknown>(
136
- endpoint: string,
137
- body?: B,
138
- options: { basePath?: string; headers?: Record<string, string> } = {}
139
- ): Promise<ApiResponse<T>> {
140
- const url = this.buildUrl(endpoint, options.basePath)
141
- try {
142
- debugLog(this.logPrefix, `PUT ${endpoint}`)
143
- const response = await fetch(url, {
144
- method: "PUT",
145
- headers: {
146
- "Content-Type": "application/json",
147
- ...options.headers,
148
- },
149
- body: body ? JSON.stringify(body) : undefined,
150
- })
151
- if (!response.ok) {
152
- debugLog(this.logPrefix, `PUT ${endpoint} failed: ${response.status}`)
153
- return { success: false, status: response.status }
154
- }
155
- const text = await response.text()
156
- if (!text) {
157
- return { success: true }
158
- }
159
- try {
160
- const data = JSON.parse(text) as T
161
- return { success: true, data }
162
- } catch {
163
- return { success: true }
164
- }
165
- } catch (error) {
166
- debugLog(this.logPrefix, `PUT ${endpoint} error: ${error}`)
167
- return { success: false, error: String(error) }
168
- }
169
- }
170
-
171
- /**
172
- * Generic DELETE request with logging and error handling
173
- */
174
- protected async delete<T>(
175
- endpoint: string,
176
- options: { basePath?: string; headers?: Record<string, string> } = {}
177
- ): Promise<ApiResponse<T>> {
178
- const url = this.buildUrl(endpoint, options.basePath)
179
- try {
180
- debugLog(this.logPrefix, `DELETE ${endpoint}`)
181
- const response = await fetch(url, {
182
- method: "DELETE",
183
- headers: options.headers,
184
- })
185
- if (!response.ok) {
186
- debugLog(this.logPrefix, `DELETE ${endpoint} failed: ${response.status}`)
187
- return { success: false, status: response.status }
188
- }
189
- const text = await response.text()
190
- if (!text) {
191
- return { success: true }
192
- }
193
- try {
194
- const data = JSON.parse(text) as T
195
- return { success: true, data }
196
- } catch {
197
- return { success: true }
198
- }
199
- } catch (error) {
200
- debugLog(this.logPrefix, `DELETE ${endpoint} error: ${error}`)
201
- return { success: false, error: String(error) }
202
- }
203
- }
204
-
205
- /**
206
- * Simple health check - override in subclass for custom logic
207
- */
208
- async isHealthy(): Promise<boolean> {
209
- try {
210
- const response = await fetch(this.baseUrl, { method: "HEAD" })
211
- return response.ok
212
- } catch {
213
- return false
214
- }
215
- }
216
- }
@@ -1,297 +0,0 @@
1
- /**
2
- * Bazarr API Client
3
- * Handles Bazarr-specific API calls for authentication and settings
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
- * Bazarr Language Profile Structure
12
- */
13
- export interface BazarrLanguageProfile {
14
- name: string
15
- cutoff: string
16
- languages: {
17
- code: string
18
- forced: boolean
19
- hi: boolean
20
- }[]
21
- }
22
-
23
- /**
24
- * Bazarr System Settings (partial - auth related fields)
25
- */
26
- export interface BazarrAuthSettings {
27
- auth: {
28
- type: "None" | "Basic" | "Form"
29
- username: string
30
- password: string
31
- apikey: string
32
- }
33
- }
34
-
35
- /**
36
- * Bazarr API Client
37
- * Note: Bazarr uses form data for POST, not JSON!
38
- */
39
- export class BazarrApiClient extends BaseApiClient implements IAutoSetupClient {
40
- protected readonly logPrefix = "Bazarr"
41
- private apiKey: string | null = null
42
-
43
- constructor(host: string, port: number) {
44
- super(host, port)
45
- }
46
-
47
- /**
48
- * Set API key for authentication
49
- */
50
- setApiKey(key: string): void {
51
- this.apiKey = key
52
- debugLog("Bazarr", `API key set`)
53
- }
54
-
55
- /**
56
- * Build API URL with optional API key
57
- */
58
- private buildApiUrlWithKey(endpoint: string): string {
59
- let url = `${this.baseUrl}/api${endpoint}`
60
- if (this.apiKey) {
61
- url = `${url}${url.includes("?") ? "&" : "?"}apikey=${this.apiKey}`
62
- }
63
- return url
64
- }
65
-
66
- /**
67
- * Make a GET request to Bazarr API
68
- */
69
- private async getJson<T>(endpoint: string): Promise<T> {
70
- const url = this.buildApiUrlWithKey(endpoint)
71
- debugLog("Bazarr", `GET ${url}`)
72
-
73
- const response = await fetch(url, {
74
- method: "GET",
75
- headers: { Accept: "application/json" },
76
- })
77
-
78
- if (!response.ok) {
79
- throw new Error(`Bazarr API error: ${response.status} ${response.statusText}`)
80
- }
81
-
82
- const text = await response.text()
83
- if (!text) return {} as T
84
- return JSON.parse(text) as T
85
- }
86
-
87
- /**
88
- * Make a POST request using form data (NOT JSON)
89
- */
90
- private async postForm(endpoint: string, data: Record<string, string>): Promise<void> {
91
- const url = this.buildApiUrlWithKey(endpoint)
92
- const formData = new URLSearchParams()
93
- for (const [key, value] of Object.entries(data)) {
94
- formData.append(key, value)
95
- }
96
-
97
- debugLog("Bazarr", `POST ${url}`)
98
-
99
- const response = await fetch(url, {
100
- method: "POST",
101
- headers: { "Content-Type": "application/x-www-form-urlencoded" },
102
- body: formData.toString(),
103
- })
104
-
105
- if (!response.ok) {
106
- throw new Error(`Bazarr API error: ${response.status} ${response.statusText}`)
107
- }
108
- }
109
-
110
- /**
111
- * Check if Bazarr is healthy
112
- */
113
- async isHealthy(): Promise<boolean> {
114
- try {
115
- await this.getJson("/system/status")
116
- return true
117
- } catch {
118
- return false
119
- }
120
- }
121
-
122
- /**
123
- * Get current system settings
124
- */
125
- async getSettings(): Promise<Record<string, unknown>> {
126
- return this.getJson<Record<string, unknown>>("/system/settings")
127
- }
128
-
129
- /**
130
- * Update authentication settings
131
- */
132
- async enableFormAuth(username: string, password: string, override = false): Promise<boolean> {
133
- try {
134
- const currentSettings = await this.getSettings()
135
- const currentAuth = (currentSettings as { auth?: { type?: string } }).auth
136
-
137
- if (currentAuth?.type && currentAuth.type !== "None" && !override) {
138
- debugLog("Bazarr", `Auth already configured (type: ${currentAuth.type}), skipping`)
139
- return false
140
- }
141
-
142
- await this.postForm("/system/settings", {
143
- "settings-auth-type": "form",
144
- "settings-auth-username": username,
145
- "settings-auth-password": password,
146
- })
147
-
148
- debugLog("Bazarr", `Form auth enabled for user: ${username}`)
149
- return true
150
- } catch (e) {
151
- debugLog("Bazarr", `Failed to enable form auth: ${e}`)
152
- throw e
153
- }
154
- }
155
-
156
- /**
157
- * Get API key from settings
158
- */
159
- async getApiKey(): Promise<string | null> {
160
- try {
161
- const settings = await this.getSettings()
162
- const auth = (settings as unknown as BazarrAuthSettings).auth
163
- return auth?.apikey || null
164
- } catch {
165
- return null
166
- }
167
- }
168
-
169
- /**
170
- * Configure Radarr connection
171
- */
172
- async configureRadarr(host: string, port: number, apiKey: string): Promise<boolean> {
173
- await this.postForm("/system/settings", {
174
- "settings-radarr-ip": host,
175
- "settings-radarr-port": String(port),
176
- "settings-radarr-apikey": apiKey,
177
- "settings-radarr-base_url": "",
178
- "settings-radarr-ssl": "false",
179
- "settings-general-use_radarr": "true",
180
- })
181
- return true
182
- }
183
-
184
- /**
185
- * Configure Sonarr connection
186
- */
187
- async configureSonarr(host: string, port: number, apiKey: string): Promise<boolean> {
188
- await this.postForm("/system/settings", {
189
- "settings-sonarr-ip": host,
190
- "settings-sonarr-port": String(port),
191
- "settings-sonarr-apikey": apiKey,
192
- "settings-sonarr-base_url": "",
193
- "settings-sonarr-ssl": "false",
194
- "settings-general-use_sonarr": "true",
195
- })
196
- return true
197
- }
198
-
199
- /**
200
- * Check if already configured
201
- */
202
- async isInitialized(): Promise<boolean> {
203
- try {
204
- const settings = await this.getSettings()
205
- const auth = (settings as { auth?: { type?: string } }).auth
206
- return !!auth?.type && auth.type !== "None"
207
- } catch {
208
- return false
209
- }
210
- }
211
-
212
- /**
213
- * Configure General Settings
214
- */
215
- async configureGeneralSettings(): Promise<boolean> {
216
- try {
217
- await this.postForm("/system/settings", {
218
- "settings-subtitles-use_embedded_subtitles": "true",
219
- "settings-subtitles-autosearch": "true",
220
- "settings-subtitles-path_mapping": "",
221
- })
222
- return true
223
- } catch {
224
- return false
225
- }
226
- }
227
-
228
- /**
229
- * Get all language profiles
230
- */
231
- async getLanguageProfiles(): Promise<BazarrLanguageProfile[]> {
232
- return this.getJson<BazarrLanguageProfile[]>("/system/languages/profiles")
233
- }
234
-
235
- /**
236
- * Configure Default Language Profile
237
- */
238
- async configureDefaultLanguageProfile(name = "English", language = "en"): Promise<boolean> {
239
- try {
240
- const profiles = (await this.getLanguageProfiles()) || []
241
- const existing = profiles.find((p) => p.name === name)
242
-
243
- if (existing) {
244
- return true
245
- }
246
-
247
- const newProfile: BazarrLanguageProfile = {
248
- name,
249
- cutoff: language,
250
- languages: [{ code: language, forced: false, hi: false }],
251
- }
252
-
253
- profiles.push(newProfile)
254
- await this.postForm("/system/settings", {
255
- languages_profiles: JSON.stringify(profiles),
256
- })
257
- return true
258
- } catch {
259
- return false
260
- }
261
- }
262
-
263
- /**
264
- * Run the auto-setup process
265
- */
266
- async setup(options: AutoSetupOptions): Promise<AutoSetupResult> {
267
- const { username, password } = options
268
-
269
- try {
270
- const healthy = await this.isHealthy()
271
- if (!healthy) {
272
- return { success: false, message: "Bazarr not reachable" }
273
- }
274
-
275
- const apiKey = await this.getApiKey()
276
- if (apiKey) {
277
- this.setApiKey(apiKey)
278
- }
279
-
280
- const initialized = await this.isInitialized()
281
- let authConfigured = false
282
-
283
- if (!initialized) {
284
- authConfigured = await this.enableFormAuth(username, password)
285
- }
286
-
287
- return {
288
- success: true,
289
- message: initialized ? "Already configured" : authConfigured ? "Auth enabled" : "Ready",
290
- data: { apiKey, authConfigured },
291
- envUpdates: apiKey ? { API_KEY_BAZARR: apiKey } : undefined,
292
- }
293
- } catch (error) {
294
- return { success: false, message: `${error}` }
295
- }
296
- }
297
- }