qBitrr2 5.5.3__tar.gz → 5.5.6__tar.gz
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.
- {qbitrr2-5.5.3/qBitrr2.egg-info → qbitrr2-5.5.6}/PKG-INFO +2 -2
- {qbitrr2-5.5.3 → qbitrr2-5.5.6}/README.md +1 -1
- {qbitrr2-5.5.3 → qbitrr2-5.5.6}/pyproject.toml +1 -1
- {qbitrr2-5.5.3 → qbitrr2-5.5.6}/qBitrr/bundled_data.py +2 -2
- {qbitrr2-5.5.3 → qbitrr2-5.5.6}/qBitrr/db_lock.py +135 -3
- qbitrr2-5.5.6/qBitrr/static/assets/ArrView.js +2 -0
- qbitrr2-5.5.6/qBitrr/static/assets/ArrView.js.map +1 -0
- qbitrr2-5.5.6/qBitrr/static/assets/ConfigView.js +5 -0
- qbitrr2-5.5.6/qBitrr/static/assets/ConfigView.js.map +1 -0
- qbitrr2-5.5.6/qBitrr/static/assets/LogsView.js +208 -0
- qbitrr2-5.5.6/qBitrr/static/assets/LogsView.js.map +1 -0
- qbitrr2-5.5.6/qBitrr/static/assets/ProcessesView.js +2 -0
- qbitrr2-5.5.6/qBitrr/static/assets/ProcessesView.js.map +1 -0
- qbitrr2-5.5.6/qBitrr/static/assets/app.css +1 -0
- qbitrr2-5.5.6/qBitrr/static/assets/app.js +11 -0
- {qbitrr2-5.5.3 → qbitrr2-5.5.6}/qBitrr/static/assets/app.js.map +1 -1
- qbitrr2-5.5.6/qBitrr/static/assets/lidarr.svg +1 -0
- qbitrr2-5.5.6/qBitrr/static/assets/logo.svg +48 -0
- {qbitrr2-5.5.3 → qbitrr2-5.5.6}/qBitrr/static/assets/react-select.esm.js +4 -4
- {qbitrr2-5.5.3 → qbitrr2-5.5.6}/qBitrr/static/assets/react-select.esm.js.map +1 -1
- {qbitrr2-5.5.3 → qbitrr2-5.5.6}/qBitrr/static/assets/table.js +1 -1
- qbitrr2-5.5.6/qBitrr/static/index.html +47 -0
- qbitrr2-5.5.6/qBitrr/static/logov2-clean.svg +48 -0
- {qbitrr2-5.5.3 → qbitrr2-5.5.6}/qBitrr/static/manifest.json +1 -1
- {qbitrr2-5.5.3 → qbitrr2-5.5.6}/qBitrr/static/sw.js +29 -6
- {qbitrr2-5.5.3 → qbitrr2-5.5.6/qBitrr2.egg-info}/PKG-INFO +2 -2
- {qbitrr2-5.5.3 → qbitrr2-5.5.6}/qBitrr2.egg-info/SOURCES.txt +3 -0
- {qbitrr2-5.5.3 → qbitrr2-5.5.6}/setup.cfg +1 -1
- qbitrr2-5.5.3/qBitrr/static/assets/ArrView.js +0 -2
- qbitrr2-5.5.3/qBitrr/static/assets/ArrView.js.map +0 -1
- qbitrr2-5.5.3/qBitrr/static/assets/ConfigView.js +0 -4
- qbitrr2-5.5.3/qBitrr/static/assets/ConfigView.js.map +0 -1
- qbitrr2-5.5.3/qBitrr/static/assets/LogsView.js +0 -2
- qbitrr2-5.5.3/qBitrr/static/assets/LogsView.js.map +0 -1
- qbitrr2-5.5.3/qBitrr/static/assets/ProcessesView.js +0 -2
- qbitrr2-5.5.3/qBitrr/static/assets/ProcessesView.js.map +0 -1
- qbitrr2-5.5.3/qBitrr/static/assets/app.css +0 -1
- qbitrr2-5.5.3/qBitrr/static/assets/app.js +0 -11
- qbitrr2-5.5.3/qBitrr/static/index.html +0 -33
- {qbitrr2-5.5.3 → qbitrr2-5.5.6}/LICENSE +0 -0
- {qbitrr2-5.5.3 → qbitrr2-5.5.6}/MANIFEST.in +0 -0
- {qbitrr2-5.5.3 → qbitrr2-5.5.6}/config.example.toml +0 -0
- {qbitrr2-5.5.3 → qbitrr2-5.5.6}/qBitrr/__init__.py +0 -0
- {qbitrr2-5.5.3 → qbitrr2-5.5.6}/qBitrr/arss.py +0 -0
- {qbitrr2-5.5.3 → qbitrr2-5.5.6}/qBitrr/auto_update.py +0 -0
- {qbitrr2-5.5.3 → qbitrr2-5.5.6}/qBitrr/config.py +0 -0
- {qbitrr2-5.5.3 → qbitrr2-5.5.6}/qBitrr/config_version.py +0 -0
- {qbitrr2-5.5.3 → qbitrr2-5.5.6}/qBitrr/db_recovery.py +0 -0
- {qbitrr2-5.5.3 → qbitrr2-5.5.6}/qBitrr/env_config.py +0 -0
- {qbitrr2-5.5.3 → qbitrr2-5.5.6}/qBitrr/errors.py +0 -0
- {qbitrr2-5.5.3 → qbitrr2-5.5.6}/qBitrr/ffprobe.py +0 -0
- {qbitrr2-5.5.3 → qbitrr2-5.5.6}/qBitrr/gen_config.py +0 -0
- {qbitrr2-5.5.3 → qbitrr2-5.5.6}/qBitrr/home_path.py +0 -0
- {qbitrr2-5.5.3 → qbitrr2-5.5.6}/qBitrr/logger.py +0 -0
- {qbitrr2-5.5.3 → qbitrr2-5.5.6}/qBitrr/main.py +0 -0
- {qbitrr2-5.5.3 → qbitrr2-5.5.6}/qBitrr/search_activity_store.py +0 -0
- {qbitrr2-5.5.3 → qbitrr2-5.5.6}/qBitrr/static/assets/build.svg +0 -0
- {qbitrr2-5.5.3 → qbitrr2-5.5.6}/qBitrr/static/assets/check-mark.svg +0 -0
- {qbitrr2-5.5.3 → qbitrr2-5.5.6}/qBitrr/static/assets/close.svg +0 -0
- {qbitrr2-5.5.3 → qbitrr2-5.5.6}/qBitrr/static/assets/download.svg +0 -0
- {qbitrr2-5.5.3 → qbitrr2-5.5.6}/qBitrr/static/assets/gear.svg +0 -0
- {qbitrr2-5.5.3 → qbitrr2-5.5.6}/qBitrr/static/assets/live-streaming.svg +0 -0
- {qbitrr2-5.5.3 → qbitrr2-5.5.6}/qBitrr/static/assets/log.svg +0 -0
- {qbitrr2-5.5.3 → qbitrr2-5.5.6}/qBitrr/static/assets/plus.svg +0 -0
- {qbitrr2-5.5.3 → qbitrr2-5.5.6}/qBitrr/static/assets/process.svg +0 -0
- {qbitrr2-5.5.3 → qbitrr2-5.5.6}/qBitrr/static/assets/refresh-arrow.svg +0 -0
- {qbitrr2-5.5.3 → qbitrr2-5.5.6}/qBitrr/static/assets/table.js.map +0 -0
- {qbitrr2-5.5.3 → qbitrr2-5.5.6}/qBitrr/static/assets/trash.svg +0 -0
- {qbitrr2-5.5.3 → qbitrr2-5.5.6}/qBitrr/static/assets/up-arrow.svg +0 -0
- {qbitrr2-5.5.3 → qbitrr2-5.5.6}/qBitrr/static/assets/useInterval.js +0 -0
- {qbitrr2-5.5.3 → qbitrr2-5.5.6}/qBitrr/static/assets/useInterval.js.map +0 -0
- {qbitrr2-5.5.3 → qbitrr2-5.5.6}/qBitrr/static/assets/vendor.js +0 -0
- {qbitrr2-5.5.3 → qbitrr2-5.5.6}/qBitrr/static/assets/vendor.js.map +0 -0
- {qbitrr2-5.5.3 → qbitrr2-5.5.6}/qBitrr/static/assets/visibility.svg +0 -0
- {qbitrr2-5.5.3 → qbitrr2-5.5.6}/qBitrr/static/vite.svg +0 -0
- {qbitrr2-5.5.3 → qbitrr2-5.5.6}/qBitrr/tables.py +0 -0
- {qbitrr2-5.5.3 → qbitrr2-5.5.6}/qBitrr/utils.py +0 -0
- {qbitrr2-5.5.3 → qbitrr2-5.5.6}/qBitrr/versioning.py +0 -0
- {qbitrr2-5.5.3 → qbitrr2-5.5.6}/qBitrr/webui.py +0 -0
- {qbitrr2-5.5.3 → qbitrr2-5.5.6}/qBitrr2.egg-info/dependency_links.txt +0 -0
- {qbitrr2-5.5.3 → qbitrr2-5.5.6}/qBitrr2.egg-info/entry_points.txt +0 -0
- {qbitrr2-5.5.3 → qbitrr2-5.5.6}/qBitrr2.egg-info/requires.txt +0 -0
- {qbitrr2-5.5.3 → qbitrr2-5.5.6}/qBitrr2.egg-info/top_level.txt +0 -0
- {qbitrr2-5.5.3 → qbitrr2-5.5.6}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: qBitrr2
|
|
3
|
-
Version: 5.5.
|
|
3
|
+
Version: 5.5.6
|
|
4
4
|
Summary: Intelligent automation for qBittorrent and *Arr apps (Radarr/Sonarr/Lidarr) - health monitoring, instant imports, quality upgrades, request integration
|
|
5
5
|
Home-page: https://github.com/Feramance/qBitrr
|
|
6
6
|
Author: Feramance
|
|
@@ -83,7 +83,7 @@ Requires-Dist: upgrade-pip==0.1.4; extra == "all"
|
|
|
83
83
|
Requires-Dist: ujson==5.10.0; extra == "all"
|
|
84
84
|
Dynamic: license-file
|
|
85
85
|
|
|
86
|
-
# qBitrr
|
|
86
|
+
# <img src="assets/logov2-clean.png" alt="qBitrr Logo" width="40" style="vertical-align: middle;"/> qBitrr
|
|
87
87
|
|
|
88
88
|
[](https://pypi.org/project/qBitrr2/)
|
|
89
89
|
[](https://pypi.org/project/qBitrr2/)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# qBitrr
|
|
1
|
+
# <img src="assets/logov2-clean.png" alt="qBitrr Logo" width="40" style="vertical-align: middle;"/> qBitrr
|
|
2
2
|
|
|
3
3
|
[](https://pypi.org/project/qBitrr2/)
|
|
4
4
|
[](https://pypi.org/project/qBitrr2/)
|
|
@@ -28,7 +28,7 @@ target-version = ['py311']
|
|
|
28
28
|
|
|
29
29
|
[tool.poetry]
|
|
30
30
|
name = "pypi-public"
|
|
31
|
-
version = "5.5.
|
|
31
|
+
version = "5.5.6"
|
|
32
32
|
description = "Intelligent automation for qBittorrent and *Arr apps (Radarr/Sonarr/Lidarr) - health monitoring, instant imports, quality upgrades, request integration"
|
|
33
33
|
authors = ["Drapersniper", "Feramance"]
|
|
34
34
|
readme = "README.md"
|
|
@@ -6,6 +6,7 @@ from contextlib import contextmanager
|
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
from typing import Iterator
|
|
8
8
|
|
|
9
|
+
from qBitrr.db_recovery import checkpoint_wal, repair_database
|
|
9
10
|
from qBitrr.home_path import APPDATA_FOLDER
|
|
10
11
|
|
|
11
12
|
if os.name == "nt": # pragma: no cover - platform specific
|
|
@@ -99,6 +100,8 @@ def with_database_retry(
|
|
|
99
100
|
- sqlite3.IntegrityError (data constraint violations)
|
|
100
101
|
- sqlite3.ProgrammingError (SQL syntax errors)
|
|
101
102
|
|
|
103
|
+
On detecting database corruption, attempts automatic recovery before retrying.
|
|
104
|
+
|
|
102
105
|
Args:
|
|
103
106
|
func: Callable to execute (should take no arguments)
|
|
104
107
|
retries: Maximum number of retry attempts (default: 5)
|
|
@@ -118,6 +121,8 @@ def with_database_retry(
|
|
|
118
121
|
import time
|
|
119
122
|
|
|
120
123
|
attempt = 0
|
|
124
|
+
corruption_recovery_attempted = False
|
|
125
|
+
|
|
121
126
|
while True:
|
|
122
127
|
try:
|
|
123
128
|
return func()
|
|
@@ -128,6 +133,61 @@ def with_database_retry(
|
|
|
128
133
|
if "syntax" in error_msg or "constraint" in error_msg:
|
|
129
134
|
raise
|
|
130
135
|
|
|
136
|
+
# Detect corruption and attempt recovery (only once)
|
|
137
|
+
if not corruption_recovery_attempted and (
|
|
138
|
+
"disk image is malformed" in error_msg
|
|
139
|
+
or "database disk image is malformed" in error_msg
|
|
140
|
+
or "database corruption" in error_msg
|
|
141
|
+
):
|
|
142
|
+
corruption_recovery_attempted = True
|
|
143
|
+
if logger:
|
|
144
|
+
logger.error(
|
|
145
|
+
"Database corruption detected: %s. Attempting automatic recovery...",
|
|
146
|
+
e,
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
recovery_succeeded = False
|
|
150
|
+
try:
|
|
151
|
+
db_path = APPDATA_FOLDER / "qbitrr.db"
|
|
152
|
+
|
|
153
|
+
# Step 1: Try WAL checkpoint (least invasive)
|
|
154
|
+
if logger:
|
|
155
|
+
logger.info("Attempting WAL checkpoint...")
|
|
156
|
+
if checkpoint_wal(db_path, logger):
|
|
157
|
+
if logger:
|
|
158
|
+
logger.info("WAL checkpoint successful - retrying operation")
|
|
159
|
+
recovery_succeeded = True
|
|
160
|
+
else:
|
|
161
|
+
# Step 2: Try full repair (more invasive)
|
|
162
|
+
if logger:
|
|
163
|
+
logger.warning(
|
|
164
|
+
"WAL checkpoint failed - attempting full database repair..."
|
|
165
|
+
)
|
|
166
|
+
if repair_database(db_path, backup=True, logger_override=logger):
|
|
167
|
+
if logger:
|
|
168
|
+
logger.info("Database repair successful - retrying operation")
|
|
169
|
+
recovery_succeeded = True
|
|
170
|
+
|
|
171
|
+
except Exception as recovery_error:
|
|
172
|
+
if logger:
|
|
173
|
+
logger.error(
|
|
174
|
+
"Database recovery error: %s",
|
|
175
|
+
recovery_error,
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
if recovery_succeeded:
|
|
179
|
+
# Reset attempt counter after successful recovery
|
|
180
|
+
attempt = 0
|
|
181
|
+
time.sleep(1) # Brief pause before retry
|
|
182
|
+
continue
|
|
183
|
+
|
|
184
|
+
# If we reach here, recovery failed - log and continue with normal retry
|
|
185
|
+
if logger:
|
|
186
|
+
logger.critical(
|
|
187
|
+
"Automatic database recovery failed. "
|
|
188
|
+
"Manual intervention may be required. Attempting normal retry..."
|
|
189
|
+
)
|
|
190
|
+
|
|
131
191
|
attempt += 1
|
|
132
192
|
if attempt >= retries:
|
|
133
193
|
if logger:
|
|
@@ -161,16 +221,18 @@ class ResilientSqliteDatabase:
|
|
|
161
221
|
(specifically when setting PRAGMAs), before query-level retry logic can help.
|
|
162
222
|
"""
|
|
163
223
|
|
|
164
|
-
def __init__(self, database, max_retries=5, backoff=0.5):
|
|
224
|
+
def __init__(self, database, max_retries=5, backoff=0.5, logger=None):
|
|
165
225
|
"""
|
|
166
226
|
Args:
|
|
167
227
|
database: Peewee SqliteDatabase instance to wrap
|
|
168
228
|
max_retries: Maximum connection retry attempts
|
|
169
229
|
backoff: Initial backoff delay in seconds
|
|
230
|
+
logger: Optional logger instance for logging recovery attempts
|
|
170
231
|
"""
|
|
171
232
|
self._db = database
|
|
172
233
|
self._max_retries = max_retries
|
|
173
234
|
self._backoff = backoff
|
|
235
|
+
self._logger = logger
|
|
174
236
|
|
|
175
237
|
def __getattr__(self, name):
|
|
176
238
|
"""Delegate all attribute access to the wrapped database."""
|
|
@@ -194,6 +256,7 @@ class ResilientSqliteDatabase:
|
|
|
194
256
|
|
|
195
257
|
last_error = None
|
|
196
258
|
delay = self._backoff
|
|
259
|
+
corruption_recovery_attempted = False
|
|
197
260
|
|
|
198
261
|
for attempt in range(1, self._max_retries + 1):
|
|
199
262
|
try:
|
|
@@ -201,8 +264,77 @@ class ResilientSqliteDatabase:
|
|
|
201
264
|
except (OperationalError, DatabaseError, sqlite3.OperationalError) as e:
|
|
202
265
|
error_msg = str(e).lower()
|
|
203
266
|
|
|
204
|
-
#
|
|
205
|
-
if
|
|
267
|
+
# Detect corruption and attempt recovery (only once)
|
|
268
|
+
if not corruption_recovery_attempted and (
|
|
269
|
+
"disk image is malformed" in error_msg
|
|
270
|
+
or "database disk image is malformed" in error_msg
|
|
271
|
+
or "database corruption" in error_msg
|
|
272
|
+
):
|
|
273
|
+
corruption_recovery_attempted = True
|
|
274
|
+
if self._logger:
|
|
275
|
+
self._logger.error(
|
|
276
|
+
"Database corruption detected during connection: %s. "
|
|
277
|
+
"Attempting automatic recovery...",
|
|
278
|
+
e,
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
recovery_succeeded = False
|
|
282
|
+
try:
|
|
283
|
+
db_path = APPDATA_FOLDER / "qbitrr.db"
|
|
284
|
+
|
|
285
|
+
# Close current connection if any
|
|
286
|
+
try:
|
|
287
|
+
if not self._db.is_closed():
|
|
288
|
+
self._db.close()
|
|
289
|
+
except Exception:
|
|
290
|
+
pass # Ignore errors closing corrupted connection
|
|
291
|
+
|
|
292
|
+
# Step 1: Try WAL checkpoint
|
|
293
|
+
if self._logger:
|
|
294
|
+
self._logger.info("Attempting WAL checkpoint...")
|
|
295
|
+
if checkpoint_wal(db_path, self._logger):
|
|
296
|
+
if self._logger:
|
|
297
|
+
self._logger.info(
|
|
298
|
+
"WAL checkpoint successful - retrying connection"
|
|
299
|
+
)
|
|
300
|
+
recovery_succeeded = True
|
|
301
|
+
else:
|
|
302
|
+
# Step 2: Try full repair
|
|
303
|
+
if self._logger:
|
|
304
|
+
self._logger.warning(
|
|
305
|
+
"WAL checkpoint failed - attempting full database repair..."
|
|
306
|
+
)
|
|
307
|
+
if repair_database(db_path, backup=True, logger_override=self._logger):
|
|
308
|
+
if self._logger:
|
|
309
|
+
self._logger.info(
|
|
310
|
+
"Database repair successful - retrying connection"
|
|
311
|
+
)
|
|
312
|
+
recovery_succeeded = True
|
|
313
|
+
|
|
314
|
+
except Exception as recovery_error:
|
|
315
|
+
if self._logger:
|
|
316
|
+
self._logger.error(
|
|
317
|
+
"Database recovery error: %s",
|
|
318
|
+
recovery_error,
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
if recovery_succeeded:
|
|
322
|
+
time.sleep(1)
|
|
323
|
+
continue
|
|
324
|
+
|
|
325
|
+
# Recovery failed - log and continue with normal retry
|
|
326
|
+
if self._logger:
|
|
327
|
+
self._logger.critical(
|
|
328
|
+
"Automatic database recovery failed. "
|
|
329
|
+
"Manual intervention may be required."
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
# Retry on transient I/O errors
|
|
333
|
+
if (
|
|
334
|
+
"disk i/o error" in error_msg
|
|
335
|
+
or "database is locked" in error_msg
|
|
336
|
+
or "disk image is malformed" in error_msg
|
|
337
|
+
):
|
|
206
338
|
last_error = e
|
|
207
339
|
|
|
208
340
|
if attempt < self._max_retries:
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{j as e,u as is,h as rs,i as os,k as cs,l as Je,m as ds,I as qe,R as Ue,n as Xe,o as es}from"./app.js";import{r as a,u as ze,g as Te,f as Pe,a as gs,b as vs,c as ys}from"./table.js";import{u as Ke}from"./useInterval.js";import"./vendor.js";function Ss({data:r,columns:t,getRowKey:u}){const h=ze({data:r,columns:t,getCoreRowModel:Te()});return e.jsx("div",{className:"table-wrapper",children:e.jsxs("table",{className:"responsive-table",children:[e.jsx("thead",{children:h.getHeaderGroups().map(y=>e.jsx("tr",{children:y.headers.map(v=>e.jsx("th",{children:v.isPlaceholder?null:Pe(v.column.columnDef.header,v.getContext())},v.id))},y.id))}),e.jsx("tbody",{children:h.getRowModel().rows.map(y=>{const v=u?u(y.original):y.id;return e.jsx("tr",{children:y.getVisibleCells().map(F=>e.jsx("td",{"data-label":String(F.column.columnDef.header),children:Pe(F.column.columnDef.cell,F.getContext())},F.id))},v)})})]})})}const Rs=a.memo(Ss,(r,t)=>r.data===t.data&&r.columns===t.columns);function Cs(r){let t=2166136261;for(let u=0;u<r.length;u++)t^=r.charCodeAt(u),t+=(t<<1)+(t<<4)+(t<<7)+(t<<8)+(t<<24);return(t>>>0).toString(36)}function ls(r,t){const u=t.map(h=>{const y=r[h];return y==null?"":typeof y=="boolean"?y?"1":"0":typeof y=="object"?JSON.stringify(y):String(y)});return Cs(u.join("|"))}function ws(r,t,u,h){const y=[],v=[],F=[],N=new Set;for(const n of t){const M=u(n),b=ls(n,h);N.add(M);const B=r.byId.get(M),K=r.byHash.get(M);B?K!==b&&v.push(n):y.push(n)}for(const n of r.allIds)N.has(n)||F.push(n);const m=t.length-y.length-v.length,T=y.length>0||v.length>0||F.length>0;return{added:y,updated:v,removed:F,unchanged:m,hasChanges:T}}function Fs(r,t,u,h){if(!t.hasChanges)return r;const y=new Map(r.byId),v=new Map(r.byHash),F=[...r.allIds];for(const N of t.added){const m=u(N),T=ls(N,h);y.set(m,N),v.set(m,T),F.push(m)}for(const N of t.updated){const m=u(N),T=ls(N,h);y.set(m,N),v.set(m,T)}for(const N of t.removed){y.delete(N),v.delete(N);const m=F.indexOf(N);m!==-1&&F.splice(m,1)}return{byId:y,byHash:v,allIds:F,lastUpdate:Date.now()}}function ss(r){return r.allIds.map(t=>r.byId.get(t)).filter(Boolean)}function ms(){return{byId:new Map,byHash:new Map,allIds:[],lastUpdate:0}}function _e(r){const{getKey:t,hashFields:u}=r,h=a.useRef(ms()),[y,v]=a.useState(0),F=a.useCallback(T=>{const n=ws(h.current,T,t,u);if(!n.hasChanges)return{data:ss(h.current),hasChanges:!1,changes:null,lastUpdate:h.current.lastUpdate};const M=Fs(h.current,n,t,u);return h.current=M,v(M.lastUpdate),{data:ss(M),hasChanges:!0,changes:n,lastUpdate:M.lastUpdate}},[t,u]),N=a.useCallback(()=>ss(h.current),[]),m=a.useCallback(()=>{h.current=ms(),v(0)},[]);return{syncData:F,getData:N,reset:m,lastUpdate:y}}const as=50,Be=50,fs=500,Ms=a.memo(function({loading:t,rows:u,total:h,page:y,totalPages:v,onPageChange:F,onRefresh:N,lastUpdated:m,sort:T,onSort:n,summary:M,instanceCount:b,isAggFiltered:B=!1}){const K=a.useMemo(()=>[...b>1?[{accessorKey:"__instance",header:"Instance",size:150}]:[],{accessorKey:"title",header:"Title",cell:A=>A.getValue()},{accessorKey:"year",header:"Year",size:80},{accessorKey:"monitored",header:"Monitored",cell:A=>{const L=A.getValue();return e.jsx("span",{className:`track-status ${L?"available":"missing"}`,children:L?"✓":"✗"})},size:100},{accessorKey:"hasFile",header:"Has File",cell:A=>{const L=A.getValue();return e.jsx("span",{className:`track-status ${L?"available":"missing"}`,children:L?"✓":"✗"})},size:100},{accessorKey:"qualityProfileName",header:"Quality Profile",cell:A=>A.getValue()||"—",size:150},{accessorKey:"reason",header:"Reason",cell:A=>{const L=A.getValue();return L?e.jsx("span",{className:"table-badge table-badge-reason",children:L}):e.jsx("span",{className:"table-badge table-badge-reason",children:"Not being searched"})},size:120}],[b]);return e.jsxs("div",{className:"stack animate-fade-in",children:[e.jsxs("div",{className:"row",style:{justifyContent:"space-between"},children:[e.jsxs("div",{className:"hint",children:["Aggregated movies across all instances"," ",m?`(updated ${m})`:"",e.jsx("br",{}),e.jsx("strong",{children:"Available:"})," ",M.available.toLocaleString(void 0,{maximumFractionDigits:0})," •"," ",e.jsx("strong",{children:"Monitored:"})," ",M.monitored.toLocaleString(void 0,{maximumFractionDigits:0})," •"," ",e.jsx("strong",{children:"Missing:"})," ",M.missing.toLocaleString(void 0,{maximumFractionDigits:0})," •"," ",e.jsx("strong",{children:"Total:"})," ",M.total.toLocaleString(void 0,{maximumFractionDigits:0}),B&&h<M.total&&e.jsxs(e.Fragment,{children:[" ","• ",e.jsx("strong",{children:"Filtered:"})," ",h.toLocaleString(void 0,{maximumFractionDigits:0})," of"," ",M.total.toLocaleString(void 0,{maximumFractionDigits:0})]})]}),e.jsxs("button",{className:"btn ghost",onClick:N,disabled:t,children:[e.jsx(qe,{src:Ue}),"Refresh"]})]}),t?e.jsxs("div",{className:"loading",children:[e.jsx("span",{className:"spinner"})," Loading Radarr library…"]}):h?e.jsx(Rs,{data:u,columns:K,getRowKey:A=>`${A.__instance}-${A.title}-${A.year}`}):e.jsx("div",{className:"hint",children:"No movies found."}),h>0&&e.jsxs("div",{className:"pagination",children:[e.jsxs("div",{children:["Page ",y+1," of ",v," (",h.toLocaleString()," items · page size"," ",Be,")"]}),e.jsxs("div",{className:"inline",children:[e.jsx("button",{className:"btn",onClick:()=>F(Math.max(0,y-1)),disabled:y===0||t,children:"Prev"}),e.jsx("button",{className:"btn",onClick:()=>F(Math.min(v-1,y+1)),disabled:y>=v-1||t,children:"Next"})]})]})]})}),Ps=a.memo(function({loading:t,data:u,page:h,totalPages:y,pageSize:v,allMovies:F,onlyMissing:N,reasonFilter:m,onPageChange:T,onRefresh:n,onRestart:M,lastUpdated:b}){const B=a.useMemo(()=>{let g=F;return N&&(g=g.filter(j=>!j.hasFile)),g},[F,N]),K=a.useMemo(()=>m==="all"?B:m==="Not being searched"?B.filter(g=>g.reason==="Not being searched"||!g.reason):B.filter(g=>g.reason===m),[B,m]),A=a.useMemo(()=>F.length,[F]),L=m!=="all"||N,ge=K.length,J=a.useMemo(()=>[{accessorKey:"title",header:"Title",cell:g=>g.getValue()},{accessorKey:"year",header:"Year",size:80},{accessorKey:"monitored",header:"Monitored",cell:g=>{const j=g.getValue();return e.jsx("span",{className:`track-status ${j?"available":"missing"}`,children:j?"✓":"✗"})},size:100},{accessorKey:"hasFile",header:"Has File",cell:g=>{const j=g.getValue();return e.jsx("span",{className:`track-status ${j?"available":"missing"}`,children:j?"✓":"✗"})},size:100},{accessorKey:"qualityProfileName",header:"Quality Profile",cell:g=>g.getValue()||"—",size:150},{accessorKey:"reason",header:"Reason",cell:g=>{const j=g.getValue();return j?e.jsx("span",{className:"table-badge table-badge-reason",children:j}):e.jsx("span",{className:"table-badge table-badge-reason",children:"Not being searched"})},size:120}],[]),C=ze({data:K.slice(h*v,h*v+v),columns:J,getCoreRowModel:Te(),getSortedRowModel:gs()});return e.jsxs("div",{className:"stack animate-fade-in",children:[e.jsxs("div",{className:"row",style:{justifyContent:"space-between"},children:[e.jsxs("div",{className:"hint",children:[u?.counts?e.jsxs(e.Fragment,{children:[e.jsx("strong",{children:"Available:"})," ",(u.counts.available??0).toLocaleString(void 0,{maximumFractionDigits:0})," •"," ",e.jsx("strong",{children:"Monitored:"})," ",(u.counts.monitored??0).toLocaleString(void 0,{maximumFractionDigits:0})," •"," ",e.jsx("strong",{children:"Missing:"})," ",((u.counts.monitored??0)-(u.counts.available??0)).toLocaleString(void 0,{maximumFractionDigits:0})," •"," ",e.jsx("strong",{children:"Total:"})," ",A.toLocaleString(void 0,{maximumFractionDigits:0}),L&&ge<A&&e.jsxs(e.Fragment,{children:[" ","• ",e.jsx("strong",{children:"Filtered:"})," ",ge.toLocaleString(void 0,{maximumFractionDigits:0})," of"," ",A.toLocaleString(void 0,{maximumFractionDigits:0})]})]}):"Loading movie information...",b?` (updated ${b})`:""]}),e.jsxs("button",{className:"btn ghost",onClick:M,disabled:t,children:[e.jsx(qe,{src:Ue}),"Restart"]})]}),t?e.jsxs("div",{className:"loading",children:[e.jsx("span",{className:"spinner"})," Loading…"]}):F.length?e.jsx("div",{className:"table-wrapper",children:e.jsxs("table",{className:"responsive-table",children:[e.jsx("thead",{children:C.getHeaderGroups().map(g=>e.jsx("tr",{children:g.headers.map(j=>e.jsx("th",{children:j.isPlaceholder?null:Pe(j.column.columnDef.header,j.getContext())},j.id))},g.id))}),e.jsx("tbody",{children:C.getRowModel().rows.map(g=>{const j=g.original,q=`${j.title}-${j.year}`;return e.jsx("tr",{children:g.getVisibleCells().map(G=>e.jsx("td",{"data-label":String(G.column.columnDef.header),children:Pe(G.column.columnDef.cell,G.getContext())},G.id))},q)})})]})}):e.jsx("div",{className:"hint",children:"No movies found."}),K.length>v&&e.jsxs("div",{className:"pagination",children:[e.jsxs("div",{children:["Page ",h+1," of ",y," (",K.length.toLocaleString()," items · page size"," ",v,")"]}),e.jsxs("div",{className:"inline",children:[e.jsx("button",{className:"btn",onClick:()=>T(Math.max(0,h-1)),disabled:h===0||t,children:"Prev"}),e.jsx("button",{className:"btn",onClick:()=>T(Math.min(y-1,h+1)),disabled:h>=y-1||t,children:"Next"})]})]})]})});function As({active:r}){const{push:t}=is(),{value:u,setValue:h,register:y,clearHandler:v}=rs(),{liveArr:F,setLiveArr:N}=os(),[m,T]=a.useState([]),[n,M]=a.useState(""),[b,B]=a.useState(null),[K,A]=a.useState(0),[L,ge]=a.useState(""),[J,C]=a.useState(!1),[g,j]=a.useState(null),[q,G]=a.useState({}),[Q,le]=a.useState(as),[te,W]=a.useState(1),Z=a.useRef(""),l=a.useRef({}),S=a.useRef(u),D=a.useRef(!1),P=_e({getKey:s=>`${s.title}-${s.year}`,hashFields:["title","year","hasFile","monitored","reason"]}),[X,ie]=a.useState([]),[ee,f]=a.useState(!1),[i,x]=a.useState(0),[R,I]=a.useState(""),[$,U]=a.useState(null),he=_e({getKey:s=>`${s.__instance}-${s.title}-${s.year}`,hashFields:["__instance","title","year","hasFile","monitored","reason"]}),[re,we]=a.useState({key:"__instance",direction:"asc"}),[ye,Re]=a.useState(!1),[me,Y]=a.useState("all"),[fe,Ae]=a.useState({available:0,monitored:0,missing:0,total:0}),be=a.useCallback(async()=>{try{const s=await cs();s.ready===!1&&!D.current?(D.current=!0,t("Radarr backend is still initialising. Check the logs if this persists.","info")):s.ready&&(D.current=!0);const o=(s.arr||[]).filter(c=>c.type==="radarr");if(T(o),!o.length){M("aggregate"),B(null),ie([]),Ae({available:0,monitored:0,missing:0,total:0});return}n===""?M(o.length===1?o[0].category:"aggregate"):n!=="aggregate"&&!o.some(c=>c.category===n)&&M(o[0].category)}catch(s){t(s instanceof Error?s.message:"Unable to load Radarr instances","error")}},[t,n]),De=a.useCallback(async(s,o,c,E,_)=>{if(E.length)try{const O=[];for(const k of E){const z=await Je(s,k,c,o),H=z.page??k;if(O.push({page:H,movies:z.movies??[]}),Z.current!==_)return}if(Z.current!==_)return;G(k=>{const z={...k};let H=!1;for(const{page:oe,movies:w}of O){const se=P.syncData(w);se.hasChanges&&(z[oe]=se.data,H=!0)}return l.current=z,H?z:k})}catch(O){t(O instanceof Error?O.message:`Failed to load additional pages for ${s}`,"error")}},[t]),pe=a.useCallback(async(s,o,c,E={})=>{const _=E.preloadAll!==!1;(E.showLoading??!0)&&C(!0);try{const k=`${s}::${c}`,z=Z.current!==k;z&&(Z.current=k,G(()=>(l.current={},{})));const H=await Je(s,o,as,c);B(H);const oe=H.page??o;A(oe),ge(c);const w=H.page_size??as,se=H.total??(H.movies??[]).length,ue=Math.max(1,Math.ceil((se||0)/w));le(w),W(ue);const xe=H.movies??[],ae=z?{}:l.current,V=P.syncData(xe),de=V.hasChanges;if(z&&P.reset(),(z||de)&&(G(je=>{const Ee={...z?{}:je,[oe]:V.data};return l.current=Ee,Ee}),j(new Date().toLocaleTimeString())),_){const je=[];for(let ce=0;ce<ue;ce+=1)ce!==oe&&(ae[ce]||je.push(ce));De(s,c,w,je,k)}}catch(k){t(k instanceof Error?k.message:`Failed to load ${s} movies`,"error")}finally{C(!1)}},[t,De]),Se=a.useCallback(async s=>{if(!m.length){ie([]),Ae({available:0,monitored:0,missing:0,total:0});return}(s?.showLoading??!0)&&f(!0);try{const c=[];let E=0,_=0;for(const oe of m){let w=0,se=!1;const ue=oe.name||oe.category;for(;w<100;){const xe=await Je(oe.category,w,fs,"");if(!se){const V=xe.counts;V&&(E+=V.available??0,_+=V.monitored??0),se=!0}const ae=xe.movies??[];if(ae.forEach(V=>{c.push({...V,__instance:ue})}),!ae.length||ae.length<fs)break;w+=1}}const O=he.syncData(c),k=O.hasChanges;k&&ie(O.data);const z={available:E,monitored:_,missing:c.length-E,total:c.length},H=fe.available!==z.available||fe.monitored!==z.monitored||fe.missing!==z.missing||fe.total!==z.total;H&&Ae(z),R!==u&&(x(0),I(u)),(k||H)&&U(new Date().toLocaleTimeString())}catch(c){ie([]),Ae({available:0,monitored:0,missing:0,total:0}),t(c instanceof Error?c.message:"Failed to load aggregated Radarr data","error")}finally{f(!1)}},[m,u,t,R]);a.useEffect(()=>{r&&be()},[r,be]),a.useEffect(()=>{if(!r||!n||n==="aggregate")return;l.current={},G({}),W(1),A(0);const s=S.current;pe(n,0,s,{preloadAll:!0,showLoading:!0})},[r,n,pe]),a.useEffect(()=>{r&&n==="aggregate"&&Se()},[r,n,Se]),Ke(()=>{n==="aggregate"&&F&&Se({showLoading:!1})},n==="aggregate"&&F?1e3:null),a.useEffect(()=>{if(!r)return;const s=o=>{n==="aggregate"?(I(o),x(0)):n&&(A(0),pe(n,0,o,{preloadAll:!0,showLoading:!0}))};return y(s),()=>{v(s)}},[r,n,y,v,pe]),Ke(()=>{if(n&&n!=="aggregate"){if(S.current?.trim?.()||"")return;pe(n,K,L,{preloadAll:!1,showLoading:!1})}},r&&n&&n!=="aggregate"&&F?1e3:null),a.useEffect(()=>{S.current=u},[u]),a.useEffect(()=>{n==="aggregate"&&I(u)},[n,u]);const Ce=a.useMemo(()=>{let s=X;if(R){const o=R.toLowerCase();s=s.filter(c=>{const E=(c.title??"").toString().toLowerCase(),_=(c.__instance??"").toLowerCase();return E.includes(o)||_.includes(o)})}return ye&&(s=s.filter(o=>!o.hasFile)),me!=="all"&&(me==="Not being searched"?s=s.filter(o=>o.reason==="Not being searched"||!o.reason):s=s.filter(o=>o.reason===me)),s},[X,R,ye,me]),Ne=!!R||me!=="all",Fe=a.useMemo(()=>{const s=[...Ce],o=(c,E)=>{switch(E){case"__instance":return(c.__instance||"").toLowerCase();case"title":return(c.title||"").toLowerCase();case"year":return c.year??0;case"monitored":return c.monitored?1:0;case"hasFile":return c.hasFile?1:0;default:return""}};return s.sort((c,E)=>{const _=o(c,re.key),O=o(E,re.key);let k=0;return typeof _=="number"&&typeof O=="number"?k=_-O:typeof _=="string"&&typeof O=="string"?k=_.localeCompare(O):k=String(_).localeCompare(String(O)),re.direction==="asc"?k:-k}),s},[Ce,re]),Oe=Math.max(1,Math.ceil(Fe.length/Be)),He=a.useMemo(()=>Fe.slice(i*Be,i*Be+Be),[Fe,i]),$e=a.useMemo(()=>{const s=Object.keys(q).map(Number).sort((c,E)=>c-E),o=[];return s.forEach(c=>{q[c]&&o.push(...q[c])}),o},[q]),Ve=a.useCallback(async()=>{if(!(!n||n==="aggregate"))try{await ds(n),t(`Restarted ${n}`,"success")}catch(s){t(s instanceof Error?s.message:`Failed to restart ${n}`,"error")}},[n,t]),Qe=a.useCallback(()=>{Se({showLoading:!0})},[Se]),We=a.useCallback(s=>{we(o=>o.key===s?{key:s,direction:o.direction==="asc"?"desc":"asc"}:{key:s,direction:"asc"})},[]),Ie=a.useCallback(()=>{n&&n!=="aggregate"&&pe(n,K,L,{preloadAll:!1,showLoading:!0})},[n,K,L,pe]),d=a.useCallback(s=>{const o=s.target.value||"aggregate";M(o),o!=="aggregate"&&h("")},[M,h]),p=n==="aggregate";return e.jsxs("section",{className:"card",children:[e.jsx("div",{className:"card-header",children:"Radarr"}),e.jsx("div",{className:"card-body",children:e.jsxs("div",{className:"split",children:[e.jsxs("aside",{className:"pane sidebar",children:[m.length>1&&e.jsx("button",{className:`btn ${p?"active":""}`,onClick:()=>M("aggregate"),children:"All Radarr"}),m.map(s=>e.jsx("button",{className:`btn ghost ${n===s.category?"active":""}`,onClick:()=>{M(s.category),h("")},children:s.name||s.category},s.category))]}),e.jsxs("div",{className:"pane",children:[e.jsxs("div",{className:"field mobile-instance-select",children:[e.jsx("label",{children:"Instance"}),e.jsxs("select",{value:n||"aggregate",onChange:d,disabled:!m.length,children:[m.length>1&&e.jsx("option",{value:"aggregate",children:"All Radarr"}),m.map(s=>e.jsx("option",{value:s.category,children:s.name||s.category},s.category))]})]}),e.jsxs("div",{className:"row",style:{alignItems:"flex-end",gap:"12px",flexWrap:"wrap"},children:[e.jsxs("div",{className:"col field",style:{flex:"1 1 200px"},children:[e.jsx("label",{children:"Search"}),e.jsx("input",{placeholder:"Filter movies",value:u,onChange:s=>h(s.target.value)})]}),e.jsxs("div",{className:"field",style:{flex:"0 0 auto",minWidth:"140px"},children:[e.jsx("label",{children:"Status"}),e.jsxs("select",{onChange:s=>{const o=s.target.value;Re(o==="missing")},value:ye?"missing":"all",children:[e.jsx("option",{value:"all",children:"All Movies"}),e.jsx("option",{value:"missing",children:"Missing Only"})]})]}),e.jsxs("div",{className:"field",style:{flex:"0 0 auto",minWidth:"140px"},children:[e.jsx("label",{children:"Search Reason"}),e.jsxs("select",{onChange:s=>Y(s.target.value),value:me,children:[e.jsx("option",{value:"all",children:"All Reasons"}),e.jsx("option",{value:"Not being searched",children:"Not Being Searched"}),e.jsx("option",{value:"Missing",children:"Missing"}),e.jsx("option",{value:"Quality",children:"Quality"}),e.jsx("option",{value:"CustomFormat",children:"Custom Format"}),e.jsx("option",{value:"Upgrade",children:"Upgrade"})]})]})]}),p?e.jsx(Ms,{loading:ee,rows:He,total:Fe.length,page:i,totalPages:Oe,onPageChange:x,onRefresh:Qe,lastUpdated:$,sort:re,onSort:We,summary:fe,instanceCount:m.length,isAggFiltered:Ne}):e.jsx(Ps,{loading:J,data:b,page:K,totalPages:te,pageSize:Q,allMovies:$e,onlyMissing:ye,reasonFilter:me,onPageChange:s=>{A(s),pe(n,s,L,{preloadAll:!0})},onRefresh:Ie,onRestart:()=>void Ve(),lastUpdated:g})]})]})})]})}const ts=25,Ge=50,bs=200;function Ds(r,t){if(!t)return r;const u=[];for(const h of r){const y=h.seasons??{},v={};for(const[F,N]of Object.entries(y)){const m=(N.episodes??[]).filter(T=>!T.hasFile);m.length&&(v[F]={...N,episodes:m})}Object.keys(v).length!==0&&u.push({...h,seasons:v})}return u}function Ye(r,t){return JSON.stringify(Ds(r,t))}function $s({active:r}){const{push:t}=is(),{value:u,setValue:h,register:y,clearHandler:v}=rs(),{liveArr:F,setLiveArr:N,groupSonarr:m,setGroupSonarr:T}=os(),[n,M]=a.useState([]),[b,B]=a.useState(""),[K,A]=a.useState(null),[L,ge]=a.useState(0),[J,C]=a.useState(""),[g,j]=a.useState(!1),[q,G]=a.useState(null),[Q,le]=a.useState({}),te=a.useRef({}),W=a.useRef(null),Z=a.useRef(""),[l,S]=a.useState(ts),[D,P]=a.useState(1),[X,ie]=a.useState(0),ee=a.useRef(u),f=a.useRef(!1),i=a.useRef(b),[x,R]=a.useState([]),[I,$]=a.useState(!1),[U,he]=a.useState(0),[re,we]=a.useState(""),[ye,Re]=a.useState(null),me=_e({getKey:s=>`${s.__instance}-${s.series}-${s.season}-${s.episode}`,hashFields:["__instance","series","season","episode","title","hasFile","monitored","airDate","reason","qualityProfileId","qualityProfileName"]}),[Y,fe]=a.useState(!1),Ae=a.useRef(Y),[be,De]=a.useState("all"),[pe,Se]=a.useState({available:0,monitored:0,missing:0,total:0}),Ce=a.useCallback(async()=>{try{const s=await cs();s.ready===!1&&!f.current?(f.current=!0,t("Sonarr backend is still initialising. Check the logs if this persists.","info")):s.ready&&(f.current=!0);const o=(s.arr||[]).filter(c=>c.type==="sonarr");if(M(o),!o.length){B("aggregate"),A(null),R([]),Se({available:0,monitored:0,missing:0,total:0});return}b===""?B(o.length===1?o[0].category:"aggregate"):b!=="aggregate"&&!o.some(c=>c.category===b)&&B(o[0].category)}catch(s){t(s instanceof Error?s.message:"Unable to load Sonarr instances","error")}},[t,b]),Ne=a.useCallback(async(s,o,c,E={})=>{const{preloadAll:_=!0,showLoading:O=!0,missingOnly:k}=E,z=k??Y;O&&j(!0);try{const H=`${s}::${c}::${z?"missing":"all"}`,oe=Z.current!==H;oe&&(Z.current=H,le(()=>(te.current={},{})),ie(0),P(1));const w=await Xe(s,o,ts,c,{missingOnly:z});console.log(`[Sonarr Instance] Response for ${s} page ${o}:`,{total:w.total,page:w.page,page_size:w.page_size,series_count:w.series?.length??0,counts:w.counts,missingOnly:z,firstSeries:w.series?.[0]?{title:w.series[0].series?.title,seasonsCount:Object.keys(w.series[0].seasons??{}).length,firstSeasonEpisodes:Object.values(w.series[0].seasons??{})[0]?.episodes?.length??0}:null});const se=w.page??o,ue=w.page_size??ts,xe=w.total??(w.series??[]).length,ae=Math.max(1,Math.ceil((xe||0)/ue)),V=w.series??[],de=oe?{}:te.current,je={...de,[se]:V},ce=Ye(de[se]??[],z),Ee=Ye(V,z),ke=oe||ce!==Ee;if(te.current=je,ke&&le(je),A(ne=>{const ve=ne?.counts??null,Le=w.counts??null;return!ne||ne.total!==w.total||ne.page!==w.page||ne.page_size!==w.page_size||(ve?.available??null)!==(Le?.available??null)||(ve?.monitored??null)!==(Le?.monitored??null)||(ve?.missing??null)!==(Le?.missing??null)||ke?(W.current=w,w):ne}),ge(ne=>ne===se?ne:se),C(ne=>ne===c?ne:c),S(ne=>ne===ue?ne:ue),P(ne=>ne===ae?ne:ae),ie(ne=>ne===xe?ne:xe),ke&&G(new Date().toLocaleTimeString()),_){const ne=[];for(let ve=0;ve<ae;ve+=1)ve!==se&&(je[ve]||ne.push(ve));for(const ve of ne)try{const Le=await Xe(s,ve,ue,c,{missingOnly:z});if(Z.current!==H)break;const Me=Le.page??ve,Ze=Le.series??[],us=te.current,js=Ye(us[Me]??[],z),ps=Ye(Ze,z);if(js===ps){te.current={...us,[Me]:Ze};continue}le(Ns=>{const hs={...Ns,[Me]:Ze};return te.current=hs,hs})}catch{break}}}catch(H){t(H instanceof Error?H.message:`Failed to load ${s} series`,"error")}finally{O&&j(!1)}},[t,Y]),Fe=a.useCallback(async s=>{if(!n.length){R([]),Se({available:0,monitored:0,missing:0,total:0});return}console.log(`[Sonarr Aggregate] Starting aggregation for ${n.length} instances`),(s?.showLoading??!0)&&$(!0);try{const c=[];let E=0,_=0,O=0;for(const se of n){let ue=0,xe=!1;const ae=se.name||se.category;for(console.log(`[Sonarr Aggregate] Processing instance: ${ae}`);ue<200;){const V=await Xe(se.category,ue,bs,"",{missingOnly:Y});if(console.log(`[Sonarr Aggregate] Response for ${ae} page ${ue}:`,{total:V.total,page:V.page,page_size:V.page_size,series_count:V.series?.length??0,counts:V.counts,missingOnly:Y,firstSeries:V.series?.[0]?{title:V.series[0].series?.title,seasonsCount:Object.keys(V.series[0].seasons??{}).length,firstSeasonEpisodes:Object.values(V.series[0].seasons??{})[0]?.episodes?.length??0,firstEpisode:Object.values(V.series[0].seasons??{})[0]?.episodes?.[0]}:null}),!xe){const ce=V.counts;ce&&(E+=ce.available??0,_+=ce.monitored??0,O+=ce.missing??0),xe=!0}const de=V.series??[];let je=0;if(de.forEach(ce=>{const Ee=Object.keys(ce.seasons??{}).length;let ke=0;Object.values(ce.seasons??{}).forEach(ne=>{ke+=(ne.episodes??[]).length}),je+=ke}),console.log(`[Sonarr Aggregate] Instance: ${ae}, Page: ${ue}, Series count: ${de.length}, Total episodes so far: ${c.length}, Episodes in this response: ${je}`),de.forEach(ce=>{const Ee=ce.series?.title||"",ke=ce.series?.qualityProfileId??null,ne=ce.series?.qualityProfileName??null;Object.entries(ce.seasons??{}).forEach(([ve,Le])=>{(Le.episodes??[]).forEach(Me=>{const Ze=Me.reason??null;c.push({__instance:ae,series:Ee,season:ve,episode:Me.episodeNumber??"",title:Me.title??"",monitored:!!Me.monitored,hasFile:!!Me.hasFile,airDate:Me.airDateUtc??"",reason:Ze,qualityProfileId:ke,qualityProfileName:ne})})})}),!de.length||de.length<bs){console.log(`[Sonarr Aggregate] Breaking pagination for ${ae} - series.length=${de.length}`);break}ue+=1}}const k=me.syncData(c),z=k.hasChanges,H=new Map;c.forEach(se=>{const ue=se.reason||"null/empty";H.set(ue,(H.get(ue)||0)+1)}),console.log("[Sonarr Aggregate] Reason distribution:",Object.fromEntries(H)),z?(console.log(`[Sonarr Aggregate] Data changed, updating from ${x.length} to ${c.length} episodes`),R(k.data)):console.log("[Sonarr Aggregate] Data unchanged, skipping update");const oe={available:E,monitored:_,missing:O,total:c.length},w=pe.available!==oe.available||pe.monitored!==oe.monitored||pe.missing!==oe.missing||pe.total!==oe.total;w&&Se(oe),re!==u&&(he(0),we(u)),(z||w)&&Re(new Date().toLocaleTimeString())}catch(c){R([]),Se({available:0,monitored:0,missing:0,total:0}),t(c instanceof Error?c.message:"Failed to load aggregated Sonarr data","error")}finally{$(!1)}},[n,u,t,Y,re]);a.useEffect(()=>{r&&Ce()},[r,Ce]),a.useEffect(()=>{if(!r||!b||b==="aggregate")return;const s=i.current!==b,o=Ae.current!==Y;s&&(ge(0),i.current=b),o&&(Ae.current=Y);const c=ee.current;Ne(b,s?0:L,c,{preloadAll:!0,showLoading:!0,missingOnly:Y})},[r,b,Y,Ne,L]),a.useEffect(()=>{r&&b==="aggregate"&&Fe()},[r,b,Fe]),Ke(()=>{b==="aggregate"&&F&&Fe({showLoading:!1})},b==="aggregate"&&F?1e3:null),a.useEffect(()=>{if(!r)return;const s=o=>{b==="aggregate"?(we(o),he(0)):b&&(ge(0),Ne(b,0,o,{preloadAll:!0,showLoading:!0,missingOnly:Y}))};return y(s),()=>v(s)},[r,b,y,v,Ne,Y]),Ke(()=>{if(b&&b!=="aggregate"){if(ee.current?.trim?.()||"")return;Ne(b,L,J,{preloadAll:!1,showLoading:!1,missingOnly:Y})}},r&&b&&b!=="aggregate"&&F?1e3:null),a.useEffect(()=>{ee.current=u},[u]),a.useEffect(()=>{b==="aggregate"&&we(u)},[b,u]);const Oe=a.useMemo(()=>{let s=x;if(re){const o=re.toLowerCase();s=s.filter(c=>c.series.toLowerCase().includes(o)||c.title.toLowerCase().includes(o)||c.__instance.toLowerCase().includes(o))}if(Y&&(s=s.filter(o=>!o.hasFile)),be!=="all"){console.log(`[Sonarr Filter] Applying reason filter: "${be}"`);const o=s.length;be==="Not being searched"?s=s.filter(c=>c.reason==="Not being searched"||!c.reason):s=s.filter(c=>c.reason===be),console.log(`[Sonarr Filter] Filtered from ${o} to ${s.length} episodes for reason "${be}"`),s.length<10&&console.log("[Sonarr Filter] Sample filtered rows:",s.slice(0,5).map(c=>({series:c.series,episode:c.episode,reason:c.reason})))}return s},[x,re,Y,be]),He=!!re||Y||be!=="all",$e=Oe,Ve=Math.max(1,Math.ceil($e.length/Ge));a.useMemo(()=>$e.slice(U*Ge,U*Ge+Ge),[$e,U]);const Qe=Q[L]??[],We=a.useMemo(()=>{const s=Object.keys(Q).map(Number).sort((c,E)=>c-E),o=[];return s.forEach(c=>{Q[c]&&o.push(...Q[c])}),o},[Q]),Ie=a.useCallback(async()=>{if(!(!b||b==="aggregate"))try{await ds(b),t(`Restarted ${b}`,"success")}catch(s){t(s instanceof Error?s.message:`Failed to restart ${b}`,"error")}},[b,t]),d=a.useCallback(s=>{const o=s.target.value||"aggregate";B(o),o!=="aggregate"&&h("")},[B,h]),p=b==="aggregate";return e.jsxs("section",{className:"card",children:[e.jsx("div",{className:"card-header",children:"Sonarr"}),e.jsx("div",{className:"card-body",children:e.jsxs("div",{className:"split",children:[e.jsxs("aside",{className:"pane sidebar",children:[n.length>1&&e.jsx("button",{className:`btn ${p?"active":""}`,onClick:()=>B("aggregate"),children:"All Sonarr"}),n.map(s=>e.jsx("button",{className:`btn ghost ${b===s.category?"active":""}`,onClick:()=>{B(s.category),h("")},children:s.name||s.category},s.category))]}),e.jsxs("div",{className:"pane",children:[e.jsxs("div",{className:"field mobile-instance-select",children:[e.jsx("label",{children:"Instance"}),e.jsxs("select",{value:b||"aggregate",onChange:d,disabled:!n.length,children:[n.length>1&&e.jsx("option",{value:"aggregate",children:"All Sonarr"}),n.map(s=>e.jsx("option",{value:s.category,children:s.name||s.category},s.category))]})]}),e.jsxs("div",{className:"row",style:{alignItems:"flex-end",gap:"12px",flexWrap:"wrap"},children:[e.jsxs("div",{className:"col field",style:{flex:"1 1 200px"},children:[e.jsx("label",{children:"Search"}),e.jsx("input",{placeholder:"Filter series or episodes",value:u,onChange:s=>h(s.target.value)})]}),e.jsxs("div",{className:"field",style:{flex:"0 0 auto",minWidth:"140px"},children:[e.jsx("label",{children:"Status"}),e.jsxs("select",{onChange:s=>{const c=s.target.value==="missing";fe(c),b&&b!=="aggregate"&&Ne(b,0,ee.current||"",{preloadAll:!0,showLoading:!0,missingOnly:c})},value:Y?"missing":"all",children:[e.jsx("option",{value:"all",children:"All Episodes"}),e.jsx("option",{value:"missing",children:"Missing Only"})]})]}),e.jsxs("div",{className:"field",style:{flex:"0 0 auto",minWidth:"140px"},children:[e.jsx("label",{children:"Search Reason"}),e.jsxs("select",{onChange:s=>De(s.target.value),value:be,children:[e.jsx("option",{value:"all",children:"All Reasons"}),e.jsx("option",{value:"Not being searched",children:"Not Being Searched"}),e.jsx("option",{value:"Missing",children:"Missing"}),e.jsx("option",{value:"Quality",children:"Quality"}),e.jsx("option",{value:"CustomFormat",children:"Custom Format"}),e.jsx("option",{value:"Upgrade",children:"Upgrade"})]})]})]}),p?e.jsx(ks,{loading:I,rows:$e,total:$e.length,page:U,totalPages:Ve,onPageChange:he,onRefresh:()=>void Fe({showLoading:!0}),lastUpdated:ye,groupSonarr:m,summary:pe,instanceCount:n.length,isAggFiltered:He}):e.jsx(Ls,{loading:g,counts:K?.counts??null,series:m?Qe:We,page:L,pageSize:l,totalPages:D,totalItems:X,onlyMissing:Y,reasonFilter:be,onPageChange:s=>{ge(s),Ne(b,s,J,{preloadAll:!1,showLoading:!0,missingOnly:Y})},onRestart:()=>void Ie(),lastUpdated:q,groupSonarr:m,instances:n,selection:b})]})]})})]})}function ks({loading:r,rows:t,total:u,page:h,totalPages:y,onPageChange:v,onRefresh:F,lastUpdated:N,groupSonarr:m,summary:T,instanceCount:n,isAggFiltered:M=!1}){const b=a.useRef([]),B=a.useRef([]),K=a.useRef(new Map),A=a.useMemo(()=>{if(t===b.current)return B.current;const l=new Map;t.forEach(P=>{const X=P.__instance,ie=P.series,ee=String(P.season);l.has(X)||l.set(X,new Map);const f=l.get(X);f.has(ie)||f.set(ie,new Map);const i=f.get(ie);i.has(ee)||i.set(ee,[]),i.get(ee).push(P)});const S=[],D=new Map;return l.forEach((P,X)=>{P.forEach((ie,ee)=>{const f=`${X}-${ee}`,i=new Set;ie.forEach(($,U)=>{$.forEach(he=>{const re=`${U}-${he.episode}`;i.add(re)})});const x=K.current.get(f);if(x&&x.episodeKeys.size===i.size){let $=!0;for(const U of i)if(!x.episodeKeys.has(U)){$=!1;break}if($){S.push(x),D.set(f,x);return}}const R=Array.from(ie.values())[0]?.[0];console.log(`[Sonarr Grouped] Series: ${ee}, QualityProfile: ${R?.qualityProfileName}, FirstEpisode:`,R);const I={instance:X,series:ee,qualityProfileId:R?.qualityProfileId,qualityProfileName:R?.qualityProfileName,subRows:Array.from(ie.entries()).map(([$,U])=>({seasonNumber:$,isSeason:!0,subRows:U.map(he=>({...he,isEpisode:!0}))})),episodeKeys:i};S.push(I),D.set(f,I)})}),b.current=t,B.current=S,K.current=D,S},[t]),L=a.useMemo(()=>A.slice(h*50,(h+1)*50),[A,h]),ge=a.useMemo(()=>t.slice(h*50,(h+1)*50),[t,h]),J=m?L:ge,C=a.useMemo(()=>[{accessorKey:"title",header:"Title",cell:({row:l})=>{if(l.original.isEpisode)return l.original.title;if(l.original.isSeason)return`Season ${l.original.seasonNumber}`;const S=[l.original.series];return l.original.instance&&S.push(`(${l.original.instance})`),l.original.qualityProfileName&&S.push(`• ${l.original.qualityProfileName}`),S.join(" ")}},{accessorKey:"monitored",header:"Monitored",cell:({row:l})=>{const S=(l.original.isEpisode,l.original.monitored);return e.jsx("span",{className:`track-status ${S?"available":"missing"}`,children:S?"✓":"✗"})}},{accessorKey:"hasFile",header:"Has File",cell:({row:l})=>{if(l.original.isEpisode){const S=l.original.hasFile;return e.jsx("span",{className:`track-status ${S?"available":"missing"}`,children:S?"✓":"✗"})}return null}},{accessorKey:"airDate",header:"Air Date",cell:({row:l})=>l.original.isEpisode?l.original.airDate||"—":null}],[]),g=a.useMemo(()=>[...n>1?[{accessorKey:"__instance",header:"Instance"}]:[],{accessorKey:"series",header:"Series"},{accessorKey:"season",header:"Season"},{accessorKey:"episode",header:"Episode"},{accessorKey:"title",header:"Title"},{accessorKey:"monitored",header:"Monitored",cell:({getValue:l})=>{const S=l();return e.jsx("span",{className:`track-status ${S?"available":"missing"}`,children:S?"✓":"✗"})}},{accessorKey:"hasFile",header:"Has File",cell:({getValue:l})=>{const S=l();return e.jsx("span",{className:`track-status ${S?"available":"missing"}`,children:S?"✓":"✗"})}},{accessorKey:"airDate",header:"Air Date",cell:({getValue:l})=>l()||"—"},{accessorKey:"qualityProfileName",header:"Quality Profile",cell:({getValue:l})=>l()||"—"},{accessorKey:"reason",header:"Reason",cell:({getValue:l})=>{const S=l();return S?e.jsx("span",{className:"table-badge table-badge-reason",children:S}):e.jsx("span",{className:"table-badge table-badge-reason",children:"Not being searched"})}}],[n]),j=m?C:g,q=ze({data:J,columns:j,getCoreRowModel:Te(),getExpandedRowModel:vs()}),G=ze({data:J,columns:j,getCoreRowModel:Te(),getSortedRowModel:gs(),getPaginationRowModel:ys(),state:{pagination:{pageIndex:h,pageSize:50}},manualPagination:!0,pageCount:y}),Q=m?q:G,le=50,te=Math.ceil(m?A.length/le:t.length/le),W=Math.min(h,Math.max(0,te-1)),Z=m?`${A.length} series`:t.length.toLocaleString();return e.jsxs("div",{className:"stack animate-fade-in",children:[e.jsxs("div",{className:"row",style:{justifyContent:"space-between"},children:[e.jsxs("div",{className:"hint",children:["Aggregated episodes across all instances"," ",N?`(updated ${N})`:"",e.jsx("br",{}),e.jsx("strong",{children:"Available:"})," ",T.available.toLocaleString(void 0,{maximumFractionDigits:0})," •"," ",e.jsx("strong",{children:"Monitored:"})," ",T.monitored.toLocaleString(void 0,{maximumFractionDigits:0})," •"," ",e.jsx("strong",{children:"Missing:"})," ",T.missing.toLocaleString(void 0,{maximumFractionDigits:0})," •"," ",e.jsx("strong",{children:"Total Episodes:"})," ",T.total.toLocaleString(void 0,{maximumFractionDigits:0}),M&&t.length<T.total&&e.jsxs(e.Fragment,{children:[" ","• ",e.jsx("strong",{children:"Filtered:"})," ",t.length.toLocaleString(void 0,{maximumFractionDigits:0})," of"," ",T.total.toLocaleString(void 0,{maximumFractionDigits:0})]})]}),e.jsxs("button",{className:"btn ghost",onClick:F,disabled:r,children:[e.jsx(qe,{src:Ue}),"Refresh"]})]}),r?e.jsxs("div",{className:"loading",children:[e.jsx("span",{className:"spinner"})," Loading Sonarr library…"]}):m?e.jsx("div",{className:"sonarr-hierarchical-view",children:L.map(l=>{console.log(`[Sonarr Render] Series: ${l.series}, QualityProfile: ${l.qualityProfileName}`);let S=0;return l.subRows.forEach(D=>{S+=D.subRows.length}),e.jsxs("details",{className:"series-details",children:[e.jsxs("summary",{className:"series-summary",children:[e.jsx("span",{className:"series-title",children:l.series}),e.jsxs("span",{className:"series-instance",children:["(",l.instance,")"]}),e.jsxs("span",{className:"series-count",children:["(",S," episodes)"]}),l.qualityProfileName?e.jsxs("span",{className:"series-quality",children:["• ",l.qualityProfileName]}):null]}),e.jsx("div",{className:"series-content",children:l.subRows.map(D=>e.jsxs("details",{className:"season-details",children:[e.jsxs("summary",{className:"season-summary",children:[e.jsxs("span",{className:"season-title",children:["Season ",D.seasonNumber]}),e.jsxs("span",{className:"season-count",children:["(",D.subRows.length," episodes)"]})]}),e.jsx("div",{className:"season-content",children:e.jsx("div",{className:"episodes-table-wrapper",children:e.jsxs("table",{className:"episodes-table",children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{children:"Episode"}),e.jsx("th",{children:"Title"}),e.jsx("th",{children:"Monitored"}),e.jsx("th",{children:"Has File"}),e.jsx("th",{children:"Air Date"}),e.jsx("th",{children:"Reason"})]})}),e.jsx("tbody",{children:D.subRows.map(P=>e.jsxs("tr",{children:[e.jsx("td",{"data-label":"Episode",children:P.episode}),e.jsx("td",{"data-label":"Title",children:P.title}),e.jsx("td",{"data-label":"Monitored",children:e.jsx("span",{className:`track-status ${P.monitored?"available":"missing"}`,children:P.monitored?"✓":"✗"})}),e.jsx("td",{"data-label":"Has File",children:e.jsx("span",{className:`track-status ${P.hasFile?"available":"missing"}`,children:P.hasFile?"✓":"✗"})}),e.jsx("td",{"data-label":"Air Date",children:P.airDate||"—"}),e.jsx("td",{"data-label":"Reason",children:P.reason?e.jsx("span",{className:"table-badge table-badge-reason",children:P.reason}):e.jsx("span",{className:"table-badge table-badge-reason",children:"Not being searched"})})]},`${P.__instance}-${P.series}-${P.season}-${P.episode}`))})]})})})]},`${l.instance}-${l.series}-${D.seasonNumber}`))})]},`${l.instance}-${l.series}`)})}):!r&&T.total===0&&n>0?e.jsxs("div",{className:"hint",children:[e.jsx("p",{children:"No episodes found in the database."}),e.jsx("p",{children:"The backend may still be initializing and syncing data from your Sonarr instances. Please check the logs or wait a few moments and refresh."})]}):J.length?e.jsx("div",{className:"table-wrapper",children:e.jsxs("table",{className:"responsive-table",children:[e.jsx("thead",{children:e.jsx("tr",{children:Q.getFlatHeaders().map(l=>e.jsxs("th",{className:l.column.getCanSort()?"sortable":"",onClick:l.column.getToggleSortingHandler(),children:[l.isPlaceholder?null:Pe(l.column.columnDef.header,l.getContext()),l.column.getCanSort()&&e.jsx("span",{className:"sort-arrow",children:{asc:"▲",desc:"▼"}[l.column.getIsSorted()]??null})]},l.id))})}),e.jsx("tbody",{children:Q.getRowModel().rows.map(l=>{const S=l.original,D=`${S.__instance}-${S.series}-${S.season}-${S.episode}`;return e.jsx("tr",{children:l.getVisibleCells().map(P=>e.jsx("td",{"data-label":P.column.columnDef.header,children:Pe(P.column.columnDef.cell,P.getContext())},P.id))},D)})})]})}):e.jsx("div",{className:"hint",children:"No series found."}),J.length>0&&e.jsxs("div",{className:"pagination",children:[e.jsxs("div",{children:["Page ",W+1," of ",te," (",Z," items · page size ",le,")"]}),e.jsxs("div",{className:"inline",children:[e.jsx("button",{className:"btn",onClick:()=>v(Math.max(0,W-1)),disabled:W===0||r,children:"Prev"}),e.jsx("button",{className:"btn",onClick:()=>v(Math.min(te-1,W+1)),disabled:W>=te-1||r,children:"Next"})]})]})]})}function Ls({loading:r,counts:t,series:u,page:h,pageSize:y,totalPages:v,totalItems:F,onlyMissing:N,reasonFilter:m,onPageChange:T,onRestart:n,lastUpdated:M,groupSonarr:b,instances:B,selection:K}){const[A,L]=a.useState(0),ge=50,J=a.useRef([]),C=a.useRef([]),g=a.useMemo(()=>{if(u===J.current)return C.current;const i=[];for(const x of u){const R=x.series?.title||"",I=x.series?.qualityProfileId??null,$=x.series?.qualityProfileName??null;Object.entries(x.seasons??{}).forEach(([U,he])=>{(he.episodes??[]).forEach(re=>{i.push({__instance:"Instance",series:R,season:U,episode:re.episodeNumber??"",title:re.title??"",monitored:!!re.monitored,hasFile:!!re.hasFile,airDate:re.airDateUtc??"",reason:re.reason??null,qualityProfileId:I,qualityProfileName:$})})})}return J.current=u,C.current=i,i},[u]),j=a.useMemo(()=>{let i=g;if(N&&(i=i.filter(x=>!x.hasFile)),m!=="all"){console.log(`[Sonarr Instance Filter] Applying reason filter: "${m}"`);const x=i.length;m==="Not being searched"?i=i.filter(R=>R.reason==="Not being searched"||!R.reason):i=i.filter(R=>R.reason===m),console.log(`[Sonarr Instance Filter] Filtered from ${x} to ${i.length} episodes for reason "${m}"`),i.length<10&&console.log("[Sonarr Instance Filter] Sample filtered rows:",i.slice(0,5).map(R=>({series:R.series,episode:R.episode,reason:R.reason})))}return i},[g,N,m]);a.useEffect(()=>{L(0)},[N,m]);const q=a.useRef([]),G=a.useRef([]),Q=a.useMemo(()=>{if(j===q.current)return G.current;const i=new Map;j.forEach(R=>{const I=R.series;i.has(I)||i.set(I,new Map);const $=i.get(I),U=String(R.season);$.has(U)||$.set(U,[]),$.get(U).push(R)});const x=Array.from(i.entries()).map(([R,I])=>{const $=Array.from(I.values())[0]?.[0];return{series:R,qualityProfileId:$?.qualityProfileId,qualityProfileName:$?.qualityProfileName,subRows:Array.from(I.entries()).map(([U,he])=>({seasonNumber:U,isSeason:!0,subRows:he.map(re=>({...re,isEpisode:!0}))}))}});return q.current=j,G.current=x,x},[j]),le=a.useMemo(()=>g.length,[g]),te=m!=="all"||N,W=j.length,Z=Math.max(1,Math.ceil(j.length/ge)),l=Math.min(A,Math.max(0,Z-1)),S=a.useMemo(()=>j.slice(l*ge,(l+1)*ge),[j,l]),D=50,[P,X]=a.useState(0),ie=Math.max(1,Math.ceil(Q.length/D)),ee=Math.min(P,Math.max(0,ie-1)),f=a.useMemo(()=>Q.slice(ee*D,(ee+1)*D),[Q,ee]);return a.useEffect(()=>{X(0)},[N,m]),e.jsxs("div",{className:"stack animate-fade-in",children:[e.jsxs("div",{className:"row",style:{justifyContent:"space-between"},children:[e.jsxs("div",{className:"hint",children:[t?e.jsxs(e.Fragment,{children:[e.jsx("strong",{children:"Available:"})," ",t.available.toLocaleString(void 0,{maximumFractionDigits:0})," •"," ",e.jsx("strong",{children:"Monitored:"})," ",t.monitored.toLocaleString(void 0,{maximumFractionDigits:0})," •"," ",e.jsx("strong",{children:"Missing:"})," ",(t.missing??0).toLocaleString(void 0,{maximumFractionDigits:0})," •"," ",e.jsx("strong",{children:"Total Episodes:"})," ",le.toLocaleString(void 0,{maximumFractionDigits:0}),te&&W<le&&e.jsxs(e.Fragment,{children:[" ","• ",e.jsx("strong",{children:"Filtered:"})," ",W.toLocaleString(void 0,{maximumFractionDigits:0})," of"," ",le.toLocaleString(void 0,{maximumFractionDigits:0})]})]}):"Loading series information...",M?` (updated ${M})`:""]}),e.jsxs("button",{className:"btn ghost",onClick:n,disabled:r,children:[e.jsx(qe,{src:Ue}),"Restart"]})]}),r?e.jsxs("div",{className:"loading",children:[e.jsx("span",{className:"spinner"})," Loading series…"]}):b?e.jsx("div",{className:"sonarr-hierarchical-view",children:f.map(i=>{let x=0;i.subRows.forEach(I=>{x+=I.subRows.length});const R=B.find(I=>I.category===K)?.name||K;return e.jsxs("details",{className:"series-details",children:[e.jsxs("summary",{className:"series-summary",children:[e.jsx("span",{className:"series-title",children:i.series}),e.jsxs("span",{className:"series-instance",children:["(",R,")"]}),e.jsxs("span",{className:"series-count",children:["(",x," episodes)"]}),i.qualityProfileName?e.jsxs("span",{className:"series-quality",children:["• ",i.qualityProfileName]}):null]}),e.jsx("div",{className:"series-content",children:i.subRows.map(I=>e.jsxs("details",{className:"season-details",children:[e.jsxs("summary",{className:"season-summary",children:[e.jsxs("span",{className:"season-title",children:["Season ",I.seasonNumber]}),e.jsxs("span",{className:"season-count",children:["(",I.subRows.length," episodes)"]})]}),e.jsx("div",{className:"season-content",children:e.jsx("div",{className:"episodes-table-wrapper",children:e.jsxs("table",{className:"episodes-table",children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{children:"Episode"}),e.jsx("th",{children:"Title"}),e.jsx("th",{children:"Monitored"}),e.jsx("th",{children:"Has File"}),e.jsx("th",{children:"Air Date"}),e.jsx("th",{children:"Reason"})]})}),e.jsx("tbody",{children:I.subRows.map($=>e.jsxs("tr",{children:[e.jsx("td",{"data-label":"Episode",children:$.episode}),e.jsx("td",{"data-label":"Title",children:$.title}),e.jsx("td",{"data-label":"Monitored",children:e.jsx("span",{className:`track-status ${$.monitored?"available":"missing"}`,children:$.monitored?"✓":"✗"})}),e.jsx("td",{"data-label":"Has File",children:e.jsx("span",{className:`track-status ${$.hasFile?"available":"missing"}`,children:$.hasFile?"✓":"✗"})}),e.jsx("td",{"data-label":"Air Date",children:$.airDate||"—"}),e.jsx("td",{"data-label":"Reason",children:$.reason?e.jsx("span",{className:"table-badge table-badge-reason",children:$.reason}):e.jsx("span",{className:"table-badge table-badge-reason",children:"Not being searched"})})]},`${$.series}-${$.season}-${$.episode}`))})]})})})]},`${i.series}-${I.seasonNumber}`))})]},`${i.series}`)})}):!r&&u.length>0&&j.length===0&&g.length===0?e.jsxs("div",{className:"hint",children:[e.jsx("p",{children:"No episodes found for these series."}),e.jsx("p",{children:"The backend may still be syncing episode data from Sonarr. Please check the logs or wait a few moments and refresh."})]}):!r&&u.length>0&&j.length===0&&g.length>0?e.jsx("div",{className:"hint",children:"No episodes match the current filter."}):!b&&j.length>0?e.jsxs("div",{className:"table-wrapper",children:[e.jsxs("table",{className:"responsive-table",children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{children:"Series"}),e.jsx("th",{children:"Season"}),e.jsx("th",{children:"Episode"}),e.jsx("th",{children:"Title"}),e.jsx("th",{children:"Monitored"}),e.jsx("th",{children:"Has File"}),e.jsx("th",{children:"Air Date"}),e.jsx("th",{children:"Quality Profile"}),e.jsx("th",{children:"Reason"})]})}),e.jsx("tbody",{children:S.map((i,x)=>e.jsxs("tr",{children:[e.jsx("td",{"data-label":"Series",children:i.series}),e.jsx("td",{"data-label":"Season",children:i.season}),e.jsx("td",{"data-label":"Episode",children:i.episode}),e.jsx("td",{"data-label":"Title",children:i.title}),e.jsx("td",{"data-label":"Monitored",children:e.jsx("span",{className:`track-status ${i.monitored?"available":"missing"}`,children:i.monitored?"✓":"✗"})}),e.jsx("td",{"data-label":"Has File",children:e.jsx("span",{className:`track-status ${i.hasFile?"available":"missing"}`,children:i.hasFile?"✓":"✗"})}),e.jsx("td",{"data-label":"Air Date",children:i.airDate||"—"}),e.jsx("td",{"data-label":"Quality Profile",children:i.qualityProfileName||"—"}),e.jsx("td",{"data-label":"Reason",children:i.reason?e.jsx("span",{className:"table-badge table-badge-reason",children:i.reason}):e.jsx("span",{className:"table-badge table-badge-reason",children:"Not being searched"})})]},`${i.series}-${i.season}-${i.episode}-${x}`))})]}),Z>1&&e.jsxs("div",{className:"pagination",children:[e.jsxs("div",{children:["Page ",l+1," of ",Z," (",j.length.toLocaleString()," episodes · page size ",ge,")"]}),e.jsxs("div",{className:"inline",children:[e.jsx("button",{className:"btn",onClick:()=>L(Math.max(0,l-1)),disabled:l===0||r,children:"Prev"}),e.jsx("button",{className:"btn",onClick:()=>L(Math.min(Z-1,l+1)),disabled:l>=Z-1||r,children:"Next"})]})]})]}):e.jsx("div",{className:"hint",children:"No series found."}),b&&Q.length>0&&e.jsxs("div",{className:"pagination",children:[e.jsxs("div",{children:["Page ",ee+1," of ",ie," (",Q.length.toLocaleString()," series · page size ",D,")"]}),e.jsxs("div",{className:"inline",children:[e.jsx("button",{className:"btn",onClick:()=>X(Math.max(0,ee-1)),disabled:ee===0||r,children:"Prev"}),e.jsx("button",{className:"btn",onClick:()=>X(Math.min(ie-1,ee+1)),disabled:ee>=ie-1||r,children:"Next"})]})]})]})}const ns=50,xs=500;function Es({loading:r,rows:t,trackRows:u,page:h,onPageChange:y,onRefresh:v,lastUpdated:F,summary:N,instanceCount:m,groupLidarr:T,isAggFiltered:n=!1}){const M=a.useRef([]),b=a.useRef([]),B=a.useRef(new Map),K=a.useMemo(()=>{if(t===M.current)return b.current;const C=new Map;t.forEach(q=>{const G=q.__instance,Q=q.album?.artistName||"Unknown Artist";C.has(G)||C.set(G,new Map);const le=C.get(G);le.has(Q)||le.set(Q,[]),le.get(Q).push(q)});const g=[],j=new Map;return C.forEach((q,G)=>{q.forEach((Q,le)=>{const te=`${G}-${le}`,W=new Set;Q.forEach(P=>{const ie=`${P.album?.title}`;W.add(ie)});const Z=B.current.get(te);if(Z&&Z.albumKeys.size===W.size){let P=!0;for(const X of W)if(!Z.albumKeys.has(X)){P=!1;break}if(P){g.push(Z),j.set(te,Z);return}}const S=Q[0]?.album,D={instance:G,artist:le,qualityProfileId:S?.qualityProfileId??null,qualityProfileName:S?.qualityProfileName??null,albums:Q,albumKeys:W};g.push(D),j.set(te,D)})}),M.current=t,b.current=g,B.current=j,g},[t]),A=a.useMemo(()=>K.slice(h*50,(h+1)*50),[K,h]),L=a.useMemo(()=>{const g=h*50,j=g+50;return u.slice(g,j)},[u,h]),ge=a.useMemo(()=>[...m>1?[{accessorKey:"__instance",header:"Instance",size:120}]:[],{accessorKey:"artistName",header:"Artist",size:150},{accessorKey:"albumTitle",header:"Album",size:150},{accessorKey:"trackNumber",header:"#",size:50},{accessorKey:"title",header:"Track"},{accessorKey:"duration",header:"Duration",cell:C=>{const g=C.getValue();return g?`${Math.floor(g/60)}:${String(g%60).padStart(2,"0")}`:e.jsx("span",{className:"hint",children:"—"})},size:80},{accessorKey:"monitored",header:"Monitored",cell:C=>{const g=C.getValue();return e.jsx("span",{className:`track-status ${g?"available":"missing"}`,children:g?"✓":"✗"})},size:100},{accessorKey:"hasFile",header:"Has File",cell:C=>{const g=C.getValue();return e.jsx("span",{className:`track-status ${g?"available":"missing"}`,children:g?"✓":"✗"})},size:100},{accessorKey:"qualityProfileName",header:"Quality Profile",cell:C=>C.getValue()||"—",size:150},{accessorKey:"reason",header:"Reason",cell:C=>{const g=C.getValue();return g?e.jsx("span",{className:"table-badge table-badge-reason",children:g}):e.jsx("span",{className:"table-badge table-badge-reason",children:"Not being searched"})},size:120}],[m]),J=ze({data:L,columns:ge,getCoreRowModel:Te()});return e.jsxs("div",{className:"stack animate-fade-in",children:[e.jsxs("div",{className:"row",style:{justifyContent:"space-between"},children:[e.jsxs("div",{className:"hint",children:["Aggregated albums across all instances"," ",F?`(updated ${F})`:"",e.jsx("br",{}),e.jsx("strong",{children:"Available:"})," ",N.available.toLocaleString(void 0,{maximumFractionDigits:0})," •"," ",e.jsx("strong",{children:"Monitored:"})," ",N.monitored.toLocaleString(void 0,{maximumFractionDigits:0})," •"," ",e.jsx("strong",{children:"Missing:"})," ",N.missing.toLocaleString(void 0,{maximumFractionDigits:0})," •"," ",e.jsx("strong",{children:"Total:"})," ",N.total.toLocaleString(void 0,{maximumFractionDigits:0}),n&&(T?t.length:u.length)<N.total&&e.jsxs(e.Fragment,{children:[" ","• ",e.jsx("strong",{children:"Filtered:"})," ",(T?t.length:u.length).toLocaleString(void 0,{maximumFractionDigits:0})," of"," ",N.total.toLocaleString(void 0,{maximumFractionDigits:0})]})]}),e.jsxs("button",{className:"btn ghost",onClick:v,disabled:r,children:[e.jsx(qe,{src:Ue}),"Refresh"]})]}),r?e.jsxs("div",{className:"loading",children:[e.jsx("span",{className:"spinner"})," Loading Lidarr library…"]}):T?e.jsx("div",{className:"lidarr-hierarchical-view",children:A.map(C=>e.jsxs("details",{className:"artist-details",children:[e.jsxs("summary",{className:"artist-summary",children:[e.jsx("span",{className:"artist-title",children:C.artist}),e.jsxs("span",{className:"artist-instance",children:["(",C.instance,")"]}),e.jsxs("span",{className:"artist-count",children:["(",C.albums.length," albums)"]}),C.qualityProfileName?e.jsxs("span",{className:"artist-quality",children:["• ",C.qualityProfileName]}):null]}),e.jsx("div",{className:"artist-content",children:C.albums.map(g=>{const j=g.album,q=j?.title||"Unknown Album",G=j?.id||0,Q=j?.artistName||"",le=j?.releaseDate,te=j?.monitored,W=j?.hasFile,Z=j?.reason,l=g.tracks||[],S=g.totals;return e.jsxs("details",{className:"album-details",children:[e.jsxs("summary",{className:"album-summary",children:[e.jsx("span",{className:"album-title",children:q}),le&&e.jsx("span",{className:"album-date",children:new Date(le).toLocaleDateString()}),l&&l.length>0&&e.jsxs("span",{className:"album-track-count",children:["(",S.available||0,"/",S.monitored||l.length," tracks)"]}),e.jsx("span",{className:`album-status ${W?"has-file":"missing"}`,children:W?"✓":"✗"})]}),e.jsx("div",{className:"album-content",children:l&&l.length>0?e.jsx("div",{className:"tracks-table-wrapper",children:e.jsxs("table",{className:"tracks-table",children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{children:"#"}),e.jsx("th",{children:"Title"}),e.jsx("th",{children:"Duration"}),e.jsx("th",{children:"Has File"}),e.jsx("th",{children:"Reason"})]})}),e.jsx("tbody",{children:l.map(D=>e.jsxs("tr",{className:D.hasFile?"track-available":"track-missing",children:[e.jsx("td",{"data-label":"#",children:D.trackNumber}),e.jsx("td",{"data-label":"Title",children:D.title}),e.jsx("td",{"data-label":"Duration",children:D.duration?`${Math.floor(D.duration/60)}:${String(D.duration%60).padStart(2,"0")}`:"—"}),e.jsx("td",{"data-label":"Has File",children:e.jsx("span",{className:`track-status ${D.hasFile?"available":"missing"}`,children:D.hasFile?"✓":"✗"})}),e.jsx("td",{"data-label":"Reason",children:Z?e.jsx("span",{className:"table-badge table-badge-reason",children:Z}):e.jsx("span",{className:"table-badge table-badge-reason",children:"Not being searched"})})]},`${G}-${D.id}`))})]})}):e.jsxs("div",{className:"album-info",children:[e.jsxs("p",{children:[e.jsx("strong",{children:"Monitored:"})," ",te?"Yes":"No"," | ",e.jsx("strong",{children:"Has File:"})," ",W?"Yes":"No"]}),e.jsxs("p",{children:[e.jsx("strong",{children:"Reason:"})," ",Z?e.jsx("span",{className:"table-badge table-badge-reason",children:Z}):e.jsx("span",{className:"table-badge table-badge-reason",children:"Not being searched"})]})]})})]},`${g.__instance}-${Q}-${q}`)})})]},`${C.instance}-${C.artist}`))}):u.length?e.jsx("div",{className:"table-wrapper",children:e.jsxs("table",{className:"responsive-table",children:[e.jsx("thead",{children:J.getHeaderGroups().map(C=>e.jsx("tr",{children:C.headers.map(g=>e.jsx("th",{children:g.isPlaceholder?null:Pe(g.column.columnDef.header,g.getContext())},g.id))},C.id))}),e.jsx("tbody",{children:J.getRowModel().rows.map(C=>{const g=C.original,j=`${g.__instance}-${g.artistName}-${g.albumTitle}-${g.trackNumber}`;return e.jsx("tr",{children:C.getVisibleCells().map(q=>e.jsx("td",{"data-label":String(q.column.columnDef.header),children:Pe(q.column.columnDef.cell,q.getContext())},q.id))},j)})})]})}):e.jsx("div",{className:"hint",children:"No tracks found."}),(T?A.length>0:L.length>0)&&e.jsxs("div",{className:"pagination",children:[e.jsx("div",{children:T?e.jsxs(e.Fragment,{children:["Page ",h+1," of ",Math.ceil(K.length/50)," (",K.length," artists · page size 50)"]}):e.jsxs(e.Fragment,{children:["Page ",h+1," of ",Math.ceil(u.length/50)," (",u.length.toLocaleString()," tracks · page size 50)"]})}),e.jsxs("div",{className:"inline",children:[e.jsx("button",{className:"btn",onClick:()=>y(Math.max(0,h-1)),disabled:h===0||r,children:"Prev"}),e.jsx("button",{className:"btn",onClick:()=>y(Math.min(Math.ceil(T?K.length/50:u.length/50)-1,h+1)),disabled:h>=Math.ceil(T?K.length/50:u.length/50)-1||r,children:"Next"})]})]})]})}function Is({loading:r,data:t,page:u,totalPages:h,pageSize:y,allAlbums:v,onlyMissing:F,reasonFilter:N,onPageChange:m,onRestart:T,lastUpdated:n,groupLidarr:M,instances:b,selection:B}){const[K,A]=a.useState(0),L=50,ge=a.useMemo(()=>{let f=v;return F&&(f=f.filter(i=>!i.album?.hasFile)),f},[v,F]),J=a.useMemo(()=>N==="all"?ge:N==="Not being searched"?ge.filter(f=>{const i=f.album;return i?.reason==="Not being searched"||!i?.reason}):ge.filter(f=>f.album?.reason===N),[ge,N]),C=a.useMemo(()=>v.length,[v]),g=N!=="all"||F,j=J.length;a.useMemo(()=>{let f=0;return v.forEach(i=>{const x=i.tracks||[];f+=x.length}),f},[v]),a.useMemo(()=>{let f=0;return J.forEach(i=>{const x=i.tracks||[];f+=x.length}),f},[J]),a.useEffect(()=>{A(0)},[F,N]);const q=a.useRef([]),G=a.useRef([]),Q=a.useMemo(()=>{if(J===q.current)return G.current;const f=new Map;J.forEach(x=>{const I=x.album?.artistName||"Unknown Artist";f.has(I)||f.set(I,[]),f.get(I).push(x)});const i=Array.from(f.entries()).map(([x,R])=>{const $=R[0]?.album?.qualityProfileName??null;return{artist:x,albums:R,qualityProfileName:$}});return q.current=J,G.current=i,i},[J]),le=a.useMemo(()=>[{id:"title",header:"Album",cell:f=>f.row.original.album?.title||"Unknown Album"},{id:"artistName",header:"Artist",cell:f=>f.row.original.album?.artistName||"Unknown Artist",size:150},{id:"releaseDate",header:"Release Date",cell:f=>{const x=f.row.original.album?.releaseDate;return x?new Date(x).toLocaleDateString():e.jsx("span",{className:"hint",children:"—"})},size:120},{id:"monitored",header:"Monitored",cell:f=>{const x=f.row.original.album?.monitored;return e.jsx("span",{className:`track-status ${x?"available":"missing"}`,children:x?"✓":"✗"})},size:100},{id:"hasFile",header:"Has File",cell:f=>{const x=f.row.original.album?.hasFile;return e.jsx("span",{className:`track-status ${x?"available":"missing"}`,children:x?"✓":"✗"})},size:100},{id:"qualityProfileName",header:"Quality Profile",cell:f=>f.row.original.album?.qualityProfileName||"—",size:150},{id:"reason",header:"Reason",cell:f=>{const x=f.row.original.album?.reason;return x?e.jsx("span",{className:"table-badge table-badge-reason",children:x}):e.jsx("span",{className:"table-badge table-badge-reason",children:"Not being searched"})},size:120}],[]),te=Math.max(1,Math.ceil(J.length/L)),W=Math.min(K,Math.max(0,te-1)),Z=a.useMemo(()=>J.slice(W*L,(W+1)*L),[J,W]),l=50,[S,D]=a.useState(0),P=Math.max(1,Math.ceil(Q.length/l)),X=Math.min(S,Math.max(0,P-1)),ie=a.useMemo(()=>Q.slice(X*l,(X+1)*l),[Q,X]);a.useEffect(()=>{D(0)},[F,N]);const ee=ze({data:Z,columns:le,getCoreRowModel:Te(),getSortedRowModel:gs()});return e.jsxs("div",{className:"stack animate-fade-in",children:[e.jsxs("div",{className:"row",style:{justifyContent:"space-between"},children:[e.jsxs("div",{className:"hint",children:[t?.counts?e.jsxs(e.Fragment,{children:[e.jsx("strong",{children:"Available:"})," ",(t.counts.available??0).toLocaleString(void 0,{maximumFractionDigits:0})," •"," ",e.jsx("strong",{children:"Monitored:"})," ",(t.counts.monitored??0).toLocaleString(void 0,{maximumFractionDigits:0})," •"," ",e.jsx("strong",{children:"Missing:"})," ",((t.counts.monitored??0)-(t.counts.available??0)).toLocaleString(void 0,{maximumFractionDigits:0})," •"," ",e.jsx("strong",{children:"Total:"})," ",C.toLocaleString(void 0,{maximumFractionDigits:0}),g&&j<C&&e.jsxs(e.Fragment,{children:[" ","• ",e.jsx("strong",{children:"Filtered:"})," ",j.toLocaleString(void 0,{maximumFractionDigits:0})," of"," ",C.toLocaleString(void 0,{maximumFractionDigits:0})]})]}):"Loading album information...",n?` (updated ${n})`:""]}),e.jsxs("button",{className:"btn ghost",onClick:T,disabled:r,children:[e.jsx(qe,{src:Ue}),"Restart"]})]}),r?e.jsxs("div",{className:"loading",children:[e.jsx("span",{className:"spinner"})," Loading…"]}):M?e.jsx("div",{className:"lidarr-hierarchical-view",children:ie.map(f=>{const i=b.find(x=>x.category===B)?.name||B;return e.jsxs("details",{className:"artist-details",children:[e.jsxs("summary",{className:"artist-summary",children:[e.jsx("span",{className:"artist-title",children:f.artist}),e.jsxs("span",{className:"artist-instance",children:["(",i,")"]}),e.jsxs("span",{className:"artist-count",children:["(",f.albums.length," albums)"]}),f.qualityProfileName?e.jsxs("span",{className:"artist-quality",children:["• ",f.qualityProfileName]}):null]}),e.jsx("div",{className:"artist-content",children:f.albums.map(x=>{const R=x.album,I=R?.title||"Unknown Album",$=R?.id||0,U=R?.artistName||"",he=R?.releaseDate,re=R?.monitored,we=R?.hasFile,ye=R?.reason,Re=x.tracks||[],me=x.totals;return e.jsxs("details",{className:"album-details",children:[e.jsxs("summary",{className:"album-summary",children:[e.jsx("span",{className:"album-title",children:I}),he&&e.jsx("span",{className:"album-date",children:new Date(he).toLocaleDateString()}),Re&&Re.length>0&&e.jsxs("span",{className:"album-track-count",children:["(",me.available||0,"/",me.monitored||Re.length," tracks)"]}),e.jsx("span",{className:`album-status ${we?"has-file":"missing"}`,children:we?"✓":"✗"})]}),e.jsx("div",{className:"album-content",children:Re&&Re.length>0?e.jsx("div",{className:"tracks-table-wrapper",children:e.jsxs("table",{className:"tracks-table",children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{children:"#"}),e.jsx("th",{children:"Title"}),e.jsx("th",{children:"Duration"}),e.jsx("th",{children:"Has File"}),e.jsx("th",{children:"Reason"})]})}),e.jsx("tbody",{children:Re.map(Y=>e.jsxs("tr",{className:Y.hasFile?"track-available":"track-missing",children:[e.jsx("td",{"data-label":"#",children:Y.trackNumber}),e.jsx("td",{"data-label":"Title",children:Y.title}),e.jsx("td",{"data-label":"Duration",children:Y.duration?`${Math.floor(Y.duration/60)}:${String(Y.duration%60).padStart(2,"0")}`:"—"}),e.jsx("td",{"data-label":"Has File",children:e.jsx("span",{className:`track-status ${Y.hasFile?"available":"missing"}`,children:Y.hasFile?"✓":"✗"})}),e.jsx("td",{"data-label":"Reason",children:ye?e.jsx("span",{className:"table-badge table-badge-reason",children:ye}):e.jsx("span",{className:"table-badge table-badge-reason",children:"Not being searched"})})]},`${$}-${Y.id}`))})]})}):e.jsxs("div",{className:"album-info",children:[e.jsxs("p",{children:[e.jsx("strong",{children:"Monitored:"})," ",re?"Yes":"No"," | ",e.jsx("strong",{children:"Has File:"})," ",we?"Yes":"No"]}),e.jsxs("p",{children:[e.jsx("strong",{children:"Reason:"})," ",ye?e.jsx("span",{className:"table-badge table-badge-reason",children:ye}):e.jsx("span",{className:"table-badge table-badge-reason",children:"Not being searched"})]})]})})]},`${U}-${I}`)})})]},f.artist)})}):!M&&v.length?e.jsxs("div",{className:"table-wrapper",children:[e.jsxs("table",{className:"responsive-table",children:[e.jsx("thead",{children:ee.getHeaderGroups().map(f=>e.jsx("tr",{children:f.headers.map(i=>e.jsx("th",{children:i.isPlaceholder?null:Pe(i.column.columnDef.header,i.getContext())},i.id))},f.id))}),e.jsx("tbody",{children:ee.getRowModel().rows.map(f=>{const x=f.original.album,R=x?.title||"Unknown",I=x?.artistName||"Unknown",$=`${R}-${I}`;return e.jsx("tr",{children:f.getVisibleCells().map(U=>e.jsx("td",{"data-label":String(U.column.columnDef.header),children:Pe(U.column.columnDef.cell,U.getContext())},U.id))},$)})})]}),te>1&&e.jsxs("div",{className:"pagination",children:[e.jsxs("div",{children:["Page ",W+1," of ",te," (",J.length.toLocaleString()," albums · page size ",L,")"]}),e.jsxs("div",{className:"inline",children:[e.jsx("button",{className:"btn",onClick:()=>A(Math.max(0,W-1)),disabled:W===0||r,children:"Prev"}),e.jsx("button",{className:"btn",onClick:()=>A(Math.min(te-1,W+1)),disabled:W>=te-1||r,children:"Next"})]})]})]}):e.jsx("div",{className:"hint",children:"No albums found."}),M&&Q.length>0&&e.jsxs("div",{className:"pagination",children:[e.jsxs("div",{children:["Page ",X+1," of ",P," (",Q.length.toLocaleString()," artists · page size ",l,")"]}),e.jsxs("div",{className:"inline",children:[e.jsx("button",{className:"btn",onClick:()=>D(Math.max(0,X-1)),disabled:X===0||r,children:"Prev"}),e.jsx("button",{className:"btn",onClick:()=>D(Math.min(P-1,X+1)),disabled:X>=P-1||r,children:"Next"})]})]})]})}function _s({active:r}){const{push:t}=is(),{value:u,setValue:h,register:y,clearHandler:v}=rs(),{liveArr:F,groupLidarr:N}=os(),[m,T]=a.useState([]),[n,M]=a.useState(""),[b,B]=a.useState(null),[K,A]=a.useState(0),[L,ge]=a.useState(""),[J,C]=a.useState(!1),[g,j]=a.useState(null),[q,G]=a.useState({}),[Q,le]=a.useState(ns),[te,W]=a.useState(1),Z=a.useRef(""),l=a.useRef({}),S=a.useRef(u),D=a.useRef(!1),P=a.useRef(n),X=_e({getKey:d=>{const p=d.album,s=p?.artistName||"",o=p?.title||"";return`${s}-${o}`},hashFields:["album","tracks","totals"]}),[ie,ee]=a.useState([]),[f,i]=a.useState([]),[x,R]=a.useState(!1),[I,$]=a.useState(0),[U,he]=a.useState(""),[re,we]=a.useState(null),ye=_e({getKey:d=>{const p=d.album,s=p?.artistName||"",o=p?.title||"";return`${d.__instance}-${s}-${o}`},hashFields:["__instance","album","tracks","totals"]}),Re=_e({getKey:d=>`${d.__instance}-${d.artistName}-${d.albumTitle}-${d.trackNumber}`,hashFields:["__instance","artistName","albumTitle","trackNumber","title","hasFile","monitored","reason"]}),[me,Y]=a.useState(!1),[fe,Ae]=a.useState("all"),[be,De]=a.useState({available:0,monitored:0,missing:0,total:0}),pe=a.useCallback(async()=>{try{const d=await cs();d.ready===!1&&!D.current?(D.current=!0,t("Lidarr backend is still initialising. Check the logs if this persists.","info")):d.ready&&(D.current=!0);const p=(d.arr||[]).filter(s=>s.type==="lidarr");if(T(p),!p.length){M("aggregate"),B(null),ee([]),De({available:0,monitored:0,missing:0,total:0});return}n===""?M(p.length===1?p[0].category:"aggregate"):n!=="aggregate"&&!p.some(s=>s.category===n)&&M(p[0].category)}catch(d){t(d instanceof Error?d.message:"Unable to load Lidarr instances","error")}},[t,n]),Se=a.useCallback(async(d,p,s,o,c)=>{if(o.length)try{const E=[];for(const _ of o){const O=await es(d,_,s,p),k=O.page??_;if(E.push({page:k,albums:O.albums??[]}),Z.current!==c)return}if(Z.current!==c)return;G(_=>{const O={..._};let k=!1;for(const{page:z,albums:H}of E){const oe=X.syncData(H);oe.hasChanges&&(O[z]=oe.data,k=!0)}return l.current=O,k?O:_})}catch(E){t(E instanceof Error?E.message:`Failed to load additional pages for ${d}`,"error")}},[t]),Ce=a.useCallback(async(d,p,s,o={})=>{const c=o.preloadAll!==!1;(o.showLoading??!0)&&C(!0);try{const _=`${d}::${s}`,O=Z.current!==_;O&&(Z.current=_,G(()=>(l.current={},{})));const k=await es(d,p,ns,s);B(k);const z=k.page??p;A(z),ge(s);const H=k.page_size??ns,oe=k.total??(k.albums??[]).length,w=Math.max(1,Math.ceil((oe||0)/H));le(H),W(w);const se=k.albums??[],ue=O?{}:l.current,xe=X.syncData(se),ae=xe.hasChanges;if(O&&X.reset(),(O||ae)&&(G(V=>{const je={...O?{}:V,[z]:xe.data};return l.current=je,je}),j(new Date().toLocaleTimeString())),c){const V=[];for(let de=0;de<w;de+=1)de!==z&&(ue[de]||V.push(de));Se(d,s,H,V,_)}}catch(_){t(_ instanceof Error?_.message:`Failed to load ${d} albums`,"error")}finally{C(!1)}},[t,Se]),Ne=a.useCallback(async d=>{if(!m.length){ee([]),De({available:0,monitored:0,missing:0,total:0});return}(d?.showLoading??!0)&&R(!0);try{const s=[];let o=0,c=0;for(const w of m){let se=0,ue=!1;const xe=w.name||w.category;for(;se<100;){const ae=await es(w.category,se,xs,"");if(console.log("=== Lidarr API Response ==="),console.log("Instance:",w.category),console.log("Response:",ae),console.log("Albums count:",ae.albums?.length),ae.albums&&ae.albums.length>0&&(console.log("First album entry:",ae.albums[0]),console.log("First album.album:",ae.albums[0].album),console.log("First album.totals:",ae.albums[0].totals),console.log("First album.tracks:",ae.albums[0].tracks)),console.log("========================="),!ue){const de=ae.counts;de&&(o+=de.available??0,c+=de.monitored??0),ue=!0}const V=ae.albums??[];if(V.forEach(de=>{s.push({...de,__instance:xe})}),!V.length||V.length<xs)break;se+=1}}const E=[];s.forEach(w=>{const se=w.album,ue=se?.artistName||"Unknown Artist",xe=se?.title||"Unknown Album",ae=se?.reason,V=se?.qualityProfileId,de=se?.qualityProfileName,je=w.tracks||[];je&&je.length>0&&je.forEach(ce=>{E.push({__instance:w.__instance,artistName:ue,albumTitle:xe,trackNumber:ce.trackNumber||0,title:ce.title||"Unknown Track",duration:ce.duration,hasFile:ce.hasFile||!1,monitored:ce.monitored||!1,reason:ae,qualityProfileId:V,qualityProfileName:de})})});const _=ye.syncData(s),O=_.hasChanges,k=Re.syncData(E),z=k.hasChanges;O&&ee(_.data),z&&i(k.data);const H=N?{available:o,monitored:c,missing:s.length-o,total:s.length}:{available:E.filter(w=>w.hasFile).length,monitored:E.filter(w=>w.monitored).length,missing:E.filter(w=>!w.hasFile).length,total:E.length},oe=be.available!==H.available||be.monitored!==H.monitored||be.missing!==H.missing||be.total!==H.total;oe&&De(H),U!==u&&($(0),he(u)),(O||oe)&&we(new Date().toLocaleTimeString())}catch(s){ee([]),De({available:0,monitored:0,missing:0,total:0}),t(s instanceof Error?s.message:"Failed to load aggregated Lidarr data","error")}finally{R(!1)}},[m,u,t,U,N]);a.useEffect(()=>{r&&pe()},[r,pe]),a.useEffect(()=>{if(!r||!n||n==="aggregate")return;const d=P.current!==n;d&&(l.current={},G({}),W(1),A(0),P.current=n);const p=S.current;Ce(n,d?0:K,p,{preloadAll:!0,showLoading:!0})},[r,n,Ce,K]),a.useEffect(()=>{r&&n==="aggregate"&&Ne()},[r,n,Ne]),Ke(()=>{n==="aggregate"&&F&&Ne({showLoading:!1})},n==="aggregate"&&F?1e3:null),a.useEffect(()=>{if(!r)return;const d=p=>{n==="aggregate"?(he(p),$(0)):n&&(A(0),Ce(n,0,p,{preloadAll:!0,showLoading:!0}))};return y(d),()=>{v(d)}},[r,n,y,v,Ce]),Ke(()=>{if(n&&n!=="aggregate"){if(S.current?.trim?.()||"")return;Ce(n,K,L,{preloadAll:!1,showLoading:!1})}},r&&n&&n!=="aggregate"&&F?1e3:null),a.useEffect(()=>{S.current=u},[u]),a.useEffect(()=>{n==="aggregate"&&he(u)},[n,u]);const Fe=a.useMemo(()=>{let d=ie;if(U){const p=U.toLowerCase();d=d.filter(s=>{const o=s.album,c=(o?.title??"").toString().toLowerCase(),E=(o?.artistName??"").toString().toLowerCase(),_=(s.__instance??"").toLowerCase();return c.includes(p)||E.includes(p)||_.includes(p)})}return me&&(d=d.filter(p=>!p.album?.hasFile)),fe!=="all"&&(fe==="Not being searched"?d=d.filter(p=>{const s=p.album;return s?.reason==="Not being searched"||!s?.reason}):d=d.filter(p=>p.album?.reason===fe)),d},[ie,U,me,fe]),Oe=!!U||fe!=="all",He=a.useMemo(()=>{let d=f;if(U){const p=U.toLowerCase();d=d.filter(s=>s.artistName.toLowerCase().includes(p)||s.albumTitle.toLowerCase().includes(p)||s.title.toLowerCase().includes(p)||s.__instance.toLowerCase().includes(p))}return me&&(d=d.filter(p=>!p.hasFile)),fe!=="all"&&(fe==="Not being searched"?d=d.filter(p=>p.reason==="Not being searched"||!p.reason):d=d.filter(p=>p.reason===fe)),d},[f,U,me,fe]),$e=a.useMemo(()=>{const d=Object.keys(q).map(Number).sort((s,o)=>s-o),p=[];return d.forEach(s=>{q[s]&&p.push(...q[s])}),p},[q]),Ve=a.useMemo(()=>q[K]??[],[q,K]),Qe=a.useCallback(async()=>{if(!(!n||n==="aggregate"))try{await ds(n),t(`Restarted ${n}`,"success")}catch(d){t(d instanceof Error?d.message:`Failed to restart ${n}`,"error")}},[n,t]),We=a.useCallback(d=>{const p=d.target.value||"aggregate";M(p),p!=="aggregate"&&h("")},[M,h]),Ie=n==="aggregate";return e.jsxs("section",{className:"card",children:[e.jsx("div",{className:"card-header",children:"Lidarr"}),e.jsx("div",{className:"card-body",children:e.jsxs("div",{className:"split",children:[e.jsxs("aside",{className:"pane sidebar",children:[m.length>1&&e.jsx("button",{className:`btn ${Ie?"active":""}`,onClick:()=>M("aggregate"),children:"All Lidarr"}),m.map(d=>e.jsx("button",{className:`btn ghost ${n===d.category?"active":""}`,onClick:()=>{M(d.category),h("")},children:d.name||d.category},d.category))]}),e.jsxs("div",{className:"pane",children:[e.jsxs("div",{className:"field mobile-instance-select",children:[e.jsx("label",{children:"Instance"}),e.jsxs("select",{value:n||"aggregate",onChange:We,disabled:!m.length,children:[m.length>1&&e.jsx("option",{value:"aggregate",children:"All Lidarr"}),m.map(d=>e.jsx("option",{value:d.category,children:d.name||d.category},d.category))]})]}),e.jsxs("div",{className:"row",style:{alignItems:"flex-end",gap:"12px",flexWrap:"wrap"},children:[e.jsxs("div",{className:"col field",style:{flex:"1 1 200px"},children:[e.jsx("label",{children:"Search"}),e.jsx("input",{placeholder:"Filter albums",value:u,onChange:d=>h(d.target.value)})]}),e.jsxs("div",{className:"field",style:{flex:"0 0 auto",minWidth:"140px"},children:[e.jsx("label",{children:"Status"}),e.jsxs("select",{onChange:d=>{const p=d.target.value;Y(p==="missing")},value:me?"missing":"all",children:[e.jsx("option",{value:"all",children:"All Albums"}),e.jsx("option",{value:"missing",children:"Missing Only"})]})]}),e.jsxs("div",{className:"field",style:{flex:"0 0 auto",minWidth:"140px"},children:[e.jsx("label",{children:"Search Reason"}),e.jsxs("select",{onChange:d=>Ae(d.target.value),value:fe,children:[e.jsx("option",{value:"all",children:"All Reasons"}),e.jsx("option",{value:"Not being searched",children:"Not Being Searched"}),e.jsx("option",{value:"Missing",children:"Missing"}),e.jsx("option",{value:"Quality",children:"Quality"}),e.jsx("option",{value:"CustomFormat",children:"Custom Format"}),e.jsx("option",{value:"Upgrade",children:"Upgrade"})]})]})]}),Ie?e.jsx(Es,{loading:x,rows:Fe,trackRows:He,page:I,onPageChange:$,onRefresh:()=>void Ne({showLoading:!0}),lastUpdated:re,summary:be,instanceCount:m.length,groupLidarr:N,isAggFiltered:Oe}):e.jsx(Is,{loading:J,data:b,page:K,totalPages:te,pageSize:Q,allAlbums:N?Ve:$e,onlyMissing:me,reasonFilter:fe,onPageChange:d=>{A(d),Ce(n,d,L,{preloadAll:!0})},onRestart:()=>void Qe(),lastUpdated:g,groupLidarr:N,instances:m,selection:n})]})]})})]})}function Us({type:r,active:t}){return r==="radarr"?e.jsx(As,{active:t}):r==="lidarr"?e.jsx(_s,{active:t}):e.jsx($s,{active:t})}export{Us as ArrView};
|
|
2
|
+
//# sourceMappingURL=ArrView.js.map
|