@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,461 +0,0 @@
1
- /**
2
- * Maintainerr API Client
3
- * Handles Maintainerr integration for automated media management
4
- * API docs: https://github.com/Maintainerr/Maintainerr
5
- */
6
-
7
- import type { AutoSetupOptions, AutoSetupResult, IAutoSetupClient } from "./auto-setup-types"
8
- import { BaseApiClient } from "./base-api"
9
-
10
- // === Response Types ===
11
-
12
- export interface MaintainerrVersionInfo {
13
- version: string
14
- commitTag?: string
15
- }
16
-
17
- export interface MaintainerrCollection {
18
- id: number
19
- libraryId: number
20
- title: string
21
- description?: string
22
- isActive: boolean
23
- arrAction: number
24
- visibleOnHome?: boolean
25
- deleteAfterDays?: number
26
- }
27
-
28
- export interface MaintainerrRuleGroup {
29
- id: number
30
- libraryId: number
31
- name: string
32
- description?: string
33
- isActive: boolean
34
- arrAction: number
35
- useRules: boolean
36
- dataType: number
37
- rules?: MaintainerrRule[]
38
- }
39
-
40
- export interface MaintainerrRule {
41
- operator: number | null
42
- action: number
43
- firstVal: number[]
44
- lastVal?: number[]
45
- customVal?: { ruleTypeId: number; value: string }
46
- section: number
47
- }
48
-
49
- export interface MaintainerrTaskStatus {
50
- time?: string
51
- running: boolean
52
- runningSince?: string
53
- }
54
-
55
- export interface MaintainerrSettings {
56
- plex_name?: string
57
- plex_hostname?: string
58
- plex_port?: number
59
- plex_ssl?: boolean
60
- plex_auth_token?: string
61
- radarr_url?: string
62
- radarr_api_key?: string
63
- sonarr_url?: string
64
- sonarr_api_key?: string
65
- overseerr_url?: string
66
- overseerr_api_key?: string
67
- tautulli_url?: string
68
- tautulli_api_key?: string
69
- collection_handler_job_cron?: string
70
- rules_handler_job_cron?: string
71
- }
72
-
73
- export interface MaintainerrSettingsUpdate {
74
- plex_name?: string
75
- plex_hostname?: string
76
- plex_port?: number
77
- plex_ssl?: boolean
78
- plex_auth_token?: string
79
- overseerr_url?: string
80
- overseerr_api_key?: string
81
- tautulli_url?: string
82
- tautulli_api_key?: string
83
- collection_handler_job_cron?: string
84
- rules_handler_job_cron?: string
85
- }
86
-
87
- export interface MaintainerrLibrary {
88
- key: string
89
- title: string
90
- type: "movie" | "show"
91
- }
92
-
93
- export interface PlexServerConnection {
94
- protocol: string
95
- address: string
96
- port: number
97
- uri: string
98
- local: boolean
99
- }
100
-
101
- export interface PlexServerDevice {
102
- name: string
103
- product: string
104
- productVersion: string
105
- platform: string
106
- clientIdentifier: string
107
- owned: boolean
108
- accessToken: string
109
- connection: PlexServerConnection[]
110
- }
111
-
112
- // === API Client ===
113
-
114
- export class MaintainerrClient extends BaseApiClient implements IAutoSetupClient {
115
- protected readonly logPrefix = "MaintainerrApi"
116
-
117
- constructor(host: string, port: number = 6246) {
118
- super(host, port)
119
- }
120
-
121
- private buildApiUrl(endpoint: string): string {
122
- return `${this.baseUrl}/api${endpoint}`
123
- }
124
-
125
- // === Health & Status ===
126
-
127
- async isHealthy(): Promise<boolean> {
128
- try {
129
- const response = await fetch(this.buildApiUrl("/settings/version"), { method: "GET" })
130
- return response.ok
131
- } catch {
132
- return false
133
- }
134
- }
135
-
136
- async isInitialized(): Promise<boolean> {
137
- try {
138
- const response = await fetch(this.buildApiUrl("/settings/test/setup"), { method: "GET" })
139
- return response.ok
140
- } catch {
141
- return false
142
- }
143
- }
144
-
145
- async getVersion(): Promise<MaintainerrVersionInfo | null> {
146
- try {
147
- const response = await fetch(this.buildApiUrl("/settings/version"), { method: "GET" })
148
- if (response.ok) {
149
- const versionText = await response.text()
150
- return { version: versionText.trim() }
151
- }
152
- } catch {
153
- // Ignore
154
- }
155
- return null
156
- }
157
-
158
- // === Collections API ===
159
-
160
- async getCollections(libraryId?: number): Promise<MaintainerrCollection[]> {
161
- try {
162
- const url = libraryId
163
- ? this.buildApiUrl(`/collections?libraryId=${libraryId}`)
164
- : this.buildApiUrl("/collections")
165
- const response = await fetch(url, { method: "GET" })
166
- if (response.ok) {
167
- return (await response.json()) as MaintainerrCollection[]
168
- }
169
- } catch {
170
- // Ignore
171
- }
172
- return []
173
- }
174
-
175
- async getCollection(id: number): Promise<MaintainerrCollection | null> {
176
- try {
177
- const response = await fetch(this.buildApiUrl(`/collections/collection/${id}`), {
178
- method: "GET",
179
- })
180
- if (response.ok) {
181
- return (await response.json()) as MaintainerrCollection
182
- }
183
- } catch {
184
- // Ignore
185
- }
186
- return null
187
- }
188
-
189
- // === Rules API ===
190
-
191
- async getRules(activeOnly: boolean = false, libraryId?: number): Promise<MaintainerrRuleGroup[]> {
192
- try {
193
- const params = new URLSearchParams()
194
- if (activeOnly) params.set("activeOnly", "true")
195
- if (libraryId) params.set("libraryId", libraryId.toString())
196
- const queryString = params.toString()
197
- const url = queryString
198
- ? this.buildApiUrl(`/rules?${queryString}`)
199
- : this.buildApiUrl("/rules")
200
- const response = await fetch(url, { method: "GET" })
201
- if (response.ok) {
202
- return (await response.json()) as MaintainerrRuleGroup[]
203
- }
204
- } catch {
205
- // Ignore
206
- }
207
- return []
208
- }
209
-
210
- async executeRules(): Promise<boolean> {
211
- try {
212
- const response = await fetch(this.buildApiUrl("/rules/execute"), { method: "POST" })
213
- return response.ok
214
- } catch {
215
- return false
216
- }
217
- }
218
-
219
- // === Tasks API ===
220
-
221
- async getTaskStatus(
222
- taskName: "rule-executor" | "collection-handler"
223
- ): Promise<MaintainerrTaskStatus | null> {
224
- try {
225
- const response = await fetch(this.buildApiUrl(`/tasks/${taskName}/status`), { method: "GET" })
226
- if (response.ok) {
227
- return (await response.json()) as MaintainerrTaskStatus
228
- }
229
- } catch {
230
- // Ignore
231
- }
232
- return null
233
- }
234
-
235
- // === Plex API ===
236
-
237
- async getPlexLibraries(): Promise<MaintainerrLibrary[]> {
238
- try {
239
- const response = await fetch(this.buildApiUrl("/plex/libraries"), { method: "GET" })
240
- if (response.ok) {
241
- return (await response.json()) as MaintainerrLibrary[]
242
- }
243
- } catch {
244
- // Ignore
245
- }
246
- return []
247
- }
248
-
249
- async testPlexConnection(): Promise<boolean> {
250
- try {
251
- const response = await fetch(this.buildApiUrl("/settings/test/plex"), { method: "GET" })
252
- return response.ok
253
- } catch {
254
- return false
255
- }
256
- }
257
-
258
- // === Settings API ===
259
-
260
- async getSettings(): Promise<MaintainerrSettings | null> {
261
- try {
262
- const response = await fetch(this.buildApiUrl("/settings"), { method: "GET" })
263
- if (response.ok) {
264
- return (await response.json()) as MaintainerrSettings
265
- }
266
- } catch {
267
- // Ignore
268
- }
269
- return null
270
- }
271
-
272
- async updateSettings(settings: MaintainerrSettingsUpdate): Promise<boolean> {
273
- try {
274
- const currentSettings = await this.getSettings()
275
- if (!currentSettings) return false
276
-
277
- const mergedSettings = { ...currentSettings, ...settings }
278
- const response = await fetch(this.buildApiUrl("/settings"), {
279
- method: "POST",
280
- headers: { "Content-Type": "application/json" },
281
- body: JSON.stringify(mergedSettings),
282
- })
283
- return response.ok
284
- } catch {
285
- return false
286
- }
287
- }
288
-
289
- async configureRadarr(host: string, port: number, apiKey: string): Promise<boolean> {
290
- try {
291
- const response = await fetch(this.buildApiUrl("/settings/radarr"), {
292
- method: "POST",
293
- headers: { "Content-Type": "application/json" },
294
- body: JSON.stringify({
295
- url: `http://${host}:${port}`,
296
- apiKey,
297
- serverName: host,
298
- }),
299
- })
300
- if (response.ok) {
301
- const result = await response.json()
302
- return result?.status === "OK"
303
- }
304
- return false
305
- } catch {
306
- return false
307
- }
308
- }
309
-
310
- async configureSonarr(host: string, port: number, apiKey: string): Promise<boolean> {
311
- try {
312
- const response = await fetch(this.buildApiUrl("/settings/sonarr"), {
313
- method: "POST",
314
- headers: { "Content-Type": "application/json" },
315
- body: JSON.stringify({
316
- url: `http://${host}:${port}`,
317
- apiKey,
318
- serverName: host,
319
- }),
320
- })
321
- if (response.ok) {
322
- const result = await response.json()
323
- return result?.status === "OK"
324
- }
325
- return false
326
- } catch {
327
- return false
328
- }
329
- }
330
-
331
- async configureOverseerr(host: string, port: number, apiKey: string): Promise<boolean> {
332
- return this.updateSettings({
333
- overseerr_url: `http://${host}:${port}`,
334
- overseerr_api_key: apiKey,
335
- })
336
- }
337
-
338
- async configureTautulli(host: string, port: number, apiKey: string): Promise<boolean> {
339
- return this.updateSettings({
340
- tautulli_url: `http://${host}:${port}`,
341
- tautulli_api_key: apiKey,
342
- })
343
- }
344
-
345
- async generateApiKey(): Promise<string | null> {
346
- try {
347
- const response = await fetch(this.buildApiUrl("/settings/api/generate"), { method: "GET" })
348
- if (response.ok) {
349
- const apiKey = await response.text()
350
- return apiKey.trim() || null
351
- }
352
- } catch {
353
- // Ignore
354
- }
355
- return null
356
- }
357
-
358
- async getPlexServers(): Promise<PlexServerDevice[] | null> {
359
- try {
360
- const response = await fetch(this.buildApiUrl("/settings/plex/devices/servers"), {
361
- method: "GET",
362
- })
363
- if (response.ok) {
364
- return (await response.json()) as PlexServerDevice[]
365
- }
366
- } catch {
367
- // Ignore
368
- }
369
- return null
370
- }
371
-
372
- async setPlexToken(token: string): Promise<boolean> {
373
- try {
374
- const response = await fetch(this.buildApiUrl("/settings/plex/token"), {
375
- method: "POST",
376
- headers: { "Content-Type": "application/json" },
377
- body: JSON.stringify({ plex_auth_token: token }),
378
- })
379
- return response.ok
380
- } catch {
381
- return false
382
- }
383
- }
384
-
385
- async configurePlexServer(
386
- hostname: string,
387
- port: number,
388
- name: string,
389
- ssl: boolean = false
390
- ): Promise<boolean> {
391
- return this.updateSettings({
392
- plex_hostname: hostname,
393
- plex_port: port,
394
- plex_name: name,
395
- plex_ssl: ssl,
396
- })
397
- }
398
-
399
- // === Auto-Setup ===
400
-
401
- async setup(options: AutoSetupOptions): Promise<AutoSetupResult> {
402
- try {
403
- const healthy = await this.isHealthy()
404
- if (!healthy) {
405
- return { success: false, message: "Maintainerr not reachable" }
406
- }
407
-
408
- const version = await this.getVersion()
409
- if (!version) {
410
- return { success: false, message: "Failed to get Maintainerr version" }
411
- }
412
-
413
- const apiKey = await this.generateApiKey()
414
- const envUpdates: Record<string, string> = {}
415
- if (apiKey) {
416
- envUpdates["API_KEY_MAINTAINERR"] = apiKey
417
- }
418
-
419
- const plexToken = options.plexToken || options.env["API_KEY_PLEX"]
420
- let plexConnected = false
421
- if (plexToken) {
422
- const plexSet = await this.setPlexToken(plexToken)
423
- if (plexSet) {
424
- const servers = await this.getPlexServers()
425
- if (servers && servers.length > 0) {
426
- const server = servers[0]
427
- const localConn = server.connection.find(
428
- (c) => c.local && c.address.startsWith("192.168")
429
- )
430
- const conn = localConn || server.connection[0]
431
-
432
- if (conn) {
433
- await this.configurePlexServer(
434
- conn.address,
435
- conn.port,
436
- server.name,
437
- conn.protocol === "https"
438
- )
439
- }
440
- }
441
- plexConnected = await this.testPlexConnection()
442
- }
443
- }
444
-
445
- return {
446
- success: true,
447
- message: plexConnected
448
- ? `Maintainerr v${version.version} connected to Plex`
449
- : `Maintainerr v${version.version} ready`,
450
- envUpdates: Object.keys(envUpdates).length > 0 ? envUpdates : undefined,
451
- data: {
452
- version: version.version,
453
- apiKey,
454
- requiresWizard: !plexConnected,
455
- },
456
- }
457
- } catch (error) {
458
- return { success: false, message: `${error}` }
459
- }
460
- }
461
- }
@@ -1,101 +0,0 @@
1
- export interface RadarrNamingConfig {
2
- renameMovies: boolean
3
- replaceIllegalCharacters: boolean
4
- colonReplacementFormat: "dash" | "spaceDash" | "spaceDashSpace" | "smart" | "delete" | number
5
- standardMovieFormat: string
6
- movieFolderFormat: string
7
- includeQuality: boolean
8
- replaceSpaces: boolean
9
- }
10
-
11
- export interface SonarrNamingConfig {
12
- renameEpisodes: boolean
13
- replaceIllegalCharacters: boolean
14
- colonReplacementFormat: "dash" | "spaceDash" | "spaceDashSpace" | "smart" | "delete" | number
15
- multiEpisodeStyle:
16
- | "extend"
17
- | "duplicate"
18
- | "repeat"
19
- | "scene"
20
- | "range"
21
- | "prefixedRange"
22
- | number
23
- dailyEpisodeFormat: string
24
- animeEpisodeFormat: string
25
- seriesFolderFormat: string
26
- seasonFolderFormat: string
27
- standardEpisodeFormat: string
28
- includeSeriesTitle: boolean
29
- includeEpisodeTitle: boolean
30
- includeQuality: boolean
31
- replaceSpaces: boolean
32
- separator: string
33
- numberStyle: string
34
- }
35
-
36
- export interface LidarrNamingConfig {
37
- renameTracks: boolean
38
- replaceIllegalCharacters: boolean
39
- colonReplacementFormat: "dash" | "spaceDash" | "spaceDashSpace" | "smart" | "delete" | number
40
- standardTrackFormat: string
41
- multiDiscTrackFormat: string
42
- artistFolderFormat: string
43
- albumFolderFormat: string
44
- }
45
-
46
- export type NamingConfig = RadarrNamingConfig | SonarrNamingConfig | LidarrNamingConfig
47
-
48
- // TRaSH Guides Recommended Naming Schemes
49
- // Source: https://trash-guides.info/Radarr/Radarr-recommended-naming-scheme/
50
- // Source: https://trash-guides.info/Sonarr/Sonarr-recommended-naming-scheme/
51
- // Lidarr: https://wiki.servarr.com/lidarr/settings#media-management
52
-
53
- export const TRASH_NAMING_CONFIG = {
54
- radarr: {
55
- renameMovies: true,
56
- replaceIllegalCharacters: true,
57
- colonReplacementFormat: "dash",
58
- standardMovieFormat:
59
- "{Movie CleanTitle} ({Release Year}) {edition-{Edition Tags}} {[Custom Formats]}{[Quality Full]}{[MediaInfo 3D]}{[Mediainfo AudioCodec}{ Mediainfo AudioChannels]}{[MediaInfo VideoDynamicRangeType]}{[Mediainfo VideoCodec]}{-Release Group}",
60
- movieFolderFormat: "{Movie CleanTitle} ({Release Year})",
61
- includeQuality: true,
62
- replaceSpaces: false,
63
- } as RadarrNamingConfig,
64
-
65
- sonarr: {
66
- renameEpisodes: true,
67
- replaceIllegalCharacters: true,
68
- colonReplacementFormat: 1, // 1 = Dash
69
- multiEpisodeStyle: 5, // 5 = Prefixed Range
70
- standardEpisodeFormat:
71
- "{Series TitleYear} - S{season:00}E{episode:00} - {Episode CleanTitle} {[Custom Formats]}{[Quality Full]}{[Mediainfo AudioCodec}{ Mediainfo AudioChannels]}{[MediaInfo VideoDynamicRangeType]}{[Mediainfo VideoCodec]}{-Release Group}",
72
- dailyEpisodeFormat:
73
- "{Series TitleYear} - {Air-Date} - {Episode CleanTitle} {[Custom Formats]}{[Quality Full]}{[MediaInfo 3D]}{[Mediainfo AudioCodec}{ Mediainfo AudioChannels]}{[MediaInfo VideoDynamicRangeType]}{[Mediainfo VideoCodec]}{-Release Group}",
74
- animeEpisodeFormat:
75
- "{Series TitleYear} - S{season:00}E{episode:00} - {absolute:000} - {Episode CleanTitle} {[Custom Formats]}{[Quality Full]}{[MediaInfo 3D]}{[Mediainfo AudioCodec}{ Mediainfo AudioChannels]}{MediaInfo AudioLanguages}{[MediaInfo VideoDynamicRangeType]}[{Mediainfo VideoCodec }{MediaInfo VideoBitDepth}bit]{-Release Group}",
76
- seriesFolderFormat: "{Series TitleYear}",
77
- seasonFolderFormat: "Season {season:00}",
78
- includeSeriesTitle: true,
79
- includeEpisodeTitle: true,
80
- includeQuality: true,
81
- replaceSpaces: false,
82
- separator: " - ",
83
- numberStyle: "S{season:00}E{episode:00}",
84
- } as SonarrNamingConfig,
85
-
86
- lidarr: {
87
- renameTracks: true,
88
- replaceIllegalCharacters: true,
89
- colonReplacementFormat: 4, // 4 = Smart Replace (Dash or Space Dash depending on name)
90
- // Standard track format: Artist - Album (Year) - Track# - Title
91
- standardTrackFormat:
92
- "{Artist CleanName} - {Album CleanTitle} ({Release Year}) - {track:00} - {Track CleanTitle}",
93
- // Multi-disc format includes disc number
94
- multiDiscTrackFormat:
95
- "{Artist CleanName} - {Album CleanTitle} ({Release Year}) - {medium:00}-{track:00} - {Track CleanTitle}",
96
- // Artist folder: Artist Name
97
- artistFolderFormat: "{Artist CleanName}",
98
- // Album folder: Album Title (Year) [Quality]
99
- albumFolderFormat: "{Album CleanTitle} ({Release Year})",
100
- } as LidarrNamingConfig,
101
- }