Spaces:
Running
Running
Hermes Agent commited on
Commit ·
d4eac6b
1
Parent(s): e9fd438
fix(ui): hide topbar on startup/login/mismatch, restore portrait poster card, permanently purge duplicate movies, suppress floating play buttons, and structure landscape metadata
Browse files- apex/repair_jellyfin_libraries.py +38 -0
- apex/ui/abyss.css +139 -46
- apex/ui/spotlight-loader.js +47 -30
apex/repair_jellyfin_libraries.py
CHANGED
|
@@ -200,6 +200,44 @@ def repair_database(db_path: str, marker_file: str | None = None, force_reset_pa
|
|
| 200 |
cur.executemany("DELETE FROM BaseItems WHERE Id = ?", [(did,) for did in dup_to_delete])
|
| 201 |
print(f"[REPAIR] Removed {len(dup_to_delete)} duplicate row(s) pointing to identical paths from {db_path}.")
|
| 202 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 203 |
# Check and repair Users table: clear password and login attempt lockouts
|
| 204 |
cur.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='Users'")
|
| 205 |
if cur.fetchone():
|
|
|
|
| 200 |
cur.executemany("DELETE FROM BaseItems WHERE Id = ?", [(did,) for did in dup_to_delete])
|
| 201 |
print(f"[REPAIR] Removed {len(dup_to_delete)} duplicate row(s) pointing to identical paths from {db_path}.")
|
| 202 |
|
| 203 |
+
# Permanent disk + DB deduplication for Movie directories
|
| 204 |
+
movies_dir = "/data/jellyfin/media/Movies"
|
| 205 |
+
if os.path.isdir(movies_dir):
|
| 206 |
+
seen_movie_keys = {}
|
| 207 |
+
for lang in os.listdir(movies_dir):
|
| 208 |
+
lang_path = os.path.join(movies_dir, lang)
|
| 209 |
+
if not os.path.isdir(lang_path):
|
| 210 |
+
continue
|
| 211 |
+
for movie_folder in sorted(os.listdir(lang_path)):
|
| 212 |
+
folder_path = os.path.join(lang_path, movie_folder)
|
| 213 |
+
if not os.path.isdir(folder_path):
|
| 214 |
+
continue
|
| 215 |
+
strm_files = [f for f in os.listdir(folder_path) if f.endswith('.strm')]
|
| 216 |
+
strm_url = None
|
| 217 |
+
if strm_files:
|
| 218 |
+
try:
|
| 219 |
+
with open(os.path.join(folder_path, strm_files[0]), 'r', encoding='utf-8', errors='ignore') as sf:
|
| 220 |
+
strm_url = sf.read().strip()
|
| 221 |
+
except Exception:
|
| 222 |
+
pass
|
| 223 |
+
norm_k = re.sub(r'^(linkz|the)', '', movie_folder, flags=re.I)
|
| 224 |
+
norm_k = re.sub(r'\(\d{4}\)', '', norm_k)
|
| 225 |
+
norm_k = re.sub(r'[^a-zA-Z0-9]', '', norm_k).lower()
|
| 226 |
+
|
| 227 |
+
key = strm_url if strm_url else (norm_k if norm_k else None)
|
| 228 |
+
if key:
|
| 229 |
+
if key in seen_movie_keys:
|
| 230 |
+
existing_folder = seen_movie_keys[key]
|
| 231 |
+
is_curr_worse = 'linkz' in movie_folder.lower() or 'man brand' in movie_folder.lower()
|
| 232 |
+
to_delete = folder_path if is_curr_worse else existing_folder
|
| 233 |
+
to_keep = existing_folder if is_curr_worse else folder_path
|
| 234 |
+
print(f"[REPAIR] Duplicate movie detected: keeping '{to_keep}', deleting duplicate '{to_delete}'")
|
| 235 |
+
shutil.rmtree(to_delete, ignore_errors=True)
|
| 236 |
+
cur.execute("DELETE FROM BaseItems WHERE Path LIKE ?", (to_delete + '%',))
|
| 237 |
+
seen_movie_keys[key] = to_keep
|
| 238 |
+
else:
|
| 239 |
+
seen_movie_keys[key] = folder_path
|
| 240 |
+
|
| 241 |
# Check and repair Users table: clear password and login attempt lockouts
|
| 242 |
cur.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='Users'")
|
| 243 |
if cur.fetchone():
|
apex/ui/abyss.css
CHANGED
|
@@ -3281,19 +3281,12 @@ div[role="main"] {
|
|
| 3281 |
}
|
| 3282 |
|
| 3283 |
|
| 3284 |
-
/*
|
| 3285 |
-
.page:not(#itemDetailPage):not(#videoOsdPage) .btnPlay,
|
| 3286 |
-
.
|
| 3287 |
-
.
|
| 3288 |
-
.
|
| 3289 |
-
.
|
| 3290 |
-
.pageTitleWithDefaultButton .btnShuffle,
|
| 3291 |
-
button[data-action="playall"],
|
| 3292 |
-
button[data-action="shuffleall"],
|
| 3293 |
-
.mainTabsContainer .btnPlay,
|
| 3294 |
-
.mainTabsContainer .btnShuffle,
|
| 3295 |
-
.headerRight .btnPlay,
|
| 3296 |
-
.headerRight .btnShuffle {
|
| 3297 |
display: none !important;
|
| 3298 |
visibility: hidden !important;
|
| 3299 |
width: 0 !important;
|
|
@@ -3301,6 +3294,7 @@ button[data-action="shuffleall"],
|
|
| 3301 |
padding: 0 !important;
|
| 3302 |
margin: 0 !important;
|
| 3303 |
pointer-events: none !important;
|
|
|
|
| 3304 |
}
|
| 3305 |
|
| 3306 |
/* Hidden duplicate cards */
|
|
@@ -4434,14 +4428,26 @@ div[data-category="nextup"] .cardImageContainer img,
|
|
| 4434 |
left: 0 !important;
|
| 4435 |
right: 0 !important;
|
| 4436 |
width: 100% !important;
|
| 4437 |
-
height:
|
| 4438 |
-
display:
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4439 |
align-items: center !important;
|
| 4440 |
justify-content: center !important;
|
| 4441 |
-
pointer-events: none !important;
|
| 4442 |
z-index: 99999 !important;
|
| 4443 |
box-sizing: border-box !important;
|
| 4444 |
padding: 0 16px !important;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4445 |
}
|
| 4446 |
|
| 4447 |
#apex-tv-topbar .apex-tv-tabs {
|
|
@@ -4536,22 +4542,34 @@ div[data-category="nextup"] .cardImageContainer img,
|
|
| 4536 |
}
|
| 4537 |
}
|
| 4538 |
|
| 4539 |
-
/* Never display topbar on splash screen, login screen, server selection, or wizard */
|
| 4540 |
#loginPage ~ #apex-tv-topbar,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4541 |
body:has(#loginPage:not(.hide)) #apex-tv-topbar,
|
| 4542 |
body:has(.loginPage:not(.hide)) #apex-tv-topbar,
|
| 4543 |
body:has(#selectServerPage:not(.hide)) #apex-tv-topbar,
|
|
|
|
| 4544 |
body:has(#forgotPasswordPage:not(.hide)) #apex-tv-topbar,
|
| 4545 |
body:has(.manualLoginForm) #apex-tv-topbar,
|
|
|
|
| 4546 |
body:has(.splashLogo:not(.hide):not(:empty)) #apex-tv-topbar,
|
| 4547 |
-
body.is-logged-out #apex-tv-topbar
|
|
|
|
|
|
|
| 4548 |
display: none !important;
|
| 4549 |
visibility: hidden !important;
|
| 4550 |
opacity: 0 !important;
|
| 4551 |
pointer-events: none !important;
|
| 4552 |
}
|
| 4553 |
|
| 4554 |
-
/* Clean header on login and splash */
|
|
|
|
|
|
|
| 4555 |
#loginPage .skinHeader,
|
| 4556 |
.loginPage .skinHeader,
|
| 4557 |
#selectServerPage .skinHeader,
|
|
@@ -4605,50 +4623,86 @@ header .pageTitle,
|
|
| 4605 |
|
| 4606 |
/* ========================================================
|
| 4607 |
DETAIL PAGE: MOBILE PORTRAIT & DEFAULT CLEAN LAYOUT
|
| 4608 |
-
|
| 4609 |
======================================================== */
|
| 4610 |
@media (orientation: portrait) {
|
| 4611 |
#itemDetailPage .detailRibbon,
|
| 4612 |
.layout-mobile #itemDetailPage .detailRibbon {
|
| 4613 |
display: flex !important;
|
| 4614 |
-
flex-direction:
|
| 4615 |
-
align-items:
|
|
|
|
| 4616 |
width: 100% !important;
|
| 4617 |
max-width: 100% !important;
|
| 4618 |
-
padding: 16px
|
| 4619 |
-
gap:
|
| 4620 |
box-sizing: border-box !important;
|
|
|
|
|
|
|
| 4621 |
}
|
| 4622 |
|
| 4623 |
-
|
| 4624 |
-
|
| 4625 |
-
|
| 4626 |
-
|
| 4627 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4628 |
width: 100% !important;
|
| 4629 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4630 |
}
|
| 4631 |
|
| 4632 |
-
|
| 4633 |
-
.layout-mobile #itemDetailPage .detailImageContainer {
|
| 4634 |
display: none !important;
|
| 4635 |
-
|
| 4636 |
-
|
| 4637 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4638 |
margin: 0 !important;
|
| 4639 |
padding: 0 !important;
|
| 4640 |
-
pointer-events: none !important;
|
| 4641 |
}
|
| 4642 |
|
| 4643 |
#itemDetailPage .nameContainer,
|
| 4644 |
.layout-mobile #itemDetailPage .nameContainer {
|
| 4645 |
display: flex !important;
|
| 4646 |
-
flex-direction:
|
| 4647 |
-
align-items:
|
| 4648 |
-
justify-content:
|
|
|
|
| 4649 |
width: 100% !important;
|
| 4650 |
max-width: 100% !important;
|
| 4651 |
-
margin
|
| 4652 |
}
|
| 4653 |
|
| 4654 |
#itemDetailPage .itemName,
|
|
@@ -4657,14 +4711,29 @@ header .pageTitle,
|
|
| 4657 |
display: block !important;
|
| 4658 |
width: 100% !important;
|
| 4659 |
max-width: 100% !important;
|
| 4660 |
-
font-size: 1.
|
| 4661 |
font-weight: 700 !important;
|
| 4662 |
line-height: 1.25 !important;
|
| 4663 |
color: #ffffff !important;
|
| 4664 |
white-space: normal !important;
|
| 4665 |
word-break: normal !important;
|
| 4666 |
overflow-wrap: break-word !important;
|
| 4667 |
-
letter-spacing:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4668 |
}
|
| 4669 |
|
| 4670 |
#itemDetailPage .itemMiscInfo,
|
|
@@ -4675,7 +4744,9 @@ header .pageTitle,
|
|
| 4675 |
flex-wrap: wrap !important;
|
| 4676 |
gap: 8px !important;
|
| 4677 |
width: 100% !important;
|
| 4678 |
-
margin
|
|
|
|
|
|
|
| 4679 |
}
|
| 4680 |
|
| 4681 |
#itemDetailPage .mainDetailButtons,
|
|
@@ -4683,9 +4754,13 @@ header .pageTitle,
|
|
| 4683 |
display: flex !important;
|
| 4684 |
flex-direction: row !important;
|
| 4685 |
align-items: center !important;
|
| 4686 |
-
flex-wrap:
|
| 4687 |
gap: 10px !important;
|
| 4688 |
width: 100% !important;
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4689 |
}
|
| 4690 |
}
|
| 4691 |
|
|
@@ -4858,13 +4933,31 @@ button:has(svg[data-testid="PeopleIcon"]),
|
|
| 4858 |
}
|
| 4859 |
|
| 4860 |
#itemDetailPage .nameContainer,
|
| 4861 |
-
.layout-mobile #itemDetailPage .nameContainer
|
| 4862 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4863 |
width: 100% !important;
|
| 4864 |
max-width: 100% !important;
|
|
|
|
| 4865 |
margin: 0 0 4px 0 !important;
|
| 4866 |
}
|
| 4867 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4868 |
#itemDetailPage .itemName,
|
| 4869 |
.layout-mobile #itemDetailPage .itemName,
|
| 4870 |
#itemDetailPage h1.itemName {
|
|
|
|
| 3281 |
}
|
| 3282 |
|
| 3283 |
|
| 3284 |
+
/* Universal suppression of floating Play All & Shuffle All buttons outside item detail & video */
|
| 3285 |
+
:is(.view, .page, [data-role="page"], .libraryPage):not(#itemDetailPage):not(#videoOsdPage) :is(.btnPlay, .btnShuffle, button[data-action="playall"], button[data-action="shuffleall"], button[title*="Play all" i], button[title*="Shuffle all" i]),
|
| 3286 |
+
.viewSettings :is(.btnPlay, .btnShuffle, button[data-action="play"], button[data-action="shuffle"]),
|
| 3287 |
+
.pageTitleWithDefaultButton :is(.btnPlay, .btnShuffle),
|
| 3288 |
+
.mainTabsContainer :is(.btnPlay, .btnShuffle),
|
| 3289 |
+
.headerRight :is(.btnPlay, .btnShuffle) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3290 |
display: none !important;
|
| 3291 |
visibility: hidden !important;
|
| 3292 |
width: 0 !important;
|
|
|
|
| 3294 |
padding: 0 !important;
|
| 3295 |
margin: 0 !important;
|
| 3296 |
pointer-events: none !important;
|
| 3297 |
+
opacity: 0 !important;
|
| 3298 |
}
|
| 3299 |
|
| 3300 |
/* Hidden duplicate cards */
|
|
|
|
| 4428 |
left: 0 !important;
|
| 4429 |
right: 0 !important;
|
| 4430 |
width: 100% !important;
|
| 4431 |
+
height: 52px !important;
|
| 4432 |
+
display: none !important;
|
| 4433 |
+
visibility: hidden !important;
|
| 4434 |
+
opacity: 0 !important;
|
| 4435 |
+
pointer-events: none !important;
|
| 4436 |
+
flex-direction: row !important;
|
| 4437 |
align-items: center !important;
|
| 4438 |
justify-content: center !important;
|
|
|
|
| 4439 |
z-index: 99999 !important;
|
| 4440 |
box-sizing: border-box !important;
|
| 4441 |
padding: 0 16px !important;
|
| 4442 |
+
transition: opacity 0.2s ease, visibility 0.2s ease !important;
|
| 4443 |
+
}
|
| 4444 |
+
|
| 4445 |
+
body.show-apex-topbar #apex-tv-topbar,
|
| 4446 |
+
#apex-tv-topbar.is-visible {
|
| 4447 |
+
display: flex !important;
|
| 4448 |
+
visibility: visible !important;
|
| 4449 |
+
opacity: 1 !important;
|
| 4450 |
+
pointer-events: auto !important;
|
| 4451 |
}
|
| 4452 |
|
| 4453 |
#apex-tv-topbar .apex-tv-tabs {
|
|
|
|
| 4542 |
}
|
| 4543 |
}
|
| 4544 |
|
| 4545 |
+
/* Never display topbar on splash screen, login screen, server selection, server mismatch, or wizard */
|
| 4546 |
#loginPage ~ #apex-tv-topbar,
|
| 4547 |
+
#serverMismatchPage ~ #apex-tv-topbar,
|
| 4548 |
+
#selectServerPage ~ #apex-tv-topbar,
|
| 4549 |
+
.serverMismatchPage #apex-tv-topbar,
|
| 4550 |
+
#serverMismatchPage #apex-tv-topbar,
|
| 4551 |
+
body:has(#serverMismatchPage) #apex-tv-topbar,
|
| 4552 |
+
body:has(.serverMismatchPage) #apex-tv-topbar,
|
| 4553 |
body:has(#loginPage:not(.hide)) #apex-tv-topbar,
|
| 4554 |
body:has(.loginPage:not(.hide)) #apex-tv-topbar,
|
| 4555 |
body:has(#selectServerPage:not(.hide)) #apex-tv-topbar,
|
| 4556 |
+
body:has(.selectServerPage:not(.hide)) #apex-tv-topbar,
|
| 4557 |
body:has(#forgotPasswordPage:not(.hide)) #apex-tv-topbar,
|
| 4558 |
body:has(.manualLoginForm) #apex-tv-topbar,
|
| 4559 |
+
body:has(.manualServerContainer) #apex-tv-topbar,
|
| 4560 |
body:has(.splashLogo:not(.hide):not(:empty)) #apex-tv-topbar,
|
| 4561 |
+
body.is-logged-out #apex-tv-topbar,
|
| 4562 |
+
body.hide-apex-topbar #apex-tv-topbar,
|
| 4563 |
+
body.videoPlayerContainerVisible #apex-tv-topbar {
|
| 4564 |
display: none !important;
|
| 4565 |
visibility: hidden !important;
|
| 4566 |
opacity: 0 !important;
|
| 4567 |
pointer-events: none !important;
|
| 4568 |
}
|
| 4569 |
|
| 4570 |
+
/* Clean header on login, mismatch, and splash */
|
| 4571 |
+
#serverMismatchPage .skinHeader,
|
| 4572 |
+
.serverMismatchPage .skinHeader,
|
| 4573 |
#loginPage .skinHeader,
|
| 4574 |
.loginPage .skinHeader,
|
| 4575 |
#selectServerPage .skinHeader,
|
|
|
|
| 4623 |
|
| 4624 |
/* ========================================================
|
| 4625 |
DETAIL PAGE: MOBILE PORTRAIT & DEFAULT CLEAN LAYOUT
|
| 4626 |
+
Clean poster card thumbnail + left-aligned stacked title & metadata
|
| 4627 |
======================================================== */
|
| 4628 |
@media (orientation: portrait) {
|
| 4629 |
#itemDetailPage .detailRibbon,
|
| 4630 |
.layout-mobile #itemDetailPage .detailRibbon {
|
| 4631 |
display: flex !important;
|
| 4632 |
+
flex-direction: row !important;
|
| 4633 |
+
align-items: flex-start !important;
|
| 4634 |
+
flex-wrap: wrap !important;
|
| 4635 |
width: 100% !important;
|
| 4636 |
max-width: 100% !important;
|
| 4637 |
+
padding: 16px 16px 12px 16px !important;
|
| 4638 |
+
gap: 14px !important;
|
| 4639 |
box-sizing: border-box !important;
|
| 4640 |
+
position: relative !important;
|
| 4641 |
+
background: transparent !important;
|
| 4642 |
}
|
| 4643 |
|
| 4644 |
+
/* Fixed Clean Poster Thumbnail in Portrait */
|
| 4645 |
+
#itemDetailPage .detailImageContainer,
|
| 4646 |
+
.layout-mobile #itemDetailPage .detailImageContainer {
|
| 4647 |
+
display: block !important;
|
| 4648 |
+
visibility: visible !important;
|
| 4649 |
+
opacity: 1 !important;
|
| 4650 |
+
position: relative !important;
|
| 4651 |
+
width: 108px !important;
|
| 4652 |
+
min-width: 108px !important;
|
| 4653 |
+
max-width: 108px !important;
|
| 4654 |
+
height: 162px !important;
|
| 4655 |
+
aspect-ratio: 2 / 3 !important;
|
| 4656 |
+
border-radius: 10px !important;
|
| 4657 |
+
overflow: hidden !important;
|
| 4658 |
+
margin: 0 !important;
|
| 4659 |
+
padding: 0 !important;
|
| 4660 |
+
flex-shrink: 0 !important;
|
| 4661 |
+
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.75) !important;
|
| 4662 |
+
border: 1px solid rgba(255, 255, 255, 0.14) !important;
|
| 4663 |
+
}
|
| 4664 |
+
|
| 4665 |
+
#itemDetailPage .detailImageContainer .card,
|
| 4666 |
+
.layout-mobile #itemDetailPage .detailImageContainer .card,
|
| 4667 |
+
#itemDetailPage .detailImageContainer .cardScalable {
|
| 4668 |
width: 100% !important;
|
| 4669 |
+
height: 100% !important;
|
| 4670 |
+
aspect-ratio: 2 / 3 !important;
|
| 4671 |
+
border-radius: 10px !important;
|
| 4672 |
+
overflow: hidden !important;
|
| 4673 |
+
margin: 0 !important;
|
| 4674 |
+
position: relative !important;
|
| 4675 |
+
top: 0 !important;
|
| 4676 |
+
left: 0 !important;
|
| 4677 |
}
|
| 4678 |
|
| 4679 |
+
#itemDetailPage .detailImageContainer .cardPadder {
|
|
|
|
| 4680 |
display: none !important;
|
| 4681 |
+
}
|
| 4682 |
+
|
| 4683 |
+
#itemDetailPage .infoWrapper,
|
| 4684 |
+
.layout-mobile #itemDetailPage .infoWrapper {
|
| 4685 |
+
display: flex !important;
|
| 4686 |
+
flex-direction: column !important;
|
| 4687 |
+
align-items: flex-start !important;
|
| 4688 |
+
justify-content: flex-start !important;
|
| 4689 |
+
flex: 1 1 calc(100% - 128px) !important;
|
| 4690 |
+
min-width: 0 !important;
|
| 4691 |
+
gap: 3px !important;
|
| 4692 |
margin: 0 !important;
|
| 4693 |
padding: 0 !important;
|
|
|
|
| 4694 |
}
|
| 4695 |
|
| 4696 |
#itemDetailPage .nameContainer,
|
| 4697 |
.layout-mobile #itemDetailPage .nameContainer {
|
| 4698 |
display: flex !important;
|
| 4699 |
+
flex-direction: column !important;
|
| 4700 |
+
align-items: flex-start !important;
|
| 4701 |
+
justify-content: flex-start !important;
|
| 4702 |
+
gap: 2px !important;
|
| 4703 |
width: 100% !important;
|
| 4704 |
max-width: 100% !important;
|
| 4705 |
+
margin: 0 0 4px 0 !important;
|
| 4706 |
}
|
| 4707 |
|
| 4708 |
#itemDetailPage .itemName,
|
|
|
|
| 4711 |
display: block !important;
|
| 4712 |
width: 100% !important;
|
| 4713 |
max-width: 100% !important;
|
| 4714 |
+
font-size: 1.45rem !important;
|
| 4715 |
font-weight: 700 !important;
|
| 4716 |
line-height: 1.25 !important;
|
| 4717 |
color: #ffffff !important;
|
| 4718 |
white-space: normal !important;
|
| 4719 |
word-break: normal !important;
|
| 4720 |
overflow-wrap: break-word !important;
|
| 4721 |
+
letter-spacing: -0.01em !important;
|
| 4722 |
+
margin: 0 !important;
|
| 4723 |
+
padding: 0 !important;
|
| 4724 |
+
text-align: left !important;
|
| 4725 |
+
}
|
| 4726 |
+
|
| 4727 |
+
#itemDetailPage .originalTitle,
|
| 4728 |
+
#itemDetailPage .parentName,
|
| 4729 |
+
#itemDetailPage .itemName.originalTitle,
|
| 4730 |
+
#itemDetailPage .nameContainer > *:not(h1) {
|
| 4731 |
+
font-size: 0.95rem !important;
|
| 4732 |
+
font-weight: 500 !important;
|
| 4733 |
+
color: rgba(255, 255, 255, 0.72) !important;
|
| 4734 |
+
margin: 0 !important;
|
| 4735 |
+
padding: 0 !important;
|
| 4736 |
+
text-align: left !important;
|
| 4737 |
}
|
| 4738 |
|
| 4739 |
#itemDetailPage .itemMiscInfo,
|
|
|
|
| 4744 |
flex-wrap: wrap !important;
|
| 4745 |
gap: 8px !important;
|
| 4746 |
width: 100% !important;
|
| 4747 |
+
margin: 2px 0 6px 0 !important;
|
| 4748 |
+
color: rgba(255, 255, 255, 0.72) !important;
|
| 4749 |
+
font-size: 0.85rem !important;
|
| 4750 |
}
|
| 4751 |
|
| 4752 |
#itemDetailPage .mainDetailButtons,
|
|
|
|
| 4754 |
display: flex !important;
|
| 4755 |
flex-direction: row !important;
|
| 4756 |
align-items: center !important;
|
| 4757 |
+
flex-wrap: nowrap !important;
|
| 4758 |
gap: 10px !important;
|
| 4759 |
width: 100% !important;
|
| 4760 |
+
flex-basis: 100% !important;
|
| 4761 |
+
margin-top: 10px !important;
|
| 4762 |
+
overflow-x: auto !important;
|
| 4763 |
+
padding-bottom: 4px !important;
|
| 4764 |
}
|
| 4765 |
}
|
| 4766 |
|
|
|
|
| 4933 |
}
|
| 4934 |
|
| 4935 |
#itemDetailPage .nameContainer,
|
| 4936 |
+
.layout-mobile #itemDetailPage .nameContainer,
|
| 4937 |
+
.layout-desktop #itemDetailPage .nameContainer,
|
| 4938 |
+
.layout-tv #itemDetailPage .nameContainer {
|
| 4939 |
+
display: flex !important;
|
| 4940 |
+
flex-direction: column !important;
|
| 4941 |
+
align-items: flex-start !important;
|
| 4942 |
+
justify-content: flex-start !important;
|
| 4943 |
width: 100% !important;
|
| 4944 |
max-width: 100% !important;
|
| 4945 |
+
gap: 2px !important;
|
| 4946 |
margin: 0 0 4px 0 !important;
|
| 4947 |
}
|
| 4948 |
|
| 4949 |
+
#itemDetailPage .originalTitle,
|
| 4950 |
+
#itemDetailPage .parentName,
|
| 4951 |
+
#itemDetailPage .itemName.originalTitle,
|
| 4952 |
+
#itemDetailPage .nameContainer > *:not(h1) {
|
| 4953 |
+
font-size: 1.1rem !important;
|
| 4954 |
+
font-weight: 500 !important;
|
| 4955 |
+
color: rgba(255, 255, 255, 0.75) !important;
|
| 4956 |
+
margin: 2px 0 0 0 !important;
|
| 4957 |
+
padding: 0 !important;
|
| 4958 |
+
text-align: left !important;
|
| 4959 |
+
}
|
| 4960 |
+
|
| 4961 |
#itemDetailPage .itemName,
|
| 4962 |
.layout-mobile #itemDetailPage .itemName,
|
| 4963 |
#itemDetailPage h1.itemName {
|
apex/ui/spotlight-loader.js
CHANGED
|
@@ -764,31 +764,49 @@ function findHomeTab() {
|
|
| 764 |
|
| 765 |
if (tvTopbar) {
|
| 766 |
var isVideo = !!document.querySelector(".videoPlayerContainer:not(.hide), #videoOsdPage:not(.hide), video");
|
| 767 |
-
var hash = window.location.hash || "";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 768 |
var isHiddenRoute = hash.indexOf("dashboard") !== -1 ||
|
| 769 |
hash.indexOf("settings") !== -1 ||
|
| 770 |
hash.indexOf("login") !== -1 ||
|
| 771 |
hash.indexOf("selectserver") !== -1 ||
|
|
|
|
| 772 |
hash.indexOf("forgotpassword") !== -1 ||
|
| 773 |
hash.indexOf("details") !== -1 ||
|
| 774 |
hash.indexOf("itemdetails") !== -1;
|
| 775 |
|
| 776 |
var isDetailPage = !!document.querySelector("#itemDetailPage:not(.hide), .itemDetailPage:not(.hide)");
|
| 777 |
-
var isAuthPage = !!document.querySelector("#loginPage:not(.hide), .loginPage:not(.hide), #selectServerPage:not(.hide), #forgotPasswordPage:not(.hide), .manualLoginForm, form[data-action='login'], input[type='password']");
|
| 778 |
-
var isSplash = !!document.querySelector(".splashLogo:not(.hide), #splashPage:not(.hide), .splashLogo");
|
| 779 |
-
var hasHomeOrLibrary = !!document.querySelector("#indexPage, .homePage, #homeTab, .pageTabContent, .mainDrawer, .itemsContainer");
|
| 780 |
var isUserLoggedIn = (window.ApiClient && typeof window.ApiClient.isLoggedIn === "function") ? window.ApiClient.isLoggedIn() : (window.ApiClient && !!window.ApiClient.accessToken());
|
| 781 |
|
| 782 |
-
if (isVideo || isHiddenRoute || isDetailPage ||
|
|
|
|
|
|
|
| 783 |
tvTopbar.style.display = "none";
|
| 784 |
tvTopbar.style.visibility = "hidden";
|
| 785 |
return;
|
| 786 |
} else {
|
|
|
|
|
|
|
| 787 |
tvTopbar.style.display = "flex";
|
| 788 |
tvTopbar.style.visibility = "visible";
|
| 789 |
}
|
| 790 |
}
|
| 791 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 792 |
// 2. Hide Hamburger, Cast, SyncPlay, and stray drawer buttons completely
|
| 793 |
var unwanted = document.querySelectorAll(
|
| 794 |
'button[aria-label="Menu"], button[aria-label="menu"], ' +
|
|
@@ -901,31 +919,30 @@ function findHomeTab() {
|
|
| 901 |
|
| 902 |
function deduplicateDomCards() {
|
| 903 |
safe(function () {
|
| 904 |
-
var
|
| 905 |
-
|
| 906 |
-
|
| 907 |
-
|
| 908 |
-
|
| 909 |
-
var
|
| 910 |
-
|
| 911 |
-
|
| 912 |
-
|
| 913 |
-
|
| 914 |
-
|
| 915 |
-
|
| 916 |
-
|
| 917 |
-
|
| 918 |
-
|
| 919 |
-
|
| 920 |
-
|
| 921 |
-
|
| 922 |
-
|
| 923 |
-
|
| 924 |
-
|
| 925 |
-
|
| 926 |
-
|
| 927 |
-
|
| 928 |
-
}
|
| 929 |
}
|
| 930 |
}
|
| 931 |
});
|
|
|
|
| 764 |
|
| 765 |
if (tvTopbar) {
|
| 766 |
var isVideo = !!document.querySelector(".videoPlayerContainer:not(.hide), #videoOsdPage:not(.hide), video");
|
| 767 |
+
var hash = (window.location.hash || "").toLowerCase();
|
| 768 |
+
var isAuthOrStartup = !!document.querySelector(
|
| 769 |
+
"#serverMismatchPage, .serverMismatchPage, #loginPage:not(.hide), .loginPage:not(.hide), " +
|
| 770 |
+
"#selectServerPage:not(.hide), .selectServerPage:not(.hide), #forgotPasswordPage:not(.hide), " +
|
| 771 |
+
".manualLoginForm, .splashLogo:not(.hide), .manualServerContainer, input[type='password']"
|
| 772 |
+
);
|
| 773 |
var isHiddenRoute = hash.indexOf("dashboard") !== -1 ||
|
| 774 |
hash.indexOf("settings") !== -1 ||
|
| 775 |
hash.indexOf("login") !== -1 ||
|
| 776 |
hash.indexOf("selectserver") !== -1 ||
|
| 777 |
+
hash.indexOf("servermismatch") !== -1 ||
|
| 778 |
hash.indexOf("forgotpassword") !== -1 ||
|
| 779 |
hash.indexOf("details") !== -1 ||
|
| 780 |
hash.indexOf("itemdetails") !== -1;
|
| 781 |
|
| 782 |
var isDetailPage = !!document.querySelector("#itemDetailPage:not(.hide), .itemDetailPage:not(.hide)");
|
|
|
|
|
|
|
|
|
|
| 783 |
var isUserLoggedIn = (window.ApiClient && typeof window.ApiClient.isLoggedIn === "function") ? window.ApiClient.isLoggedIn() : (window.ApiClient && !!window.ApiClient.accessToken());
|
| 784 |
|
| 785 |
+
if (isVideo || isHiddenRoute || isDetailPage || isAuthOrStartup || !isUserLoggedIn) {
|
| 786 |
+
tvTopbar.classList.remove("is-visible");
|
| 787 |
+
document.body.classList.remove("show-apex-topbar");
|
| 788 |
tvTopbar.style.display = "none";
|
| 789 |
tvTopbar.style.visibility = "hidden";
|
| 790 |
return;
|
| 791 |
} else {
|
| 792 |
+
tvTopbar.classList.add("is-visible");
|
| 793 |
+
document.body.classList.add("show-apex-topbar");
|
| 794 |
tvTopbar.style.display = "flex";
|
| 795 |
tvTopbar.style.visibility = "visible";
|
| 796 |
}
|
| 797 |
}
|
| 798 |
|
| 799 |
+
// 1.5 Suppress and purge floating Play / Shuffle buttons from catalog/library views
|
| 800 |
+
var strayPlayBtns = document.querySelectorAll(
|
| 801 |
+
':is(.view, .page, [data-role="page"], .libraryPage):not(#itemDetailPage):not(#videoOsdPage) :is(.btnPlay, .btnShuffle, button[data-action="playall"], button[data-action="shuffleall"], button[title*="Play all" i], button[title*="Shuffle all" i]), ' +
|
| 802 |
+
'.viewSettings :is(.btnPlay, .btnShuffle, button[data-action="play"], button[data-action="shuffle"]), ' +
|
| 803 |
+
'.pageTitleWithDefaultButton :is(.btnPlay, .btnShuffle), ' +
|
| 804 |
+
'.mainTabsContainer :is(.btnPlay, .btnShuffle)'
|
| 805 |
+
);
|
| 806 |
+
for (var sp = 0; sp < strayPlayBtns.length; sp++) {
|
| 807 |
+
strayPlayBtns[sp].remove();
|
| 808 |
+
}
|
| 809 |
+
|
| 810 |
// 2. Hide Hamburger, Cast, SyncPlay, and stray drawer buttons completely
|
| 811 |
var unwanted = document.querySelectorAll(
|
| 812 |
'button[aria-label="Menu"], button[aria-label="menu"], ' +
|
|
|
|
| 919 |
|
| 920 |
function deduplicateDomCards() {
|
| 921 |
safe(function () {
|
| 922 |
+
var activePage = document.querySelector('.page:not(.hide), .view:not(.hide), [data-role="page"]:not(.hide)') || document;
|
| 923 |
+
var cards = activePage.querySelectorAll('.card');
|
| 924 |
+
if (!cards || !cards.length) return;
|
| 925 |
+
var seenTitles = {};
|
| 926 |
+
for (var j = 0; j < cards.length; j++) {
|
| 927 |
+
var card = cards[j];
|
| 928 |
+
if (!card.classList || !card.classList.contains("card")) continue;
|
| 929 |
+
if (card.closest('#itemDetailPage') || card.closest('.detailImageContainer')) continue;
|
| 930 |
+
var titleEl = card.querySelector(".cardText-first") || card.querySelector(".cardText:not(.cardText-secondary)") || card.querySelector(".cardTextCentered") || card.querySelector(".cardText");
|
| 931 |
+
var rawTitle = (titleEl ? titleEl.textContent : (card.getAttribute("aria-label") || "")).trim().toLowerCase();
|
| 932 |
+
var normTitle = rawTitle.replace(/\s*\(\s*\d{4}\s*\)\s*/g, "").replace(/^(linkz|the)\b/g, "").replace(/[^a-z0-9]/g, "");
|
| 933 |
+
if (!normTitle) continue;
|
| 934 |
+
if (seenTitles[normTitle]) {
|
| 935 |
+
card.classList.add("apex-duplicate-hidden");
|
| 936 |
+
card.style.setProperty("display", "none", "important");
|
| 937 |
+
card.style.setProperty("visibility", "hidden", "important");
|
| 938 |
+
card.style.setProperty("width", "0px", "important");
|
| 939 |
+
card.style.setProperty("height", "0px", "important");
|
| 940 |
+
card.style.setProperty("flex", "0 0 0px", "important");
|
| 941 |
+
card.style.setProperty("margin", "0px", "important");
|
| 942 |
+
card.style.setProperty("padding", "0px", "important");
|
| 943 |
+
card.remove();
|
| 944 |
+
} else {
|
| 945 |
+
seenTitles[normTitle] = true;
|
|
|
|
| 946 |
}
|
| 947 |
}
|
| 948 |
});
|