initial commit 2
This commit is contained in:
120
banner.js
Normal file
120
banner.js
Normal file
@@ -0,0 +1,120 @@
|
||||
(function () {
|
||||
// =========== README ==========
|
||||
// Update the message below
|
||||
// incrementversion DISMISSED_KEY
|
||||
const DISMISSED_KEY = 'jf-banner-dismissed-v1';
|
||||
const BANNER_ID = 'custom-announcement-banner';
|
||||
|
||||
// Edit these to change banner content:
|
||||
const BANNER_HTML = `
|
||||
<span class="banner-text">
|
||||
<strong>News:</strong>
|
||||
<a href="https://jellyfin.org/downloads/clients/" target="_blank" rel="noopener">
|
||||
Get the official app for your device
|
||||
</a>
|
||||
|
|
||||
<a href="https://google.com/" target="_blank" rel="noopener">
|
||||
Submit requests
|
||||
</a>
|
||||
</span>
|
||||
<button id="custom-banner-close" aria-label="Dismiss banner">✕</button>
|
||||
`;
|
||||
|
||||
const BANNER_CSS = `
|
||||
#custom-announcement-banner {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background: #1c3a5c;
|
||||
color: #e0e0e0;
|
||||
font-size: 0.9em;
|
||||
padding: 8px 16px;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
z-index: 9999;
|
||||
border-bottom: 1px solid #2d5a8e;
|
||||
}
|
||||
#custom-announcement-banner a {
|
||||
color: #7cb8f0;
|
||||
}
|
||||
#custom-banner-close {
|
||||
background: none;
|
||||
border: none;
|
||||
color: #e0e0e0;
|
||||
cursor: pointer;
|
||||
font-size: 1.1em;
|
||||
padding: 0 4px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
function applyOffset() {
|
||||
const banner = document.getElementById(BANNER_ID);
|
||||
const offset = banner ? (banner.offsetHeight + 'px') : '';
|
||||
const header = document.querySelector('.headerTop');
|
||||
const drawer = document.querySelector('.mainDrawer');
|
||||
const sections = document.querySelector('.homeSectionsContainer');
|
||||
if (header) header.style.marginTop = offset;
|
||||
if (drawer) drawer.style.marginTop = offset;
|
||||
if (sections) sections.style.marginTop = offset;
|
||||
}
|
||||
|
||||
function isHomePage() {
|
||||
const h = window.location.hash;
|
||||
return h === '' || h === '#' || h === '#/home' || h === '#!'
|
||||
|| h === '#!/' || h.startsWith('#/home') || h.startsWith('#!/home');
|
||||
}
|
||||
|
||||
function injectBanner() {
|
||||
if (document.getElementById(BANNER_ID)) return;
|
||||
|
||||
const banner = document.createElement('div');
|
||||
banner.id = BANNER_ID;
|
||||
banner.innerHTML = BANNER_HTML;
|
||||
document.body.insertBefore(banner, document.body.firstChild);
|
||||
|
||||
document.getElementById('custom-banner-close').addEventListener('click', function () {
|
||||
sessionStorage.setItem(DISMISSED_KEY, '1');
|
||||
document.getElementById(BANNER_ID).remove();
|
||||
applyOffset();
|
||||
});
|
||||
|
||||
applyOffset();
|
||||
}
|
||||
|
||||
function removeBanner() {
|
||||
const el = document.getElementById(BANNER_ID);
|
||||
if (el) el.remove();
|
||||
applyOffset();
|
||||
}
|
||||
|
||||
function update() {
|
||||
if (sessionStorage.getItem(DISMISSED_KEY)) { removeBanner(); return; }
|
||||
if (isHomePage()) { injectBanner(); } else { removeBanner(); }
|
||||
}
|
||||
|
||||
// Inject CSS once
|
||||
const style = document.createElement('style');
|
||||
style.textContent = BANNER_CSS;
|
||||
document.head.appendChild(style);
|
||||
|
||||
// React to SPA navigation
|
||||
window.addEventListener('hashchange', update);
|
||||
|
||||
// Watch for Jellyfin's deferred body rendering (SPA bootstraps after DOMContentLoaded)
|
||||
// subtree: true catches .headerTop wherever it appears in the DOM
|
||||
const observer = new MutationObserver(function () {
|
||||
update();
|
||||
applyOffset();
|
||||
});
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
observer.observe(document.body, { childList: true, subtree: true });
|
||||
update();
|
||||
});
|
||||
|
||||
// Fallback: run immediately if DOMContentLoaded already fired
|
||||
if (document.readyState !== 'loading') update();
|
||||
})();
|
||||
51
inject-banner.sh
Executable file
51
inject-banner.sh
Executable file
@@ -0,0 +1,51 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
CUSTOM_DIR="/srv/jellyfin/custom"
|
||||
WEB_DIR="/usr/share/jellyfin/web"
|
||||
SRC_JS="${CUSTOM_DIR}/banner.js"
|
||||
INDEX="${WEB_DIR}/index.html"
|
||||
|
||||
if [[ ! -f "${SRC_JS}" ]]; then
|
||||
echo "jellyfin-inject-banner: ${SRC_JS} not found, skipping." >&2
|
||||
exit 0
|
||||
fi
|
||||
|
||||
python3 - "${INDEX}" "${SRC_JS}" <<'EOF'
|
||||
import sys
|
||||
|
||||
index_path, js_path = sys.argv[1], sys.argv[2]
|
||||
|
||||
with open(index_path, 'r') as f:
|
||||
html = f.read()
|
||||
|
||||
with open(js_path, 'r') as f:
|
||||
js = f.read()
|
||||
|
||||
# Remove any previous injection
|
||||
import re
|
||||
html = re.sub(r'<script id="custom-banner-script">.*?</script>', '', html, flags=re.DOTALL)
|
||||
|
||||
# Inject before </body>
|
||||
tag = f'<script id="custom-banner-script">{js}</script>'
|
||||
html = html.replace('</body>', tag + '</body>', 1)
|
||||
|
||||
with open(index_path, 'w') as f:
|
||||
f.write(html)
|
||||
|
||||
print("jellyfin-inject-banner: inlined banner script into " + index_path)
|
||||
EOF
|
||||
|
||||
# Replace the logo image (filename contains a content hash that changes on updates)
|
||||
LOGO_SRC="${CUSTOM_DIR}/banner-light.png"
|
||||
if [[ -f "${LOGO_SRC}" ]]; then
|
||||
LOGO_DEST=$(ls "${WEB_DIR}"/banner-light.*.png 2>/dev/null | head -1)
|
||||
if [[ -n "${LOGO_DEST}" ]]; then
|
||||
cp "${LOGO_SRC}" "${LOGO_DEST}"
|
||||
echo "jellyfin-inject-banner: replaced logo at ${LOGO_DEST}"
|
||||
else
|
||||
echo "jellyfin-inject-banner: no banner-light.*.png found in ${WEB_DIR}, skipping logo." >&2
|
||||
fi
|
||||
else
|
||||
echo "jellyfin-inject-banner: ${LOGO_SRC} not found, skipping logo." >&2
|
||||
fi
|
||||
10
jellyfin-banner.hook
Normal file
10
jellyfin-banner.hook
Normal file
@@ -0,0 +1,10 @@
|
||||
[Trigger]
|
||||
Operation = Install
|
||||
Operation = Upgrade
|
||||
Type = Package
|
||||
Target = jellyfin-web
|
||||
|
||||
[Action]
|
||||
Description = Injecting custom banner into Jellyfin web UI...
|
||||
When = PostTransaction
|
||||
Exec = /srv/jellyfin/custom/inject-banner.sh
|
||||
Reference in New Issue
Block a user