init
This commit is contained in:
481
Jellyfin.Plugin.RedditComments/Web/reddit-comments.js
Normal file
481
Jellyfin.Plugin.RedditComments/Web/reddit-comments.js
Normal file
@@ -0,0 +1,481 @@
|
||||
/*
|
||||
* Jellyfin Reddit Comments plugin - web client script.
|
||||
* Adds a "Reddit comments" button to the video player OSD which opens a sidebar
|
||||
* with the episode's Reddit discussion thread. Nothing is fetched until the
|
||||
* button is clicked.
|
||||
*/
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
var BUTTON_ID = 'redditCommentsOsdButton';
|
||||
var SIDEBAR_ID = 'reddit-comments-sidebar';
|
||||
var STYLE_ID = 'reddit-comments-style';
|
||||
var ACCENT = '#a55aea';
|
||||
|
||||
var loadedItemId = null;
|
||||
var sidebarOpen = false;
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// API helpers
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
function getCredentials() {
|
||||
// Primary path: the web client's global ApiClient.
|
||||
if (window.ApiClient && typeof window.ApiClient.getUrl === 'function') {
|
||||
var deviceId = window.ApiClient.deviceId;
|
||||
if (typeof deviceId === 'function') {
|
||||
deviceId = window.ApiClient.deviceId();
|
||||
}
|
||||
return {
|
||||
getUrl: function (path) { return window.ApiClient.getUrl(path); },
|
||||
token: window.ApiClient.accessToken ? window.ApiClient.accessToken() : null,
|
||||
deviceId: deviceId || null
|
||||
};
|
||||
}
|
||||
|
||||
// Fallback: read stored credentials directly.
|
||||
try {
|
||||
var raw = localStorage.getItem('jellyfin_credentials') || localStorage.getItem('jellyfin-credentials');
|
||||
if (!raw) {
|
||||
return null;
|
||||
}
|
||||
var creds = JSON.parse(raw);
|
||||
var server = (creds.Servers || [])[0];
|
||||
if (!server || !server.AccessToken) {
|
||||
return null;
|
||||
}
|
||||
var address = server.ManualAddress || server.LocalAddress;
|
||||
if (!address) {
|
||||
return null;
|
||||
}
|
||||
address = address.replace(/\/$/, '');
|
||||
return {
|
||||
getUrl: function (path) { return address + '/' + path; },
|
||||
token: server.AccessToken,
|
||||
deviceId: localStorage.getItem('_deviceId') || null
|
||||
};
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function apiFetch(path, options) {
|
||||
var creds = getCredentials();
|
||||
if (!creds) {
|
||||
return Promise.reject(new Error('Could not determine Jellyfin API credentials.'));
|
||||
}
|
||||
return fetch(creds.getUrl(path), {
|
||||
method: (options && options.method) || 'GET',
|
||||
headers: { 'X-Emby-Token': creds.token }
|
||||
}).then(function (res) {
|
||||
if (!res.ok) {
|
||||
throw new Error('Request failed with HTTP ' + res.status);
|
||||
}
|
||||
return res.json();
|
||||
});
|
||||
}
|
||||
|
||||
function getCurrentItemId() {
|
||||
return apiFetch('Sessions?activeWithinSeconds=960').then(function (sessions) {
|
||||
var creds = getCredentials();
|
||||
var session = null;
|
||||
if (creds && creds.deviceId) {
|
||||
session = sessions.find(function (s) {
|
||||
return s.DeviceId === creds.deviceId && s.NowPlayingItem;
|
||||
});
|
||||
}
|
||||
if (!session) {
|
||||
session = sessions.find(function (s) { return s.NowPlayingItem; });
|
||||
}
|
||||
return session && session.NowPlayingItem ? session.NowPlayingItem.Id : null;
|
||||
});
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Formatting helpers
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
function timeAgo(date) {
|
||||
var seconds = Math.floor((Date.now() - date.getTime()) / 1000);
|
||||
if (seconds < 60) { return 'just now'; }
|
||||
var minutes = Math.floor(seconds / 60);
|
||||
if (minutes < 60) { return minutes + 'm ago'; }
|
||||
var hours = Math.floor(minutes / 60);
|
||||
if (hours < 24) { return hours + 'h ago'; }
|
||||
var days = Math.floor(hours / 24);
|
||||
if (days < 30) { return days + 'd ago'; }
|
||||
var months = Math.floor(days / 30);
|
||||
if (months < 12) { return months + 'mo ago'; }
|
||||
return Math.floor(months / 12) + 'y ago';
|
||||
}
|
||||
|
||||
function formatScore(score) {
|
||||
if (score >= 1000) {
|
||||
return (score / 1000).toFixed(1).replace(/\.0$/, '') + 'k';
|
||||
}
|
||||
return String(score);
|
||||
}
|
||||
|
||||
function el(tag, className, text) {
|
||||
var node = document.createElement(tag);
|
||||
if (className) { node.className = className; }
|
||||
if (text !== undefined && text !== null) { node.textContent = text; }
|
||||
return node;
|
||||
}
|
||||
|
||||
function svgIcon(pathData, size) {
|
||||
var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
||||
svg.setAttribute('viewBox', '0 0 24 24');
|
||||
svg.setAttribute('width', String(size || 20));
|
||||
svg.setAttribute('height', String(size || 20));
|
||||
svg.setAttribute('fill', 'currentColor');
|
||||
var path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
||||
path.setAttribute('d', pathData);
|
||||
svg.appendChild(path);
|
||||
return svg;
|
||||
}
|
||||
|
||||
var ICONS = {
|
||||
chat: 'M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H6l-2 2V4h16v12z',
|
||||
close: 'M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z',
|
||||
refresh: 'M17.65 6.35C16.2 4.9 14.21 4 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08c-.82 2.33-3.04 4-5.65 4-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z',
|
||||
upvote: 'M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z',
|
||||
external: 'M19 19H5V5h7V3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2v-7h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z'
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Sidebar
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
function injectStyles() {
|
||||
if (document.getElementById(STYLE_ID)) {
|
||||
return;
|
||||
}
|
||||
var style = document.createElement('style');
|
||||
style.id = STYLE_ID;
|
||||
style.textContent = [
|
||||
/* ---- panel ---- */
|
||||
'#' + SIDEBAR_ID + ' { position: fixed; top: 0; right: 0; height: 100%; width: min(440px, 94vw);',
|
||||
' background: #101010; color: #e8e8e8; z-index: 100000; display: flex; flex-direction: column;',
|
||||
' box-shadow: -8px 0 32px rgba(0,0,0,0.65);',
|
||||
' font-family: inherit; font-size: 14px;',
|
||||
' transform: translateX(105%); transition: transform 0.28s cubic-bezier(0.4, 0, 0.2, 1); }',
|
||||
'#' + SIDEBAR_ID + '.rcs-open { transform: translateX(0); }',
|
||||
|
||||
/* ---- header ---- */
|
||||
'#' + SIDEBAR_ID + ' .rcs-header { padding: 14px 16px 12px; background: #181818;',
|
||||
' border-bottom: 1px solid rgba(255,255,255,0.07); display: flex; align-items: flex-start; gap: 6px; }',
|
||||
'#' + SIDEBAR_ID + ' .rcs-title-wrap { flex: 1; min-width: 0; }',
|
||||
'#' + SIDEBAR_ID + ' .rcs-heading { display: flex; align-items: center; gap: 7px; font-weight: 700;',
|
||||
' font-size: 12px; text-transform: uppercase; letter-spacing: 0.09em; color: ' + ACCENT + '; margin-bottom: 7px; }',
|
||||
'#' + SIDEBAR_ID + ' .rcs-thread-link { display: block; color: #fff; font-weight: 600; font-size: 14.5px;',
|
||||
' line-height: 1.35; text-decoration: none; margin-bottom: 8px; word-wrap: break-word; }',
|
||||
'#' + SIDEBAR_ID + ' .rcs-thread-link:hover { color: ' + ACCENT + '; }',
|
||||
'#' + SIDEBAR_ID + ' .rcs-chips { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }',
|
||||
'#' + SIDEBAR_ID + ' .rcs-chip { display: inline-flex; align-items: center; gap: 4px; padding: 3px 10px;',
|
||||
' border-radius: 999px; font-size: 11.5px; font-weight: 600; line-height: 1.4;',
|
||||
' background: rgba(165,90,234,0.14); color: #cf9df5; }',
|
||||
'#' + SIDEBAR_ID + ' .rcs-chip.rcs-chip-muted { background: rgba(255,255,255,0.06); color: #999; font-weight: 500; }',
|
||||
'#' + SIDEBAR_ID + ' .rcs-chip svg { opacity: 0.85; }',
|
||||
|
||||
/* ---- header buttons (jellyfin-style round icon buttons) ---- */
|
||||
'#' + SIDEBAR_ID + ' .rcs-icon-btn { flex-shrink: 0; width: 36px; height: 36px; display: inline-flex;',
|
||||
' align-items: center; justify-content: center; background: transparent; border: none; border-radius: 50%;',
|
||||
' color: #aaa; cursor: pointer; transition: background 0.15s ease, color 0.15s ease; }',
|
||||
'#' + SIDEBAR_ID + ' .rcs-icon-btn:hover { color: #fff; background: rgba(165,90,234,0.18); }',
|
||||
'#' + SIDEBAR_ID + ' .rcs-icon-btn:active { background: rgba(165,90,234,0.3); }',
|
||||
'#' + SIDEBAR_ID + ' .rcs-icon-btn.rcs-spinning svg { animation: rcs-spin 0.9s linear infinite; }',
|
||||
|
||||
/* ---- body ---- */
|
||||
'#' + SIDEBAR_ID + ' .rcs-body { flex: 1; overflow-y: auto; padding: 8px 12px 40px; scrollbar-width: thin;',
|
||||
' scrollbar-color: #333 transparent; }',
|
||||
'#' + SIDEBAR_ID + ' .rcs-body::-webkit-scrollbar { width: 8px; }',
|
||||
'#' + SIDEBAR_ID + ' .rcs-body::-webkit-scrollbar-thumb { background: #2e2e2e; border-radius: 4px; }',
|
||||
'#' + SIDEBAR_ID + ' .rcs-body::-webkit-scrollbar-thumb:hover { background: rgba(165,90,234,0.5); }',
|
||||
|
||||
/* ---- empty / loading states ---- */
|
||||
'#' + SIDEBAR_ID + ' .rcs-state { display: flex; flex-direction: column; align-items: center;',
|
||||
' text-align: center; margin-top: 64px; padding: 0 24px; color: #888; line-height: 1.55; }',
|
||||
'#' + SIDEBAR_ID + ' .rcs-state svg { color: rgba(165,90,234,0.45); margin-bottom: 14px; }',
|
||||
'#' + SIDEBAR_ID + ' .rcs-spinner { margin: 64px auto 0; width: 36px; height: 36px;',
|
||||
' border: 3px solid rgba(165,90,234,0.15); border-top-color: ' + ACCENT + ';',
|
||||
' border-radius: 50%; animation: rcs-spin 0.9s linear infinite; }',
|
||||
'@keyframes rcs-spin { to { transform: rotate(360deg); } }',
|
||||
|
||||
/* ---- comments ---- */
|
||||
'#' + SIDEBAR_ID + ' .rcs-comment { margin: 2px 0; }',
|
||||
'#' + SIDEBAR_ID + ' .rcs-comment-inner { padding: 8px 10px; border-radius: 8px;',
|
||||
' transition: background 0.12s ease; }',
|
||||
'#' + SIDEBAR_ID + ' .rcs-comment-inner:hover { background: rgba(255,255,255,0.035); }',
|
||||
'#' + SIDEBAR_ID + ' .rcs-comment-meta { display: flex; align-items: center; flex-wrap: wrap; gap: 5px;',
|
||||
' font-size: 12px; color: #8b8b8b; margin-bottom: 4px; cursor: pointer; user-select: none; }',
|
||||
'#' + SIDEBAR_ID + ' .rcs-comment-meta .rcs-author { color: #cf9df5; font-weight: 700; }',
|
||||
'#' + SIDEBAR_ID + ' .rcs-comment-meta .rcs-score { display: inline-flex; align-items: center; gap: 1px; color: #b0b0b0; }',
|
||||
'#' + SIDEBAR_ID + ' .rcs-comment-meta .rcs-score svg { color: ' + ACCENT + '; }',
|
||||
'#' + SIDEBAR_ID + ' .rcs-comment-meta .rcs-dot { color: #4a4a4a; }',
|
||||
'#' + SIDEBAR_ID + ' .rcs-comment-meta .rcs-collapse { margin-left: auto; padding: 0 8px; border-radius: 999px;',
|
||||
' font-size: 11px; font-weight: 600; color: #cf9df5; background: rgba(165,90,234,0.12); }',
|
||||
'#' + SIDEBAR_ID + ' .rcs-comment-body { white-space: pre-wrap; word-wrap: break-word;',
|
||||
' overflow-wrap: anywhere; line-height: 1.5; color: #dcdcdc; }',
|
||||
'#' + SIDEBAR_ID + ' .rcs-replies { margin-left: 6px; padding-left: 10px;',
|
||||
' border-left: 2px solid rgba(165,90,234,0.22); }',
|
||||
'#' + SIDEBAR_ID + ' .rcs-replies .rcs-replies { border-left-color: rgba(165,90,234,0.13); }',
|
||||
'#' + SIDEBAR_ID + ' .rcs-collapsed > .rcs-replies,',
|
||||
'#' + SIDEBAR_ID + ' .rcs-collapsed > .rcs-comment-inner > .rcs-comment-body { display: none; }'
|
||||
].join('\n');
|
||||
document.head.appendChild(style);
|
||||
}
|
||||
|
||||
function getSidebar() {
|
||||
var sidebar = document.getElementById(SIDEBAR_ID);
|
||||
if (sidebar) {
|
||||
return sidebar;
|
||||
}
|
||||
|
||||
injectStyles();
|
||||
sidebar = el('div');
|
||||
sidebar.id = SIDEBAR_ID;
|
||||
|
||||
var header = el('div', 'rcs-header');
|
||||
var titleWrap = el('div', 'rcs-title-wrap');
|
||||
|
||||
var heading = el('div', 'rcs-heading');
|
||||
heading.appendChild(svgIcon(ICONS.chat, 15));
|
||||
heading.appendChild(document.createTextNode('Reddit Comments'));
|
||||
titleWrap.appendChild(heading);
|
||||
|
||||
var threadLink = el('a', 'rcs-thread-link');
|
||||
threadLink.target = '_blank';
|
||||
threadLink.rel = 'noopener noreferrer';
|
||||
titleWrap.appendChild(threadLink);
|
||||
titleWrap.appendChild(el('div', 'rcs-chips'));
|
||||
header.appendChild(titleWrap);
|
||||
|
||||
var refreshBtn = el('button', 'rcs-icon-btn rcs-refresh');
|
||||
refreshBtn.title = 'Refresh comments';
|
||||
refreshBtn.appendChild(svgIcon(ICONS.refresh, 19));
|
||||
refreshBtn.addEventListener('click', function () {
|
||||
if (loadedItemId && !refreshBtn.classList.contains('rcs-spinning')) {
|
||||
refreshBtn.classList.add('rcs-spinning');
|
||||
loadComments(loadedItemId, true);
|
||||
}
|
||||
});
|
||||
header.appendChild(refreshBtn);
|
||||
|
||||
var closeBtn = el('button', 'rcs-icon-btn rcs-close');
|
||||
closeBtn.title = 'Close';
|
||||
closeBtn.appendChild(svgIcon(ICONS.close, 19));
|
||||
closeBtn.addEventListener('click', closeSidebar);
|
||||
header.appendChild(closeBtn);
|
||||
|
||||
sidebar.appendChild(header);
|
||||
sidebar.appendChild(el('div', 'rcs-body'));
|
||||
document.body.appendChild(sidebar);
|
||||
return sidebar;
|
||||
}
|
||||
|
||||
function setBodyContent(node) {
|
||||
var body = getSidebar().querySelector('.rcs-body');
|
||||
body.innerHTML = '';
|
||||
body.appendChild(node);
|
||||
}
|
||||
|
||||
function showMessage(text) {
|
||||
var state = el('div', 'rcs-state');
|
||||
state.appendChild(svgIcon(ICONS.chat, 44));
|
||||
state.appendChild(el('div', null, text));
|
||||
setBodyContent(state);
|
||||
}
|
||||
|
||||
function showSpinner() {
|
||||
setBodyContent(el('div', 'rcs-spinner'));
|
||||
}
|
||||
|
||||
function renderComment(comment) {
|
||||
var wrapper = el('div', 'rcs-comment');
|
||||
var inner = el('div', 'rcs-comment-inner');
|
||||
|
||||
var meta = el('div', 'rcs-comment-meta');
|
||||
meta.appendChild(el('span', 'rcs-author', comment.Author));
|
||||
|
||||
meta.appendChild(el('span', 'rcs-dot', '\u2022'));
|
||||
var score = el('span', 'rcs-score');
|
||||
score.appendChild(svgIcon(ICONS.upvote, 13));
|
||||
score.appendChild(document.createTextNode(formatScore(comment.Score)));
|
||||
meta.appendChild(score);
|
||||
|
||||
meta.appendChild(el('span', 'rcs-dot', '\u2022'));
|
||||
meta.appendChild(el('span', null, timeAgo(new Date(comment.CreatedUtc * 1000))));
|
||||
|
||||
inner.appendChild(meta);
|
||||
inner.appendChild(el('div', 'rcs-comment-body', comment.Body));
|
||||
wrapper.appendChild(inner);
|
||||
|
||||
if (comment.Replies && comment.Replies.length > 0) {
|
||||
var collapse = el('span', 'rcs-collapse', '\u2212');
|
||||
meta.appendChild(collapse);
|
||||
|
||||
var replies = el('div', 'rcs-replies');
|
||||
comment.Replies.forEach(function (reply) {
|
||||
replies.appendChild(renderComment(reply));
|
||||
});
|
||||
wrapper.appendChild(replies);
|
||||
|
||||
meta.addEventListener('click', function () {
|
||||
wrapper.classList.toggle('rcs-collapsed');
|
||||
collapse.textContent = wrapper.classList.contains('rcs-collapsed') ? '+' : '\u2212';
|
||||
});
|
||||
}
|
||||
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
function makeChip(text, icon, muted) {
|
||||
var chip = el('span', 'rcs-chip' + (muted ? ' rcs-chip-muted' : ''));
|
||||
if (icon) {
|
||||
chip.appendChild(svgIcon(icon, 12));
|
||||
}
|
||||
chip.appendChild(document.createTextNode(text));
|
||||
return chip;
|
||||
}
|
||||
|
||||
function renderResponse(data) {
|
||||
var sidebar = getSidebar();
|
||||
sidebar.querySelector('.rcs-refresh').classList.remove('rcs-spinning');
|
||||
var threadLink = sidebar.querySelector('.rcs-thread-link');
|
||||
var chips = sidebar.querySelector('.rcs-chips');
|
||||
chips.innerHTML = '';
|
||||
|
||||
if (!data.Found) {
|
||||
threadLink.textContent = data.MediaLabel || '';
|
||||
threadLink.removeAttribute('href');
|
||||
showMessage(data.Message || 'No Reddit discussion thread was found for this title.');
|
||||
return;
|
||||
}
|
||||
|
||||
threadLink.textContent = data.ThreadTitle;
|
||||
threadLink.href = 'https://www.reddit.com' + data.Permalink;
|
||||
|
||||
chips.appendChild(makeChip('r/' + data.Subreddit, null, false));
|
||||
chips.appendChild(makeChip(formatScore(data.ThreadScore) + ' points', ICONS.upvote, false));
|
||||
chips.appendChild(makeChip(data.NumComments + ' comments', ICONS.chat, false));
|
||||
chips.appendChild(makeChip(
|
||||
'fetched ' + timeAgo(new Date(data.FetchedAt)) + (data.Cached ? ' \u2022 cached' : ''),
|
||||
null,
|
||||
true));
|
||||
|
||||
if (!data.Comments || data.Comments.length === 0) {
|
||||
showMessage('The thread was found, but it has no comments to show.');
|
||||
return;
|
||||
}
|
||||
|
||||
var container = el('div');
|
||||
data.Comments.forEach(function (comment) {
|
||||
container.appendChild(renderComment(comment));
|
||||
});
|
||||
setBodyContent(container);
|
||||
}
|
||||
|
||||
function loadComments(itemId, forceRefresh) {
|
||||
showSpinner();
|
||||
var request = forceRefresh
|
||||
? apiFetch('RedditComments/Item/' + itemId + '/Refresh', { method: 'POST' })
|
||||
: apiFetch('RedditComments/Item/' + itemId);
|
||||
|
||||
request.then(function (data) {
|
||||
loadedItemId = itemId;
|
||||
renderResponse(data);
|
||||
}).catch(function (err) {
|
||||
getSidebar().querySelector('.rcs-refresh').classList.remove('rcs-spinning');
|
||||
showMessage('Failed to load Reddit comments: ' + err.message);
|
||||
});
|
||||
}
|
||||
|
||||
function openSidebar() {
|
||||
sidebarOpen = true;
|
||||
getSidebar().classList.add('rcs-open');
|
||||
getCurrentItemId().then(function (itemId) {
|
||||
if (!itemId) {
|
||||
showMessage('Nothing is playing right now.');
|
||||
return;
|
||||
}
|
||||
if (itemId === loadedItemId) {
|
||||
return; // already showing this item
|
||||
}
|
||||
loadComments(itemId, false);
|
||||
}).catch(function (err) {
|
||||
showMessage('Could not determine the currently playing item: ' + err.message);
|
||||
});
|
||||
}
|
||||
|
||||
function closeSidebar() {
|
||||
sidebarOpen = false;
|
||||
var sidebar = document.getElementById(SIDEBAR_ID);
|
||||
if (sidebar) {
|
||||
sidebar.classList.remove('rcs-open');
|
||||
}
|
||||
}
|
||||
|
||||
function toggleSidebar() {
|
||||
if (sidebarOpen) {
|
||||
closeSidebar();
|
||||
} else {
|
||||
openSidebar();
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// OSD button
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
function findOsdControls() {
|
||||
return document.querySelector('.videoOsdBottom .buttons')
|
||||
|| document.querySelector('.videoOsdBottom-maincontrols')
|
||||
|| document.querySelector('.videoOsdBottom');
|
||||
}
|
||||
|
||||
function ensureButton() {
|
||||
if (document.getElementById(BUTTON_ID)) {
|
||||
return;
|
||||
}
|
||||
var controls = findOsdControls();
|
||||
if (!controls) {
|
||||
return;
|
||||
}
|
||||
|
||||
var button = document.createElement('button');
|
||||
button.id = BUTTON_ID;
|
||||
button.type = 'button';
|
||||
button.className = 'paper-icon-button-light autoSize';
|
||||
button.title = 'Reddit comments';
|
||||
var icon = svgIcon(ICONS.chat, 24);
|
||||
icon.style.pointerEvents = 'none';
|
||||
button.appendChild(icon);
|
||||
button.addEventListener('click', function (e) {
|
||||
e.stopPropagation();
|
||||
toggleSidebar();
|
||||
});
|
||||
|
||||
var fullscreenBtn = controls.querySelector('.btnFullscreen');
|
||||
if (fullscreenBtn && fullscreenBtn.parentNode === controls) {
|
||||
controls.insertBefore(button, fullscreenBtn);
|
||||
} else {
|
||||
controls.appendChild(button);
|
||||
}
|
||||
}
|
||||
|
||||
function maybeAutoClose() {
|
||||
// Close the sidebar when playback ends (the video OSD leaves the DOM).
|
||||
if (sidebarOpen && !document.querySelector('.videoOsdBottom')) {
|
||||
closeSidebar();
|
||||
}
|
||||
}
|
||||
|
||||
var observer = new MutationObserver(function () {
|
||||
ensureButton();
|
||||
maybeAutoClose();
|
||||
});
|
||||
|
||||
observer.observe(document.body, { childList: true, subtree: true });
|
||||
ensureButton();
|
||||
})();
|
||||
Reference in New Issue
Block a user