Hermes Agent commited on
Commit
5cabfcf
·
1 Parent(s): d080af5

feat(ui): reset Jellyfin branding and web assets to stock default

Browse files
Dockerfile CHANGED
@@ -39,11 +39,7 @@ RUN python3 -m pip install --upgrade pip --break-system-packages && \
39
  COPY . .
40
  COPY apex /opt/apex
41
 
42
- RUN if [ -f /opt/apex/theme/pitch-black.css ] && [ -d /usr/share/jellyfin/web ]; then \
43
- cp /opt/apex/theme/pitch-black.css /usr/share/jellyfin/web/pitch-black.css \
44
- && sed -i 's|</head>|<link rel="stylesheet" href="pitch-black.css"></head>|' /usr/share/jellyfin/web/index.html; \
45
- fi \
46
- && chmod +x /app/combined-entrypoint.sh /opt/apex/health_doctor.py /opt/apex/combined-entrypoint.sh
47
 
48
  EXPOSE 7860
49
 
 
39
  COPY . .
40
  COPY apex /opt/apex
41
 
42
+ RUN chmod +x /app/combined-entrypoint.sh /opt/apex/health_doctor.py /opt/apex/combined-entrypoint.sh
 
 
 
 
43
 
44
  EXPOSE 7860
45
 
apex/combined-entrypoint.sh CHANGED
@@ -66,21 +66,28 @@ restore_jellyfin_local() {
66
  mkdir -p /data/jellyfin/data/metadata.snapshot
67
  fi
68
 
69
- # Seed and sync Pitch Black OLED dark theme branding
70
- if [ -f "/opt/apex/theme/branding.xml" ]; then
71
- echo "[JELLYFIN] Applying Pitch Black OLED dark theme branding..."
72
- cp -f /opt/apex/theme/branding.xml "$LOCAL_ROOT/config/branding.xml"
73
- mkdir -p /data/jellyfin/config
74
- cp -f /opt/apex/theme/branding.xml /data/jellyfin/config/branding.xml 2>/dev/null || true
75
- fi
76
-
77
- # Pre-inject pitch-black.css into web assets for instant loading
78
- if [ -f "/opt/apex/theme/pitch-black.css" ] && [ -d "/usr/share/jellyfin/web" ]; then
79
- cp -f /opt/apex/theme/pitch-black.css /usr/share/jellyfin/web/pitch-black.css 2>/dev/null || true
80
- if [ -f "/usr/share/jellyfin/web/index.html" ] && ! grep -q "pitch-black.css" /usr/share/jellyfin/web/index.html; then
81
- sed -i 's|</head>|<link rel="stylesheet" href="pitch-black.css"></head>|' /usr/share/jellyfin/web/index.html 2>/dev/null || true
82
- echo "[JELLYFIN] Pitch Black OLED theme injected into web index.html."
 
83
  fi
 
 
 
 
 
 
84
  fi
85
 
86
  echo "[JELLYFIN] Local datadir ready. Metadata redirected to fast local disk."
 
66
  mkdir -p /data/jellyfin/data/metadata.snapshot
67
  fi
68
 
69
+ # Ensure branding is completely reset to stock Jellyfin default
70
+ for b_path in "$LOCAL_ROOT/config/branding.xml" "/data/jellyfin/config/branding.xml"; do
71
+ if [ -f "$b_path" ]; then
72
+ python3 -c "
73
+ import xml.etree.ElementTree as ET
74
+ try:
75
+ tree = ET.parse('$b_path')
76
+ root = tree.getroot()
77
+ css_node = root.find('CustomCss')
78
+ if css_node is not None and css_node.text:
79
+ css_node.text = ''
80
+ tree.write('$b_path', encoding='utf-8', xml_declaration=True)
81
+ except Exception:
82
+ pass
83
+ " 2>/dev/null || true
84
  fi
85
+ done
86
+
87
+ # Clean up any custom css files and restore stock index.html
88
+ rm -f /usr/share/jellyfin/web/netflix-dark.css /usr/share/jellyfin/web/pitch-black.css 2>/dev/null || true
89
+ if [ -f "/usr/share/jellyfin/web/index.html" ]; then
90
+ sed -i 's|<link rel="stylesheet" href="[^"]*css"></head>|</head>|g' /usr/share/jellyfin/web/index.html 2>/dev/null || true
91
  fi
92
 
93
  echo "[JELLYFIN] Local datadir ready. Metadata redirected to fast local disk."
apex/entrypoint.sh CHANGED
@@ -51,21 +51,28 @@ restore_jellyfin_local() {
51
  mkdir -p /data/jellyfin/data/metadata.snapshot
52
  fi
53
 
54
- # Seed and sync Pitch Black OLED dark theme branding
55
- if [ -f "/opt/apex/theme/branding.xml" ]; then
56
- echo "[JELLYFIN] Applying Pitch Black OLED dark theme branding..."
57
- cp -f /opt/apex/theme/branding.xml "$LOCAL_ROOT/config/branding.xml"
58
- mkdir -p /data/jellyfin/config
59
- cp -f /opt/apex/theme/branding.xml /data/jellyfin/config/branding.xml 2>/dev/null || true
60
- fi
61
-
62
- # Pre-inject pitch-black.css into web assets for instant loading
63
- if [ -f "/opt/apex/theme/pitch-black.css" ] && [ -d "/usr/share/jellyfin/web" ]; then
64
- cp -f /opt/apex/theme/pitch-black.css /usr/share/jellyfin/web/pitch-black.css 2>/dev/null || true
65
- if [ -f "/usr/share/jellyfin/web/index.html" ] && ! grep -q "pitch-black.css" /usr/share/jellyfin/web/index.html; then
66
- sed -i 's|</head>|<link rel="stylesheet" href="pitch-black.css"></head>|' /usr/share/jellyfin/web/index.html 2>/dev/null || true
67
- echo "[JELLYFIN] Pitch Black OLED theme injected into web index.html."
 
68
  fi
 
 
 
 
 
 
69
  fi
70
 
71
  echo "[JELLYFIN] Local datadir ready. Metadata redirected to fast local disk."
 
51
  mkdir -p /data/jellyfin/data/metadata.snapshot
52
  fi
53
 
54
+ # Ensure branding is completely reset to stock Jellyfin default
55
+ for b_path in "$LOCAL_ROOT/config/branding.xml" "/data/jellyfin/config/branding.xml"; do
56
+ if [ -f "$b_path" ]; then
57
+ python3 -c "
58
+ import xml.etree.ElementTree as ET
59
+ try:
60
+ tree = ET.parse('$b_path')
61
+ root = tree.getroot()
62
+ css_node = root.find('CustomCss')
63
+ if css_node is not None and css_node.text:
64
+ css_node.text = ''
65
+ tree.write('$b_path', encoding='utf-8', xml_declaration=True)
66
+ except Exception:
67
+ pass
68
+ " 2>/dev/null || true
69
  fi
70
+ done
71
+
72
+ # Clean up any custom css files and restore stock index.html
73
+ rm -f /usr/share/jellyfin/web/netflix-dark.css /usr/share/jellyfin/web/pitch-black.css 2>/dev/null || true
74
+ if [ -f "/usr/share/jellyfin/web/index.html" ]; then
75
+ sed -i 's|<link rel="stylesheet" href="[^"]*css"></head>|</head>|g' /usr/share/jellyfin/web/index.html 2>/dev/null || true
76
  fi
77
 
78
  echo "[JELLYFIN] Local datadir ready. Metadata redirected to fast local disk."
apex/tests/test_pitch_black_theme.py DELETED
@@ -1,31 +0,0 @@
1
- import os
2
- import xml.etree.ElementTree as ET
3
- import pytest
4
-
5
- def test_pitch_black_theme_exists_and_valid():
6
- theme_path = os.path.join(os.path.dirname(__file__), "..", "theme", "pitch-black.css")
7
- assert os.path.exists(theme_path), "pitch-black.css must exist"
8
-
9
- with open(theme_path, "r", encoding="utf-8") as f:
10
- content = f.read()
11
-
12
- # Assert pure pitch black and red accent tokens
13
- assert "#000000" in content, "Must enforce pure pitch black background"
14
- assert "#e50914" in content.lower(), "Must use Netflix red accent"
15
-
16
- # Assert core components styled
17
- assert "#loginPage" in content, "Must style login page in pure black"
18
- assert "backgroundContainer" in content, "Must style background container"
19
- assert "itemProgressBarForeground" in content, "Must style playback progress bar"
20
-
21
- def test_branding_xml_is_valid():
22
- xml_path = os.path.join(os.path.dirname(__file__), "..", "theme", "branding.xml")
23
- assert os.path.exists(xml_path), "branding.xml must exist"
24
-
25
- tree = ET.parse(xml_path)
26
- root = tree.getroot()
27
- assert root.tag == "BrandingOptions"
28
- custom_css = root.find("CustomCss")
29
- assert custom_css is not None
30
- assert "#000000" in (custom_css.text or "")
31
- assert "#e50914" in (custom_css.text or "").lower()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
apex/theme/branding.xml DELETED
@@ -1,154 +0,0 @@
1
- <?xml version="1.0" encoding="utf-8"?>
2
- <BrandingOptions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
3
- <LoginDisclaimer />
4
- <CustomCss><![CDATA[/* ==========================================================================
5
- Apex / Jellyfin — Pitch Black OLED Dark Theme
6
- Clean Original Layout with 100% Pure Pitch Black Surfaces & Netflix Red
7
- ========================================================================== */
8
-
9
- :root,
10
- :root[data-theme] {
11
- --accent: #E50914 !important;
12
- --accent-hover: #b80710 !important;
13
- --accent-glow: rgba(229, 9, 20, 0.4) !important;
14
- --accentColor: #E50914 !important;
15
- --accentHoverColor: #b80710 !important;
16
- --uiAccentColor: #E50914 !important;
17
- --activeColor: #E50914 !important;
18
- --btnSubmitColor: #E50914 !important;
19
- --btnMiniPlayColor: #E50914 !important;
20
- --highlightOutlineColor: #E50914 !important;
21
- --cardResumeProgressColor: #E50914 !important;
22
- --osdSeekBarPlayedColor: #E50914 !important;
23
- --osdSeekBarThumbColor: #E50914 !important;
24
-
25
- --darkerGradientPoint: #000000 !important;
26
- --darkerGradientPointAlpha: rgba(0, 0, 0, 0.95) !important;
27
- --lighterGradientPoint: #050505 !important;
28
- --lighterGradientPointAlpha: rgba(5, 5, 5, 0.95) !important;
29
- --headerColor: rgba(0, 0, 0, 0.92) !important;
30
- --drawerColor: rgba(0, 0, 0, 0.98) !important;
31
- --borderColor: #222222 !important;
32
- --darkerBorderColor: #141414 !important;
33
- --selectorBackgroundColor: #111111 !important;
34
- --tableBodyColor: #050505 !important;
35
- --textareaBackgroundColor: #111111 !important;
36
-
37
- --jf-palette-background-default: #000000 !important;
38
- --jf-palette-background-paper: #0c0c0c !important;
39
- --jf-palette-AppBar-defaultBg: rgba(0, 0, 0, 0.92) !important;
40
- --jf-palette-primary-main: #E50914 !important;
41
- --jf-palette-secondary-main: #E50914 !important;
42
- --jf-palette-action-hover: rgba(229, 9, 20, 0.12) !important;
43
- --jf-palette-action-selected: rgba(229, 9, 20, 0.22) !important;
44
- --jf-palette-action-focus: rgba(229, 9, 20, 0.22) !important;
45
- }
46
-
47
- html,
48
- body,
49
- .backgroundContainer,
50
- .backdropContainer,
51
- .mainDrawer,
52
- .skinHeader,
53
- .page,
54
- .view,
55
- .dialog,
56
- .actionSheet,
57
- .cardBox,
58
- .visualCardBox,
59
- .paperList,
60
- .mainDrawer-scrollContainer,
61
- .dashboardSection,
62
- .emby-tabs-slider,
63
- div#itemDetailPage {
64
- background-color: #000000 !important;
65
- color: #FFFFFF;
66
- }
67
-
68
- .backgroundContainer.withBackdrop {
69
- background-color: rgba(0, 0, 0, 0.88) !important;
70
- }
71
-
72
- .backdropContainer {
73
- background-color: #000000 !important;
74
- }
75
-
76
- #loginPage {
77
- background-color: #000000 !important;
78
- }
79
-
80
- #loginPage .readOnlyContent {
81
- background-color: #0a0a0a !important;
82
- border: 1px solid #222222 !important;
83
- border-radius: 8px !important;
84
- box-shadow: 0 8px 32px rgba(0, 0, 0, 0.9) !important;
85
- }
86
-
87
- #loginPage .emby-input {
88
- background: #161616 !important;
89
- border: 1px solid #2a2a2a !important;
90
- color: #FFFFFF !important;
91
- }
92
-
93
- #loginPage .emby-input:focus {
94
- border-color: #E50914 !important;
95
- }
96
-
97
- #loginPage .raised.button-submit,
98
- #loginPage .MuiButton-containedPrimary {
99
- background-color: #E50914 !important;
100
- color: #FFFFFF !important;
101
- font-weight: 600 !important;
102
- }
103
-
104
- ::selection {
105
- background: #E50914 !important;
106
- color: #FFFFFF !important;
107
- }
108
-
109
- ::-moz-selection {
110
- background: #E50914 !important;
111
- color: #FFFFFF !important;
112
- }
113
-
114
- .emby-button.show-focus:focus,
115
- .paper-icon-button-light.show-focus:focus {
116
- background-color: #E50914 !important;
117
- color: #FFFFFF !important;
118
- }
119
-
120
- .emby-checkbox:checked + span + .checkboxOutline {
121
- background-color: #E50914 !important;
122
- border-color: #E50914 !important;
123
- }
124
-
125
- .itemProgressBarForeground {
126
- background-color: #E50914 !important;
127
- }
128
-
129
- .cardOverlayContainer > .cardOverlayFab-primary,
130
- .listItemImageButton {
131
- background-color: #E50914 !important;
132
- color: #FFFFFF !important;
133
- }
134
-
135
- ::-webkit-scrollbar {
136
- width: 8px;
137
- height: 8px;
138
- }
139
-
140
- ::-webkit-scrollbar-track {
141
- background: #000000;
142
- }
143
-
144
- ::-webkit-scrollbar-thumb {
145
- background: #2a2a2a;
146
- border-radius: 4px;
147
- }
148
-
149
- ::-webkit-scrollbar-thumb:hover {
150
- background: #E50914;
151
- }
152
- ]]></CustomCss>
153
- <SplashscreenEnabled>true</SplashscreenEnabled>
154
- </BrandingOptions>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
apex/theme/pitch-black.css DELETED
@@ -1,163 +0,0 @@
1
- /* ==========================================================================
2
- Apex / Jellyfin — Pitch Black OLED Dark Theme
3
- Clean Original Layout with 100% Pure Pitch Black Surfaces & Netflix Red
4
- ========================================================================== */
5
-
6
- :root,
7
- :root[data-theme] {
8
- /* Design Tokens */
9
- --accent: #E50914 !important;
10
- --accent-hover: #b80710 !important;
11
- --accent-glow: rgba(229, 9, 20, 0.4) !important;
12
- --accentColor: #E50914 !important;
13
- --accentHoverColor: #b80710 !important;
14
- --uiAccentColor: #E50914 !important;
15
- --activeColor: #E50914 !important;
16
- --btnSubmitColor: #E50914 !important;
17
- --btnMiniPlayColor: #E50914 !important;
18
- --highlightOutlineColor: #E50914 !important;
19
- --cardResumeProgressColor: #E50914 !important;
20
- --osdSeekBarPlayedColor: #E50914 !important;
21
- --osdSeekBarThumbColor: #E50914 !important;
22
-
23
- /* Pure OLED Pitch Black Tokens */
24
- --darkerGradientPoint: #000000 !important;
25
- --darkerGradientPointAlpha: rgba(0, 0, 0, 0.95) !important;
26
- --lighterGradientPoint: #050505 !important;
27
- --lighterGradientPointAlpha: rgba(5, 5, 5, 0.95) !important;
28
- --headerColor: rgba(0, 0, 0, 0.92) !important;
29
- --drawerColor: rgba(0, 0, 0, 0.98) !important;
30
- --borderColor: #222222 !important;
31
- --darkerBorderColor: #141414 !important;
32
- --selectorBackgroundColor: #111111 !important;
33
- --tableBodyColor: #050505 !important;
34
- --textareaBackgroundColor: #111111 !important;
35
-
36
- /* Jellyfin 12 MUI Tokens */
37
- --jf-palette-background-default: #000000 !important;
38
- --jf-palette-background-paper: #0c0c0c !important;
39
- --jf-palette-AppBar-defaultBg: rgba(0, 0, 0, 0.92) !important;
40
- --jf-palette-primary-main: #E50914 !important;
41
- --jf-palette-secondary-main: #E50914 !important;
42
- --jf-palette-action-hover: rgba(229, 9, 20, 0.12) !important;
43
- --jf-palette-action-selected: rgba(229, 9, 20, 0.22) !important;
44
- --jf-palette-action-focus: rgba(229, 9, 20, 0.22) !important;
45
- }
46
-
47
- /* --------------------------------------------------------------------------
48
- 1. Pure OLED Pitch Black Backgrounds (#000000) Everywhere
49
- -------------------------------------------------------------------------- */
50
- html,
51
- body,
52
- .backgroundContainer,
53
- .backdropContainer,
54
- .mainDrawer,
55
- .skinHeader,
56
- .page,
57
- .view,
58
- .dialog,
59
- .actionSheet,
60
- .cardBox,
61
- .visualCardBox,
62
- .paperList,
63
- .mainDrawer-scrollContainer,
64
- .dashboardSection,
65
- .emby-tabs-slider,
66
- div#itemDetailPage {
67
- background-color: #000000 !important;
68
- color: #FFFFFF;
69
- }
70
-
71
- .backgroundContainer.withBackdrop {
72
- background-color: rgba(0, 0, 0, 0.88) !important;
73
- }
74
-
75
- .backdropContainer {
76
- background-color: #000000 !important;
77
- }
78
-
79
- /* --------------------------------------------------------------------------
80
- 2. Pure Pitch Black Login Page
81
- -------------------------------------------------------------------------- */
82
- #loginPage {
83
- background-color: #000000 !important;
84
- }
85
-
86
- #loginPage .readOnlyContent {
87
- background-color: #0a0a0a !important;
88
- border: 1px solid #222222 !important;
89
- border-radius: 8px !important;
90
- box-shadow: 0 8px 32px rgba(0, 0, 0, 0.9) !important;
91
- }
92
-
93
- #loginPage .emby-input {
94
- background: #161616 !important;
95
- border: 1px solid #2a2a2a !important;
96
- color: #FFFFFF !important;
97
- }
98
-
99
- #loginPage .emby-input:focus {
100
- border-color: #E50914 !important;
101
- }
102
-
103
- #loginPage .raised.button-submit,
104
- #loginPage .MuiButton-containedPrimary {
105
- background-color: #E50914 !important;
106
- color: #FFFFFF !important;
107
- font-weight: 600 !important;
108
- }
109
-
110
- /* --------------------------------------------------------------------------
111
- 3. Netflix Red Interactive Elements & Focus
112
- -------------------------------------------------------------------------- */
113
- ::selection {
114
- background: #E50914 !important;
115
- color: #FFFFFF !important;
116
- }
117
-
118
- ::-moz-selection {
119
- background: #E50914 !important;
120
- color: #FFFFFF !important;
121
- }
122
-
123
- .emby-button.show-focus:focus,
124
- .paper-icon-button-light.show-focus:focus {
125
- background-color: #E50914 !important;
126
- color: #FFFFFF !important;
127
- }
128
-
129
- .emby-checkbox:checked + span + .checkboxOutline {
130
- background-color: #E50914 !important;
131
- border-color: #E50914 !important;
132
- }
133
-
134
- .itemProgressBarForeground {
135
- background-color: #E50914 !important;
136
- }
137
-
138
- .cardOverlayContainer > .cardOverlayFab-primary,
139
- .listItemImageButton {
140
- background-color: #E50914 !important;
141
- color: #FFFFFF !important;
142
- }
143
-
144
- /* --------------------------------------------------------------------------
145
- 4. Custom Dark Scrollbars
146
- -------------------------------------------------------------------------- */
147
- ::-webkit-scrollbar {
148
- width: 8px;
149
- height: 8px;
150
- }
151
-
152
- ::-webkit-scrollbar-track {
153
- background: #000000;
154
- }
155
-
156
- ::-webkit-scrollbar-thumb {
157
- background: #2a2a2a;
158
- border-radius: 4px;
159
- }
160
-
161
- ::-webkit-scrollbar-thumb:hover {
162
- background: #E50914;
163
- }