Skip to content

Instantly share code, notes, and snippets.

@raingart
Last active March 12, 2025 22:22
Show Gist options
  • Select an option

  • Save raingart/d734dd35f7090e9824df15f52c36f8a0 to your computer and use it in GitHub Desktop.

Select an option

Save raingart/d734dd35f7090e9824df15f52c36f8a0 to your computer and use it in GitHub Desktop.

Revisions

  1. raingart revised this gist Feb 20, 2025. 1 changed file with 19725 additions and 19317 deletions.
    39,042 changes: 19,725 additions & 19,317 deletions nova-dev.user.js
    19,725 additions, 19,317 deletions not shown because the diff is too large. Please use a local Git client to view these changes.
  2. raingart revised this gist Feb 20, 2025. 1 changed file with 19316 additions and 13735 deletions.
    33,051 changes: 19,316 additions & 13,735 deletions nova-dev.user.js
    19,316 additions, 13,735 deletions not shown because the diff is too large. Please use a local Git client to view these changes.
  3. raingart revised this gist Jan 17, 2025. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion nova-dev.user.js
    Original file line number Diff line number Diff line change
    @@ -7426,7 +7426,7 @@ break;
    }
    function setPlayerFullViewport(exclude_pause) {
    NOVA.css.push(
    `${PLAYER_SELECTOR}.paused-mode${exclude_pause ? `.ytp-progress-bar-hover.${fixOnSeeking}` : ``},
    `${PLAYER_SELECTOR}.paused-mode${exclude_pause ? `.ytp-progress-bar-hover` : ``},
    ${PLAYER_SELECTOR}.playing-mode {
    width: 100vw;
    height: 100vh;
  4. raingart revised this gist Dec 9, 2024. 1 changed file with 3 additions and 5 deletions.
    8 changes: 3 additions & 5 deletions nova-dev.user.js
    Original file line number Diff line number Diff line change
    @@ -2080,8 +2080,7 @@ cursor: not-allowed;
    }
    .${NOVA_REPLIES_SWITCH_CLASS_NAME} input[type=checkbox] [disabled] {
    opacity: var(--disabled-opacity);
    }
    `);
    }`);
    }
    function insertFilterInput(parent_selector_id = required()) {
    if (typeof parent_selector_id !== 'string') {
    @@ -2303,10 +2302,10 @@ opacity: 0;
    #${MODAL_CONTENT_SELECTOR_ID} td a.nova-reply-copy-link::before {
    content: "link";
    }
    #${MODAL_CONTENT_SELECTOR_ID} td:hover > nova-reply-text > a.nova-reply-copy-link {
    #${MODAL_CONTENT_SELECTOR_ID} td:hover > .nova-reply-text > a.nova-reply-copy-link {
    opacity: .3;
    }
    #${MODAL_CONTENT_SELECTOR_ID} td > nova-reply-text > a.nova-reply-copy-link:hover {
    #${MODAL_CONTENT_SELECTOR_ID} td > .nova-reply-text > a.nova-reply-copy-link:hover {
    opacity: 1;
    text-decoration: underline;
    }`);
    @@ -6364,7 +6363,6 @@ display: -webkit-box;
    line-clamp: 5;
    -webkit-box-orient: vertical;
    --background-color: ${COLOR_OSD}50;
    --background-color: ${COLOR_OSD};
    }`);
    const template = document.createElement('div');
    template.id = SELECTOR_ID;
  5. raingart revised this gist Dec 9, 2024. 1 changed file with 26 additions and 36 deletions.
    62 changes: 26 additions & 36 deletions nova-dev.user.js
    Original file line number Diff line number Diff line change
    @@ -1,7 +1,7 @@
    // ==UserScript==
    // @name Nova YouTube
    // @name Nova YouTube beta
    // @namespace yt-nova
    // @version 0.51.0b
    // @version 0.50.0b
    // @description Powerful control on YouTube

    // @author raingart <[email protected]>
    @@ -981,10 +981,7 @@ location.host,
    location.hash,
    channelName,
    playerData?.microformat?.playerMicroformatRenderer.category,
    (
    document.body.querySelector('.ytd-page-manager[video-id]')?.__data.playlistData
    || document.body.querySelector('yt-playlist-manager')?.polymerController?.currentPlaylistData_
    )?.title
    document.body.querySelector('yt-playlist-manager')?.getPlaylistData()?.title
    ]
    .some(i => i?.toUpperCase().includes('MUSIC'))
    || document.body.querySelector('#upload-info #channel-name .badge-style-type-verified-artist')
    @@ -1541,7 +1538,7 @@ NOVA.css.push(
    '.ytp-title-channel-logo',
    '#player .ytp-title-channel',
    'ytm-profile-icon',
    '.ytd-page-manager[video-id]',
    '#page-manager .ytd-page-manager',
    'a.ytd-thumbnail',
    '#search .ytd-searchbox',
    '#ytd-player',
    @@ -6324,10 +6321,6 @@ if (!message) {
    console.error('message must be non empty:', message);
    return Promise.reject(new Error('message must be non empty'));
    }
    if (!Number.isFinite(ui_value)) {
    console.error('ui_value must be a positive number:', ui_value);
    return Promise.reject(new Error('ui_value must be a finite number'));
    }
    if (fade_ms && !Number.isFinite(fade_ms)) {
    console.error('fade_ms must be a positive number:', fade_ms);
    return Promise.reject(new Error('fade_ms must be a finite number'));
    @@ -6531,7 +6524,7 @@ opt_api_key_warn: true,
    desc: 'Some mirrors will partially replace VPNs',
    _runtime: user_settings => {
    const SELECTOR_EMBED = '#movie_player.ytp-embed-error .ytp-error[role="alert"] .ytp-error-content-wrap-subreason:not(:empty)';
    const SELECTOR = `.ytd-page-manager[video-id][player-unavailable] #player-error-message-container #info, ${SELECTOR_EMBED}`;
    const SELECTOR = `#page-manager .ytd-page-manager[player-unavailable] #player-error-message-container #info, ${SELECTOR_EMBED}`;
    NOVA.waitSelector(SELECTOR, { destroy_after_page_leaving: true })
    .then(async container => {
    if (container.querySelector('button')) return;
    @@ -6584,7 +6577,7 @@ ul.append(liAtention);
    container.append(ul);
    }
    });
    NOVA.waitSelector(`.ytd-page-manager[video-id][player-unavailable], ${SELECTOR_EMBED}`, { destroy_after_page_leaving: true })
    NOVA.waitSelector(`#page-manager .ytd-page-manager[player-unavailable], ${SELECTOR_EMBED}`, { destroy_after_page_leaving: true })
    .then(el => {
    if (user_settings.video_unblock_region_domain
    && el.querySelector('yt-player-error-message-renderer #button.yt-player-error-message-renderer button')
    @@ -6923,7 +6916,7 @@ run_on_pages: 'watch, embed, -mobile',
    section: 'player',
    desc: "skip 'The following content may contain suicide or self-harm topics.'",
    _runtime: user_settings => {
    NOVA.waitSelector('.ytd-page-manager[video-id][player-unavailable] #player-error-message-container #info button', { destroy_after_page_leaving: true })
    NOVA.waitSelector('#page-manager .ytd-page-manager[player-unavailable] #player-error-message-container #info button', { destroy_after_page_leaving: true })
    .then(btn => btn.click());
    },
    });
    @@ -7305,7 +7298,7 @@ if (user_settings.player_full_viewport_mode == 'redirect_watch_to_embed') {
    return location.assign(`https://www.youtube.com/embed/` + NOVA.queryURL.get('v'));
    }
    if (user_settings.theater_mode_ignore_playlist && location.search.includes('list=')) return;
    NOVA.waitSelector('.ytd-page-manager[video-id]:not([player-unavailable])')
    NOVA.waitSelector('#page-manager .ytd-page-manager:not([player-unavailable])')
    .then(el => {
    if (isTheaterMode()) return;
    NOVA.waitUntil(() => isTheaterMode() || toggleTheater(), 1000);
    @@ -7338,7 +7331,7 @@ cancelable: false,
    }
    }
    if (!user_settings['video-unblock-warn-content']) {
    NOVA.waitSelector('.ytd-page-manager[video-id][player-unavailable] yt-player-error-message-renderer #button.yt-player-error-message-renderer button', { destroy_after_page_leaving: true })
    NOVA.waitSelector('#page-manager .ytd-page-manager[player-unavailable] yt-player-error-message-renderer #button.yt-player-error-message-renderer button', { destroy_after_page_leaving: true })
    .then(btn => btn.click());
    }
    });
    @@ -7352,7 +7345,7 @@ return;
    NOVA.waitSelector('#movie_player')
    .then(movie_player => {
    const
    PLAYER_CONTAINER_SELECTOR = '.ytd-page-manager[video-id][theater]:not([fullscreen]) #ytd-player',
    PLAYER_CONTAINER_SELECTOR = '#page-manager .ytd-page-manager[theater]:not([fullscreen]) #ytd-player',
    PINNED_SELECTOR = '.nova-player-pin',
    PLAYER_SCROLL_LOCK_CLASS_NAME = 'nova-lock-scroll',
    _PLAYER_SELECTOR = `#movie_player:not(${PINNED_SELECTOR}):not(.${PLAYER_SCROLL_LOCK_CLASS_NAME})`,
    @@ -7369,7 +7362,7 @@ min-height: calc(100vh - ${user_settings['header-compact']
    }) !important;
    }`
    +
    `.ytd-page-manager[video-id][theater]:not([fullscreen]) ${user_settings.player_full_viewport_mode_exit ? `*:has(${_PLAYER_SELECTOR}.playing-mode) ~` : ''} #columns {
    `#page-manager .ytd-page-manager[theater]:not([fullscreen]) ${user_settings.player_full_viewport_mode_exit ? `*:has(${_PLAYER_SELECTOR}.playing-mode) ~` : ''} #columns {
    position: absolute;
    top: 100vh;
    }
    @@ -7446,13 +7439,13 @@ background-color: black;
    }`);
    if (CSS.supports('selector(:has(*))')) {
    NOVA.css.push(
    `#masthead-container:has( ~ #page-manager .ytd-page-manager[video-id][theater]) {
    `#masthead-container:has( ~ #page-manager #page-manager .ytd-page-manager[theater]) {
    position: fixed;
    z-index: ${zIndex + 1};
    opacity: 0;
    }
    #masthead-container:has( ~ #page-manager .ytd-page-manager[video-id][theater]):hover,
    #masthead-container:has( ~ #page-manager .ytd-page-manager[video-id][theater]):focus {
    #masthead-container:has( ~ #page-manager #page-manager .ytd-page-manager[theater]):hover,
    #masthead-container:has( ~ #page-manager #page-manager .ytd-page-manager[theater]):focus {
    opacity: 1;
    }`);
    }
    @@ -8217,7 +8210,7 @@ if (user_settings['player-control-below']) return;
    let selectorHover, selectorGradientHide;
    switch (user_settings.player_control_autohide_container) {
    case 'player':
    selectorHover = '.ytd-page-manager[video-id]:not([fullscreen]) #movie_player:hover .ytp-chrome-bottom';
    selectorHover = '#page-manager .ytd-page-manager:not([fullscreen]) #movie_player:hover .ytp-chrome-bottom';
    selectorGradientHide = '#movie_player:not(:hover) .ytp-gradient-bottom';
    NOVA.waitSelector('#ytd-player')
    .then(movie_player => {
    @@ -8306,7 +8299,7 @@ if ((heightPanel = NOVA.css.get(control_panel, 'height'))
    && (heightProgressBar = NOVA.css.get('.ytp-progress-bar-container', 'height'))
    ) {
    const height = `calc(${heightPanel} + ${heightProgressBar})` || '51px';
    let SELECTOR_CONTAINER = '.ytd-page-manager[video-id]:not([fullscreen])';
    let SELECTOR_CONTAINER = '#page-manager .ytd-page-manager:not([fullscreen])';
    if (['force', 'offset'].includes(user_settings.player_full_viewport_mode)) {
    SELECTOR_CONTAINER += `:not([theater])`;
    }
    @@ -11536,8 +11529,8 @@ const vids_list = (document.body.querySelector('ytd-app')?.data?.response || win
    ?.tabs[0].tabRenderer?.content?.sectionListRenderer
    ?.contents[0].itemSectionRenderer
    ?.contents[0].playlistVideoListRenderer?.contents
    || document.body.querySelector('.ytd-page-manager[video-id]')?.__data.playlistData?.contents
    || document.body.querySelector('.ytd-page-manager[video-id]')?.data?.playlist?.playlist?.contents;
    || document.body.querySelector('#page-manager .ytd-page-manager')?.__data.playlistData?.contents
    || document.body.querySelector('#page-manager .ytd-page-manager')?.data?.playlist?.playlist?.contents;
    const duration = vids_list?.reduce((acc, vid) => acc + (+vid.playlistVideoRenderer?.lengthSeconds || 0), 0);
    if (duration) {
    return outFormat(duration);
    @@ -11550,8 +11543,9 @@ NOVA.waitSelector('#page-manager #playlist .index-message-wrapper:not([hidden])'
    .then(el => {
    const waitPlaylist = setInterval(() => {
    const
    playlistLength = movie_player.getPlaylist()?.length,
    playlistList = document.body.querySelector('yt-playlist-manager')?.currentPlaylistData_?.contents
    manager = document.body.querySelector('yt-playlist-manager')?.getPlaylistData(),
    playlistLength = movie_player.getPlaylist()?.length || manager?.totalVideos,
    playlistList = manager?.contents
    .filter(e => e.playlistPanelVideoRenderer?.lengthText?.simpleText)
    .map(e => NOVA.formatTime.hmsToSec(e.playlistPanelVideoRenderer.lengthText.simpleText));
    console.assert(playlistList?.length === playlistLength, 'playlist loading:', playlistList?.length + '/' + playlistLength);
    @@ -11741,7 +11735,7 @@ document.addEventListener('yt-page-data-updated', insertButton, { capture: true,
    }
    });
    function insertButton() {
    NOVA.waitSelector('.ytd-page-manager[video-id] #playlist #playlist-action-menu .top-level-buttons:not([hidden]), #secondary #playlist #playlist-action-menu #top-level-buttons-computed', { destroy_after_page_leaving: true })
    NOVA.waitSelector('#page-manager .ytd-page-manager #playlist #playlist-action-menu .top-level-buttons:not([hidden]), #secondary #playlist #playlist-action-menu #top-level-buttons-computed', { destroy_after_page_leaving: true })
    .then(el => createButton(el));
    async function createButton(container = required()) {
    if (!(container instanceof HTMLElement)) {
    @@ -11795,7 +11789,7 @@ autoplayBtn.title = 'out of reach';
    }
    async function reverseControl() {
    if (!window.nova_playlistReversed) return;
    if ((ytdWatch = await NOVA.waitSelector('.ytd-page-manager[video-id]', { destroy_after_page_leaving: true }))
    if ((ytdWatch = await NOVA.waitSelector('#page-manager .ytd-page-manager', { destroy_after_page_leaving: true }))
    && (data = await NOVA.waitUntil(() => ytdWatch.data?.contents?.twoColumnWatchNextResults, 100))
    && (playlist = data.playlist?.playlist)
    && (autoplay = data.autoplay?.autoplay)
    @@ -11899,7 +11893,7 @@ insertButton();
    }
    });
    function insertButton() {
    NOVA.waitSelector('.ytd-page-manager[video-id]:not([hidden]) ytd-playlist-panel-renderer:not([collapsed]) #playlist-action-menu .top-level-buttons:not([hidden]), #secondary #playlist #playlist-action-menu #top-level-buttons-computed', { destroy_after_page_leaving: true })
    NOVA.waitSelector('#page-manager .ytd-page-manager:not([hidden]) ytd-playlist-panel-renderer:not([collapsed]) #playlist-action-menu .top-level-buttons:not([hidden]), #secondary #playlist #playlist-action-menu #top-level-buttons-computed', { destroy_after_page_leaving: true })
    .then(el => insertrCheckbox(el));
    function insertrCheckbox(container = required()) {
    if (!(container instanceof HTMLElement)) {
    @@ -11928,7 +11922,7 @@ if (autoplayCheckbox.checked) checkHiddenVideo();
    }
    else console.error('Error playlist-autoplay. Playlist manager:', manager);
    async function checkHiddenVideo() {
    const watchManager = document.body.querySelector('.ytd-page-manager[video-id]');
    const watchManager = document.body.querySelector('#page-manager .ytd-page-manager');
    let playlistItems;
    await NOVA.waitUntil(() => (playlistItems = getPlaylistContents(watchManager)) && playlistItems.length, 1000);
    const
    @@ -11975,7 +11969,7 @@ section: 'sidebar',
    _runtime: user_settings => {
    if (user_settings.move_to_sidebar_target != 'info' && location.search.includes('list=')) return;
    const
    SELECTOR_CONTAINER = '.ytd-page-manager[video-id]:not([fullscreen])',
    SELECTOR_CONTAINER = '#page-manager .ytd-page-manager:not([fullscreen])',
    SELECTOR_BELOW = `${SELECTOR_CONTAINER} #below`,
    SELECTOR_SECONDARY = `${SELECTOR_CONTAINER} #secondary`;
    switch (user_settings.move_to_sidebar_target) {
    @@ -12206,7 +12200,6 @@ requestAnimationFrame(hideThumb);
    });
    document.addEventListener('visibilitychange', () => !document.hidden && hideThumb());
    document.addEventListener('yt-action', evt => {
    console.debug(evt.detail?.actionName);
    switch (evt.detail?.actionName) {
    case 'yt-append-continuation-items-action':
    case 'ytd-update-grid-state-action':
    @@ -12384,7 +12377,6 @@ NOVA.css.push(
    display: none !important;
    }`);
    }
    hideThumb();
    },
    options: {
    thumbs_hide_shorts: {
    @@ -12915,7 +12907,6 @@ if (video_title.textContent.trim().toLowerCase().includes(keyword)
    });
    }
    else {
    hideThumb();
    document.addEventListener('scroll', () => {
    requestAnimationFrame(hideThumb);
    });
    @@ -13225,7 +13216,6 @@ thumb.remove();
    });
    }
    else {
    hideThumb();
    document.addEventListener('scroll', () => {
    requestAnimationFrame(hideThumb);
    });
  6. raingart revised this gist Dec 9, 2024. 1 changed file with 10176 additions and 9188 deletions.
    19,364 changes: 10,176 additions & 9,188 deletions nova-dev.user.js
    10,176 additions, 9,188 deletions not shown because the diff is too large. Please use a local Git client to view these changes.
  7. raingart revised this gist Aug 8, 2024. 1 changed file with 14 additions and 1 deletion.
    15 changes: 14 additions & 1 deletion nova-dev.user.js
    Original file line number Diff line number Diff line change
    @@ -9553,7 +9553,7 @@ return;
    }
    if (user_settings.player_full_viewport_mode == 'redirect_watch_to_embed') return;
    if (user_settings['player-fullscreen-mode']) return;
    if (window.innerWidth > 720 && window.innerHeight > 480) return;
    if (window.innerWidth > (+user_settings.embed_popup_min_width || 720) && window.innerHeight > 480) return;
    NOVA.waitSelector('#movie_player video')
    .then(video => {
    video.addEventListener('playing', createPopup.bind(video), { capture: true, once: true });
    @@ -9576,6 +9576,19 @@ url.searchParams.set('popup', true);
    NOVA.openPopup({ 'url': url.href, 'width': width, 'height': height });
    }
    },
    options: {
    embed_popup_min_width: {
    _tagName: 'input',
    label: 'Min iframe width',
    type: 'number',
    title: 'in px',
    placeholder: '300-900',
    step: 5,
    min: 300,
    max: 900,
    value: 720,
    },
    }
    });
    window.nova_plugins.push({
    id: 'theater-mode',
  8. raingart revised this gist Aug 8, 2024. 1 changed file with 6 additions and 4 deletions.
    10 changes: 6 additions & 4 deletions nova-dev.user.js
    Original file line number Diff line number Diff line change
    @@ -327,7 +327,9 @@ btn.style.display = 'none';
    }
    });
    function prepareModal() {
    document.getElementById(MODAL_CONTENT_SELECTOR_ID).innerHTML = NOVA.createSafeHTML('<pre>Loading data...</pre>');
    const pre = document.createElement('pre');
    pre.textContent = 'Loading data...';
    document.getElementById(MODAL_CONTENT_SELECTOR_ID)?.append(pre);
    }
    });
    function getParentSelector() {
    @@ -7430,13 +7432,13 @@ sliderCheckbox.title = 'Remember speed';
    const out = {};
    const right = document.createElement('div');
    right.className = 'ytp-menuitem-content';
    out.sliderCheckbox = right.append(sliderCheckbox);
    out.slider = right.append(slider);
    out.sliderCheckbox = right.appendChild(sliderCheckbox);
    out.slider = right.appendChild(slider);
    const speedMenu = document.createElement('div');
    speedMenu.className = 'ytp-menuitem';
    speedMenu.id = SELECTOR_ID;
    speedMenu.append(sliderIcon);
    out.sliderLabel = speedMenu.append(sliderLabel);
    out.sliderLabel = speedMenu.appendChild(sliderLabel);
    speedMenu.append(right);
    document.body.querySelector('.ytp-panel-menu')
    ?.append(speedMenu);
  9. raingart revised this gist Aug 8, 2024. 1 changed file with 203 additions and 283 deletions.
    486 changes: 203 additions & 283 deletions nova-dev.user.js
    Original file line number Diff line number Diff line change
    @@ -290,8 +290,7 @@ const btn = document.createElement('span');
    btn.className = BTN_CLASS_NAME;
    btn.setAttribute('data-open-modal', MODAL_NAME_SELECTOR_ID);
    btn.title = 'Nova comments';
    btn.appendChild(createSvgIcon());
    function createSvgIcon() {
    btn.append((function createSvgIcon() {
    const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
    svg.setAttribute('width', '100%');
    svg.setAttribute('height', '100%');
    @@ -300,10 +299,10 @@ const g = document.createElementNS('http://www.w3.org/2000/svg', 'g');
    g.setAttribute('fill', 'currentColor');
    const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
    path.setAttribute('d', 'M30.28,110.09,49.37,91.78A3.84,3.84,0,0,1,52,90.72h60a2.15,2.15,0,0,0,2.16-2.16V9.82a2.16,2.16,0,0,0-.64-1.52A2.19,2.19,0,0,0,112,7.66H9.82A2.24,2.24,0,0,0,7.65,9.82V88.55a2.19,2.19,0,0,0,2.17,2.16H26.46a3.83,3.83,0,0,1,3.82,3.83v15.55ZM28.45,63.56a3.83,3.83,0,1,1,0-7.66h53a3.83,3.83,0,0,1,0,7.66Zm0-24.86a3.83,3.83,0,1,1,0-7.65h65a3.83,3.83,0,0,1,0,7.65ZM53.54,98.36,29.27,121.64a3.82,3.82,0,0,1-6.64-2.59V98.36H9.82A9.87,9.87,0,0,1,0,88.55V9.82A9.9,9.9,0,0,1,9.82,0H112a9.87,9.87,0,0,1,9.82,9.82V88.55A9.85,9.85,0,0,1,112,98.36Z');
    g.appendChild(path);
    svg.appendChild(g);
    g.append(path);
    svg.append(g);
    return svg;
    }
    })());
    btn.addEventListener('click', evt => {
    evt.preventDefault();
    evt.stopPropagation();
    @@ -328,9 +327,7 @@ btn.style.display = 'none';
    }
    });
    function prepareModal() {
    const pre = document.createElement('pre');
    pre.textContent = 'Loading data...';
    document.getElementById(MODAL_CONTENT_SELECTOR_ID).appendChild(pre);
    document.getElementById(MODAL_CONTENT_SELECTOR_ID).innerHTML = NOVA.createSafeHTML('<pre>Loading data...</pre>');
    }
    });
    function getParentSelector() {
    @@ -382,9 +379,9 @@ document.getElementById(MODAL_NAME_SELECTOR_ID)
    return alert(`Error [${res.code}]: ${res.reason}`);
    }
    else {
    return document.getElementById(MODAL_CONTENT_SELECTOR_ID).innerHTML =
    return document.getElementById(MODAL_CONTENT_SELECTOR_ID).innerHTML = NOVA.createSafeHTML(
    `<pre>Error [${res.code}]: ${res.reason}</pre>
    <pre>${res.error}</pre>`;
    <pre>${res.error}</pre>`);
    }
    }
    res?.items?.forEach(item => {
    @@ -406,7 +403,7 @@ if (commentList.length >= MAX_COMMENTS) {
    genTable();
    }
    else if (res?.nextPageToken) {
    document.getElementById(MODAL_CONTENT_SELECTOR_ID).innerHTML = `<pre>Loading: ${commentList.length + (user_settings['user-api-key'] ? '' : '/' + MAX_COMMENTS)}</pre>`;
    document.getElementById(MODAL_CONTENT_SELECTOR_ID).innerHTML = NOVA.createSafeHTML(`<pre>Loading: ${commentList.length + (user_settings['user-api-key'] ? '' : '/' + MAX_COMMENTS)}</pre>`);
    getComments(res?.nextPageToken);
    }
    else {
    @@ -416,7 +413,7 @@ genTable();
    }
    function genTable() {
    if (!commentList.length) {
    return document.getElementById(MODAL_CONTENT_SELECTOR_ID).innerHTML = `<pre>Comments empty</pre>`;
    return document.getElementById(MODAL_CONTENT_SELECTOR_ID).innerHTML = NOVA.createSafeHTML(`<pre>Comments empty</pre>`);
    }
    const ul = document.createElement('tbody');
    const channelName = (href = document.body.querySelector('#owner #upload-info #channel-name a[href]')?.href) && new URL(href).pathname;
    @@ -435,68 +432,23 @@ li = document.createElement('tr');
    let replyCount = 0;
    li.className = 'item';
    if (channelName && comment.authorChannelUrl.includes(channelName)) li.classList.add('author');
    function createCommentCell(text, sortKey) {
    const td = document.createElement('td');
    td.textContent = text;
    if (sortKey) {
    td.setAttribute('sorttable_customkey', sortKey);
    }
    return td;
    }
    function createCommentLinkCell(comment, replyInputName) {
    const td = document.createElement('td');
    td.classList.add(NOVA_REPLYS_SWITCH_CLASS_NAME);
    if (comment.comments?.length) {
    const link = document.createElement('a');
    link.href = `https://www.youtube.com/watch?v=${comment.videoId}&lc=${comment.id}`;
    link.target = '_blank';
    link.title = 'Open comment link';
    link.textContent = comment.totalReplyCount;
    const label = document.createElement('label');
    label.htmlFor = replyInputName;
    td.appendChild(link);
    td.appendChild(label);
    }
    return td;
    }
    function createCommentDateCell(comment) {
    const td = document.createElement('td');
    td.setAttribute('sorttable_customkey', new Date(comment.publishedAt).getTime());
    td.textContent = NOVA.formatTimeOut.ago(new Date(comment.publishedAt));
    return td;
    }
    function createCommentAuthorCell(comment) {
    const td = document.createElement('td');
    const link = document.createElement('a');
    link.href = comment.authorChannelUrl;
    link.target = '_blank';
    link.title = comment.authorDisplayName;
    const img = document.createElement('img');
    img.src = comment.authorProfileImageUrl;
    img.alt = comment.authorDisplayName;
    link.appendChild(img);
    td.appendChild(link);
    return td;
    }
    function createCommentTextCell(comment) {
    const td = document.createElement('td');
    const span = document.createElement('span');
    span.classList.add('text-overflow-dynamic-ellipsis');
    span.textContent = comment.textDisplay;
    td.appendChild(span);
    return td;
    }
    const likeCountCell = createCommentCell(comment.likeCount);
    const replyCountCell = createCommentLinkCell(comment, replyInputName);
    const dateCell = createCommentDateCell(comment);
    const authorCell = createCommentAuthorCell(comment);
    const textCell = createCommentTextCell(comment);
    li.appendChild(likeCountCell);
    li.appendChild(replyCountCell);
    li.appendChild(dateCell);
    li.appendChild(authorCell);
    li.appendChild(textCell);
    ul.append(li);
    li.innerHTML = NOVA.createSafeHTML(
    `<td>${comment.likeCount}</td>
    <td sorttable_customkey="${comment.totalReplyCount}" class="${NOVA_REPLYS_SWITCH_CLASS_NAME}">
    ${comment.comments?.length
    ? `<a href="https://www.youtube.com/watch?v=${comment.videoId}&lc=${comment.id}" target="_blank" title="Open comment link">${comment.totalReplyCount}</a> <label for="${replyInputName}"></label>`
    : ''}</td>
    <td sorttable_customkey="${new Date(comment.publishedAt).getTime()}">${NOVA.formatTimeOut.ago(new Date(comment.publishedAt))}</td>
    <td>
    <a href="${comment.authorChannelUrl}" target="_blank" title="${comment.authorDisplayName}">
    <img src="${comment.authorProfileImageUrl}" alt="${comment.authorDisplayName}" />
    </a>
    </td>
    <td sorttable_customkey="${comment.textOriginal.length}">
    <span class="text-overflow-dynamic-ellipsis">${comment.textDisplay}</span>
    ${appendReplies()?.outerHTML || ''}
    </td>`);
    ul.append(li);
    if (replyCount) {
    const checkbox = document.createElement('input');
    checkbox.type = 'checkbox';
    @@ -527,8 +479,8 @@ authorLink.title = reply.snippet.authorDisplayName;
    const authorImage = document.createElement('img');
    authorImage.src = reply.snippet.authorProfileImageUrl;
    authorImage.alt = reply.snippet.authorDisplayName;
    authorLink.appendChild(authorImage);
    authorCell.appendChild(authorLink);
    authorLink.append(authorImage);
    authorCell.append(authorLink);
    const contentCell = document.createElement('td');
    const contentSpan = document.createElement('span');
    contentSpan.classList.add('text-overflow-dynamic-ellipsis');
    @@ -537,14 +489,14 @@ likeCountDiv.classList.add('nova-reply-time-text');
    likeCountDiv.textContent = reply.snippet.likeCount ? `${reply.snippet.likeCount} likes` : '';
    const textDiv = document.createElement('div');
    textDiv.textContent = reply.snippet.textDisplay;
    contentSpan.appendChild(likeCountDiv);
    contentSpan.appendChild(textDiv);
    contentCell.appendChild(contentSpan);
    li.appendChild(authorCell);
    li.appendChild(contentCell);
    contentSpan.append(likeCountDiv);
    contentSpan.append(textDiv);
    contentCell.append(contentSpan);
    li.append(authorCell);
    li.append(contentCell);
    table.append(li);
    });
    return table.outerHTML;
    return table;
    }
    } catch (error) {
    console.error('Error comment generate:\n', error.stack + '\n', comment);
    @@ -578,38 +530,19 @@ return;
    return str;
    }
    const MODAL_CONTENT_FILTER_SELECTOR_ID = 'nova-search-comment';
    const table = document.createElement('table');
    table.classList.add('sortable');
    table.border = '0';
    table.cellSpacing = '0';
    table.cellPadding = '0';
    const thead = document.createElement('thead');
    thead.id = MODAL_CONTENT_FILTER_SELECTOR_ID;
    const headerRow = document.createElement('tr');
    const thLikes = document.createElement('th');
    thLikes.classList.add('sorttable_numeric');
    thLikes.textContent = 'likes';
    headerRow.appendChild(thLikes);
    const thReplys = document.createElement('th');
    thReplys.classList.add('sorttable_numeric');
    thReplys.textContent = 'replys';
    headerRow.appendChild(thReplys);
    const thDate = document.createElement('th');
    thDate.classList.add('sorttable_numeric');
    thDate.textContent = 'date';
    headerRow.appendChild(thDate);
    const thAvatar = document.createElement('th');
    thAvatar.classList.add('sorttable_nosort');
    thAvatar.textContent = 'avatar';
    headerRow.appendChild(thAvatar);
    const thComment = document.createElement('th');
    thComment.classList.add('sorttable_numeric');
    thComment.textContent = `comments (${commentList.length})`;
    headerRow.appendChild(thComment);
    thead.appendChild(headerRow);
    table.appendChild(thead);
    document.getElementById(MODAL_CONTENT_SELECTOR_ID).textContent = '';
    document.getElementById(MODAL_CONTENT_SELECTOR_ID).appendChild(table);
    document.getElementById(MODAL_CONTENT_SELECTOR_ID).innerHTML = NOVA.createSafeHTML(
    `<table class="sortable" border="0" cellspacing="0" cellpadding="0">
    <thead id="${MODAL_CONTENT_FILTER_SELECTOR_ID}">
    <tr>
    <th class="sorttable_numeric">likes</th>
    <th class="sorttable_numeric">replys</th>
    <th class="sorttable_numeric">date</th>
    <th class="sorttable_nosort">avatar</th>
    <th class="sorttable_numeric">comments (${commentList.length})</th>
    </tr>
    </thead>
    <!-- $ {ul.innerHTML} -->
    </table>`);
    document.getElementById(MODAL_CONTENT_FILTER_SELECTOR_ID).after(ul);
    connectSortable.apply(this).makeSortable(document.body.querySelector('table.sortable'));
    document.body.querySelector(`table.sortable thead`)
    @@ -833,10 +766,10 @@ modalContent.id = MODAL_CONTENT_SELECTOR_ID;
    modalContent.classList.add('modal-content');
    const container = document.createElement('div');
    container.classList.add('modal-container');
    container.appendChild(modalClose);
    container.appendChild(modalContent);
    modalContainer.appendChild(container);
    document.body.appendChild(modalContainer);
    container.append(modalClose);
    container.append(modalContent);
    modalContainer.append(container);
    document.body.append(modalContainer);
    const modalShowClass = 'modal-visible';
    document.getElementById(MODAL_NAME_SELECTOR_ID)
    .addEventListener('click', ({ target }) => {
    @@ -922,7 +855,7 @@ background-color: rgba(0, 47, 144, .2);
    text-decoration: none;
    color: var(--yt-spec-call-to-action);
    }`);
    return sorttable = { selectorTables: "table.sortable", classSortBottom: "sortbottom", classNoSort: "sorttable_nosort", classSorted: "sorttable_sorted", classSortedReverse: "sorttable_sorted_reverse", idSorttableSortfwdind: "sorttable_sortfwdind", idSorttableSortfrevind: "sorttable_sortrevind", iconUp: "&nbsp;&#x25B4;", iconDown: "&nbsp;&#x25BE;", regexNonDecimal: /[^0-9\.\-]/g, regexTrim: /^\s+|\s+$/g, regexAnySorttableClass: /\bsorttable_([a-z0-9]+)\b/, init() { sorttable.init.done || (sorttable.init.done = !0, document.querySelectorAll(sorttable.selectorTables).forEach(sorttable.makeSortable)) }, innerSortFunction(t, e) { d("wait"); const o = this.classList.contains(sorttable.classSorted), s = this.classList.contains(sorttable.classSortedReverse); if (o || s) return sorttable.reverse(this.sorttable_tbody), c(this, s), void t.preventDefault(); const r = [], a = this.sorttable_columnindex, n = this.sorttable_tbody.rows; for (let t = 0; t < n.length; t++)r.push([sorttable.getInnerText(n[t].cells[a]), n[t]]); r.sort(this.sorttable_sortfunction), c(this, !0); const l = this.sorttable_tbody, i = document.createDocumentFragment(); for (let t = 0; t < r.length; t++)i.appendChild(r[t][1]); function c(t, e) { const { id: o, icon: s } = e ? { id: sorttable.idSorttableSortfwdind, icon: sorttable.iconDown } : { id: sorttable.idSorttableSortfrevind, icon: sorttable.iconUp }; document.getElementById(sorttable.idSorttableSortfwdind)?.remove(), document.getElementById(sorttable.idSorttableSortfrevind)?.remove(); const r = document.createElement("span"); r.id = o, r.innerHTML = s, t.appendChild(r), t.classList.remove(sorttable.classSorted, sorttable.classSortedReverse), t.classList.add(e ? sorttable.classSorted : sorttable.classSortedReverse), d() } function d(t = null) { e.style.cursor = t } l.appendChild(i), t.preventDefault() }, makeSortable(t) { if (!t.tHead) { const e = document.createElement("thead"); t.insertBefore(e, t.firstChild) } if (1 !== t.tHead.rows.length) return; const e = Array.from(t.rows).filter((t => t.classList.contains(sorttable.classSortBottom))); if (e.length) { let o = t.tFoot; o || (o = document.createElement("tfoot"), t.appendChild(o)); const s = document.createDocumentFragment(); e.forEach((t => s.appendChild(t))), o.appendChild(s) } const o = t.tHead.rows[0].cells; for (let e = 0; e < o.length; e++) { const s = o[e]; if (!s.classList.contains(sorttable.classNoSort)) { const o = s.className.match(sorttable.regexAnySorttableClass)?.[1]; s.sorttable_sortfunction = o ? sorttable[`sort_${o}`] : sorttable.guessType(t, e), s.sorttable_columnindex = e, s.sorttable_tbody = t.tBodies[0] } } t.tHead.addEventListener("click", (e => { const o = e.target; "TH" !== o.tagName || o.classList.contains(sorttable.classNoSort) || sorttable.innerSortFunction.call(o, e, t) })) }, guessedTypesCache: new WeakMap, guessType(t, e) { const o = this.guessedTypesCache.get(t) || new Map; if (o.has(e)) return o.get(e); const s = []; for (let o = 0; o < t.rows.length; o++) { const r = t.rows[o].cells[e]; if (r.textContent?.trim()) { s.push(sorttable.sort_alpha); break } } return o.set(e, columnType), this.guessedTypesCache.set(t, o), columnType }, innerTextCache: new WeakMap, getInnerText(t) { if (!t) return ""; if (t.dataset && t.dataset.value) return t.dataset.value; if (customkey = t.getAttribute("sorttable_customkey")) return customkey; const e = sorttable.innerTextCache.get(t); if (void 0 !== e) return e; const o = "function" == typeof t?.getElementsByTagName && t.getElementsByTagName("input").length, s = t.textContent?.trim() || t.innerText?.trim() || t.text?.trim(); if (s && !o) return s; let r = ""; switch (t.nodeType) { case 3: "input" === t.nodeName.toLowerCase() && (r = t.value.trim()); break; case 1: for (let e = 0; e < t.childNodes.length; e++)r += sorttable.getInnerText(t.childNodes[e]) }return sorttable.innerTextCache.set(t, r.trim()), r.trim() }, reverse(t) { Array.from(t.rows).reverse().forEach((e => t.appendChild(e))) }, sort_numeric: (t, e) => parseFloat(t[0].replace(sorttable.regexNonDecimal, "")) - parseFloat(e[0].replace(sorttable.regexNonDecimal, "")), sort_alpha: (t, e) => t[0].localeCompare(e[0]), shakerSort(t, e) { let o = 0, s = t.length - 1; for (; o < s;)r(t, e, o, s), s-- , a(t, e, o, s), o++; function r(t, e, o, s) { let r = !1; for (let a = o; a < s; a++)e(t[a], t[a + 1]) > 0 && ([t[a], t[a + 1]] = [t[a + 1], t[a]], r = !0); return r } function a(t, e, o, s) { let r = !1; for (let a = s; a > o; a--)e(t[a], t[a - 1]) < 0 && ([t[a], t[a - 1]] = [t[a - 1], t[a]], r = !0); return r } } };
    return sorttable = { selectorTables: "table.sortable", classSortBottom: "sortbottom", classNoSort: "sorttable_nosort", classSorted: "sorttable_sorted", classSortedReverse: "sorttable_sorted_reverse", idSorttableSortfwdind: "sorttable_sortfwdind", idSorttableSortfrevind: "sorttable_sortrevind", iconUp: "&nbsp;&#x25B4;", iconDown: "&nbsp;&#x25BE;", regexNonDecimal: /[^0-9\.\-]/g, regexTrim: /^\s+|\s+$/g, regexAnySorttableClass: /\bsorttable_([a-z0-9]+)\b/, init() { sorttable.init.done || (sorttable.init.done = !0, document.querySelectorAll(sorttable.selectorTables).forEach(sorttable.makeSortable)) }, innerSortFunction(t, e) { d("wait"); const o = this.classList.contains(sorttable.classSorted), s = this.classList.contains(sorttable.classSortedReverse); if (o || s) return sorttable.reverse(this.sorttable_tbody), c(this, s), void t.preventDefault(); const r = [], a = this.sorttable_columnindex, n = this.sorttable_tbody.rows; for (let t = 0; t < n.length; t++)r.push([sorttable.getInnerText(n[t].cells[a]), n[t]]); r.sort(this.sorttable_sortfunction), c(this, !0); const l = this.sorttable_tbody, i = document.createDocumentFragment(); for (let t = 0; t < r.length; t++)i.append(r[t][1]); function c(t, e) { const { id: o, icon: s } = e ? { id: sorttable.idSorttableSortfwdind, icon: sorttable.iconDown } : { id: sorttable.idSorttableSortfrevind, icon: sorttable.iconUp }; document.getElementById(sorttable.idSorttableSortfwdind)?.remove(), document.getElementById(sorttable.idSorttableSortfrevind)?.remove(); const r = document.createElement("span"); r.id = o, r.innerHTML = NOVA.createSafeHTML(s), t.append(r), t.classList.remove(sorttable.classSorted, sorttable.classSortedReverse), t.classList.add(e ? sorttable.classSorted : sorttable.classSortedReverse), d() } function d(t = null) { e.style.cursor = t } l.append(i), t.preventDefault() }, makeSortable(t) { if (!t.tHead) { const e = document.createElement("thead"); t.insertBefore(e, t.firstChild) } if (1 !== t.tHead.rows.length) return; const e = Array.from(t.rows).filter((t => t.classList.contains(sorttable.classSortBottom))); if (e.length) { let o = t.tFoot; o || (o = document.createElement("tfoot"), t.append(o)); const s = document.createDocumentFragment(); e.forEach((t => s.append(t))), o.append(s) } const o = t.tHead.rows[0].cells; for (let e = 0; e < o.length; e++) { const s = o[e]; if (!s.classList.contains(sorttable.classNoSort)) { const o = s.className.match(sorttable.regexAnySorttableClass)?.[1]; s.sorttable_sortfunction = o ? sorttable[`sort_${o}`] : sorttable.guessType(t, e), s.sorttable_columnindex = e, s.sorttable_tbody = t.tBodies[0] } } t.tHead.addEventListener("click", (e => { const o = e.target; "TH" !== o.tagName || o.classList.contains(sorttable.classNoSort) || sorttable.innerSortFunction.call(o, e, t) })) }, guessedTypesCache: new WeakMap, guessType(t, e) { const o = this.guessedTypesCache.get(t) || new Map; if (o.has(e)) return o.get(e); const s = []; for (let o = 0; o < t.rows.length; o++) { const r = t.rows[o].cells[e]; if (r.textContent?.trim()) { s.push(sorttable.sort_alpha); break } } return o.set(e, columnType), this.guessedTypesCache.set(t, o), columnType }, innerTextCache: new WeakMap, getInnerText(t) { if (!t) return ""; if (t.dataset && t.dataset.value) return t.dataset.value; if (customkey = t.getAttribute("sorttable_customkey")) return customkey; const e = sorttable.innerTextCache.get(t); if (void 0 !== e) return e; const o = "function" == typeof t?.getElementsByTagName && t.getElementsByTagName("input").length, s = t.textContent?.trim() || t.innerText?.trim() || t.text?.trim(); if (s && !o) return s; let r = ""; switch (t.nodeType) { case 3: "input" === t.nodeName.toLowerCase() && (r = t.value.trim()); break; case 1: for (let e = 0; e < t.childNodes.length; e++)r += sorttable.getInnerText(t.childNodes[e]) }return sorttable.innerTextCache.set(t, r.trim()), r.trim() }, reverse(t) { Array.from(t.rows).reverse().forEach((e => t.append(e))) }, sort_numeric: (t, e) => parseFloat(t[0].replace(sorttable.regexNonDecimal, "")) - parseFloat(e[0].replace(sorttable.regexNonDecimal, "")), sort_alpha: (t, e) => t[0].localeCompare(e[0]), shakerSort(t, e) { let o = 0, s = t.length - 1; for (; o < s;)r(t, e, o, s), s-- , a(t, e, o, s), o++; function r(t, e, o, s) { let r = !1; for (let a = o; a < s; a++)e(t[a], t[a + 1]) > 0 && ([t[a], t[a + 1]] = [t[a + 1], t[a]], r = !0); return r } function a(t, e, o, s) { let r = !1; for (let a = s; a > o; a--)e(t[a], t[a - 1]) < 0 && ([t[a], t[a - 1]] = [t[a - 1], t[a]], r = !0); return r } } };
    }
    },
    options: {
    @@ -1114,6 +1047,15 @@ type: 'checkbox',
    }
    });
    const NOVA = {
    createSafeHTML(html = required()) {
    if (typeof html !== 'string') return console.error('not string "html":', typeof html);
    if (typeof this.policy === 'undefined') {
    this.policy = (typeof trustedTypes !== 'undefined')
    ? trustedTypes.createPolicy('nova-policy', { createHTML: html => html, })
    : null;
    }
    return this.policy ? this.policy.createHTML(html) : html;
    },
    waitSelector(selector = required(), options = {}) {
    const { container = document, destroy_timeout, destroy_after_page_leaving } = options;
    return new Promise((resolve, reject) => {
    @@ -1635,8 +1577,9 @@ text = item.innerText,
    hasText = text?.toLowerCase().includes(keyword),
    highlight = el => {
    if (el.innerHTML.includes('<mark ')) {
    el.innerHTML = el.innerHTML
    .replace(/<\/?mark[^>]*>/g, '');
    el.innerHTML = this.createSafeHTML(el.innerHTML
    .replace(/<\/?mark[^>]*>/g, '')
    );
    }
    item.style.display = hasText ? '' : 'none';
    if (hasText && keyword) {
    @@ -1657,7 +1600,7 @@ pattern = new RegExp('(>[^<.]*)?(' + keyword + ')([^<.]*)?', 'gi'),
    highlightStyle = highlightClass ? `class="${highlightClass}"` : 'style="background-color:#afafaf"',
    replaceWith = `$1<mark ${highlightStyle}>$2</mark>$3`,
    marked = content.replaceAll(pattern, replaceWith);
    return (target.innerHTML = marked) !== content;
    return (target.innerHTML = NOVA.createSafeHTML(marked)) !== content;
    }
    },
    isMusic() {
    @@ -2049,20 +1992,19 @@ btn.className = `ytp-button ${SELECTOR_CLASS}`;
    btn.style.opacity = .5;
    btn.style.minWidth = getComputedStyle(container).width || '48px';
    btn.title = 'Repeat';
    btn.appendChild(createSvgIcon());
    function createSvgIcon() {
    btn.append((function createSvgIcon() {
    const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
    svg.setAttribute('viewBox', '-6 -6 36 36');
    svg.setAttribute('height', '100%');
    svg.setAttribute('width', '100%');
    svg.setAttribute('viewBox', '-6 -6 36 36');
    const g = document.createElementNS('http://www.w3.org/2000/svg', 'g');
    g.setAttribute('fill', 'currentColor');
    const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
    path.setAttribute('d', 'M 7 7 L 17 7 L 17 10 L 21 6 L 17 2 L 17 5 L 5 5 L 5 11 L 7 11 L 7 7 Z M 7.06 17 L 7 14 L 3 18 L 7 22 L 7 19 L 19 19 L 19 13 L 17 13 L 17 17 L 7.06 17 Z');
    g.appendChild(path);
    svg.appendChild(g);
    g.append(path);
    svg.append(g);
    return svg;
    }
    })());
    btn.addEventListener('click', toggleLoop);
    container.after(btn);
    NOVA.waitSelector('#movie_player video')
    @@ -2586,23 +2528,14 @@ if (!(init_container instanceof HTMLElement)) {
    return console.error('init_container not HTMLElement:', init_container);
    }
    return document.getElementById(SELECTOR_ID) || (function () {
    const container = document.createElement('div');
    container.id = SELECTOR_ID;
    const innerContainer = document.createElement('div');
    innerContainer.classList.add('container');
    const buffer = document.createElement('div');
    buffer.id = `${SELECTOR_ID}-buffer`;
    buffer.classList.add('ytp-load-progress');
    const progress = document.createElement('div');
    progress.id = `${SELECTOR_ID}-progress`;
    progress.classList.add('ytp-swatch-background-color');
    const chapters = document.createElement('div');
    chapters.id = `${SELECTOR_ID}-chapters`;
    innerContainer.appendChild(buffer);
    innerContainer.appendChild(progress);
    container.appendChild(innerContainer);
    container.appendChild(chapters);
    init_container.appendChild(container);
    init_container.insertAdjacentHTML('beforeend', NOVA.createSafeHTML(
    `<div id="${SELECTOR_ID}" class="">
    <div class="container">
    <div id="${SELECTOR_ID}-buffer" class="ytp-load-progress"></div>
    <div id="${SELECTOR_ID}-progress" class="ytp-swatch-background-color"></div>
    </div>
    <div id="${SELECTOR_ID}-chapters"></div>
    </div>`));
    NOVA.css.push(
    `[id|=${SELECTOR_ID}] {
    position: absolute;
    @@ -2913,39 +2846,35 @@ if (user_settings.player_buttons_custom_items?.includes('picture-in-picture')) {
    const pipBtn = document.createElement('button');
    pipBtn.className = `ytp-button ${SELECTOR_BTN_CLASS_NAME}`;
    pipBtn.setAttribute('tooltip', 'Picture in Picture (PiP)');
    pipBtn.appendChild(createSvgIcon());
    pipBtn.innerHTML = createSvgIcon();
    pipBtn.addEventListener('click', () => document.pictureInPictureElement
    ? document.exitPictureInPicture() : NOVA.videoElement?.requestPictureInPicture()
    ? document.exitPictureInPicture()
    : NOVA.videoElement?.requestPictureInPicture()
    );
    container.prepend(pipBtn);
    NOVA.videoElement?.addEventListener('enterpictureinpicture', () => {
    pipBtn.textContent = '';
    pipBtn.appendChild(createSvgIcon(pip_enabled));
    });
    NOVA.videoElement?.addEventListener('leavepictureinpicture', () => {
    pipBtn.textContent = '';
    pipBtn.appendChild(createSvgIcon());
    });
    NOVA.videoElement?.addEventListener('enterpictureinpicture', () => pipBtn.innerHTML = createSvgIcon('pip_enabled'));
    NOVA.videoElement?.addEventListener('leavepictureinpicture', () => pipBtn.innerHTML = createSvgIcon());
    function createSvgIcon(pip_enabled) {
    const svg = document.createElement('svg');
    const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
    svg.setAttribute('width', '100%');
    svg.setAttribute('height', '100%');
    svg.setAttribute('viewBox', '-8 -6 36 36');
    const path = document.createElement('path');
    path.setAttribute('fill', 'currentColor');
    const g = document.createElementNS('http://www.w3.org/2000/svg', 'g');
    g.setAttribute('fill', 'currentColor');
    const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
    path.setAttribute('d', pip_enabled
    ? 'M18.5,11H18v1h.5A1.5,1.5,0,0,1,20,13.5v5A1.5,1.5,0,0,1,18.5,20h-8A1.5,1.5,0,0,1,9,18.5V18H8v.5A2.5,2.5,0,0,0,10.5,21h8A2.5,2.5,0,0,0,21,18.5v-5A2.5,2.5,0,0,0,18.5,11Z M14.5,4H2.5A2.5,2.5,0,0,0,0,6.5v8A2.5,2.5,0,0,0,2.5,17h12A2.5,2.5,0,0,0,17,14.5v-8A2.5,2.5,0,0,0,14.5,4Z'
    : 'M2.5,17A1.5,1.5,0,0,1,1,15.5v-9A1.5,1.5,0,0,1,2.5,5h13A1.5,1.5,0,0,1,17,6.5V10h1V6.5A2.5,2.5,0,0,0,15.5,4H2.5A2.5,2.5,0,0,0,0,6.5v9A2.5,2.5,0,0,0,2.5,18H7V17Z M18.5,11h-8A2.5,2.5,0,0,0,8,13.5v5A2.5,2.5,0,0,0,10.5,21h8A2.5,2.5,0,0,0,21,18.5v-5A2.5,2.5,0,0,0,18.5,11Z');
    svg.append(path);
    return svg;
    g.append(path);
    svg.append(g);
    return NOVA.createSafeHTML(svg.outerHTML);
    }
    }
    if (user_settings.player_buttons_custom_items?.indexOf('popup') !== -1 && !NOVA.queryURL.has('popup')) {
    const popupBtn = document.createElement('button');
    popupBtn.className = `ytp-button ${SELECTOR_BTN_CLASS_NAME}`;
    popupBtn.setAttribute('tooltip', 'Open in popup');
    popupBtn.appendChild(createSvgIcon());
    function createSvgIcon() {
    popupBtn.append((function createSvgIcon() {
    const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
    svg.setAttribute('viewBox', '-8 -8 36 36');
    svg.setAttribute('height', '100%');
    @@ -2954,18 +2883,17 @@ const g = document.createElementNS('http://www.w3.org/2000/svg', 'g');
    g.setAttribute('fill', 'currentColor');
    const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
    path.setAttribute('d', 'M18 2H6v4H2v12h12v-4h4V2z M12 16H4V8h2v6h6V16z M16 12h-2h-2H8V8V6V4h8V12z');
    g.appendChild(path);
    svg.appendChild(g);
    g.append(path);
    svg.append(g);
    return svg;
    }
    })());
    popupBtn.addEventListener('click', () => {
    const { width, height } = NOVA.aspectRatio.sizeToFit({
    'src_width': NOVA.videoElement.videoWidth,
    'src_height': NOVA.videoElement.videoHeight,
    });
    url = new URL(
    document.head.querySelector('link[itemprop="embedUrl"][href]')?.href
    || (location.origin + '/embed/' + movie_player.getVideoData().video_id)
    const url = new URL(
    document.head.querySelector('link[itemprop="embedUrl"][href]')?.href || (location.origin + '/embed/' + movie_player.getVideoData().video_id)
    );
    if (currentTime = Math.trunc(NOVA.videoElement?.currentTime)) url.searchParams.set('start', currentTime);
    url.searchParams.set('autoplay', 1);
    @@ -3012,25 +2940,24 @@ ${SELECTOR_SCREENSHOT} .close-btn > * { margin: auto; }`);
    const screenshotBtn = document.createElement('button');
    screenshotBtn.className = `ytp-button ${SELECTOR_BTN_CLASS_NAME}`;
    screenshotBtn.setAttribute('tooltip', 'Take screenshot');
    createSvgIcon();
    function createSvgIcon() {
    const svg = document.createElement('svg');
    screenshotBtn.append((function createSvgIcon() {
    const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
    svg.setAttribute('viewBox', '0 -166 512 860');
    svg.setAttribute('height', '100%');
    svg.setAttribute('width', '100%');
    const g = document.createElement('g');
    const g = document.createElementNS('http://www.w3.org/2000/svg', 'g');
    g.setAttribute('fill', 'currentColor');
    const circle = document.createElement('circle');
    const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
    circle.setAttribute('cx', '255.811');
    circle.setAttribute('cy', '285.309');
    circle.setAttribute('r', '75.217');
    const path = document.createElement('path');
    const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
    path.setAttribute('d', 'M477,137H352.718L349,108c0-16.568-13.432-30-30-30H191c-16.568,0-30,13.432-30,30l-3.718,29H34 c-11.046,0-20,8.454-20,19.5v258c0,11.046,8.954,20.5,20,20.5h443c11.046,0,20-9.454,20-20.5v-258C497,145.454,488.046,137,477,137 z M255.595,408.562c-67.928,0-122.994-55.066-122.994-122.993c0-67.928,55.066-122.994,122.994-122.994 c67.928,0,122.994,55.066,122.994,122.994C378.589,353.495,323.523,408.562,255.595,408.562z M474,190H369v-31h105V190z');
    g.appendChild(circle);
    g.appendChild(path);
    svg.appendChild(g);
    screenshotBtn.appendChild(svg);
    }
    g.append(circle);
    g.append(path);
    svg.append(g);
    return svg;
    })());
    screenshotBtn.addEventListener('click', () => {
    const
    container = document.getElementById(SELECTOR_SCREENSHOT_ID) || document.createElement('a'),
    @@ -3112,7 +3039,7 @@ const close = document.createElement('a');
    close.className = 'close-btn';
    const span = document.createElement('span');
    span.textContent = 'CLOSE';
    close.appendChild(span);
    close.append(span);
    close.title = 'Close';
    close.addEventListener('click', evt => {
    evt.preventDefault();
    @@ -3143,8 +3070,7 @@ if (user_settings.player_buttons_custom_items?.includes('thumbnail')) {
    const thumbBtn = document.createElement('button');
    thumbBtn.className = `ytp-button ${SELECTOR_BTN_CLASS_NAME}`;
    thumbBtn.setAttribute('tooltip', 'View Thumbnail');
    thumbBtn.appendChild(createSvgIcon());
    function createSvgIcon() {
    thumbBtn.append((function createSvgIcon() {
    const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
    svg.setAttribute('viewBox', '0 -10 21 40');
    svg.setAttribute('height', '100%');
    @@ -3159,12 +3085,12 @@ const path1 = document.createElementNS('http://www.w3.org/2000/svg', 'path');
    path1.setAttribute('d', 'M0 2v16h20V2H0z M18 16H2V4h16V16z');
    const polygon = document.createElementNS('http://www.w3.org/2000/svg', 'polygon');
    polygon.setAttribute('points', '17 10.9 14 7.9 9 12.9 6 9.9 3 12.9 3 15 17 15');
    g.appendChild(circle);
    g.appendChild(path1);
    g.appendChild(polygon);
    svg.appendChild(g);
    g.append(circle);
    g.append(path1);
    g.append(polygon);
    svg.append(g);
    return svg;
    }
    })());
    thumbBtn.addEventListener('click', async () => {
    const
    videoId = NOVA.queryURL.get('v') || movie_player.getVideoData().video_id,
    @@ -3205,8 +3131,7 @@ rotateBtn = document.createElement('button');
    rotateBtn.className = `ytp-button ${SELECTOR_BTN_CLASS_NAME}`;
    rotateBtn.setAttribute('tooltip', `Rotate video (${hotkey.replace('Key', '')})`);
    rotateBtn.style.cssText = 'padding: 0 1.1em;';
    rotateBtn.appendChild(createRotateIcon());
    function createRotateIcon() {
    rotateBtn.append((function createRotateIcon() {
    const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
    svg.setAttribute('viewBox', '0 0 1536 1536');
    svg.setAttribute('height', '100%');
    @@ -3215,10 +3140,10 @@ const g = document.createElementNS('http://www.w3.org/2000/svg', 'g');
    g.setAttribute('fill', 'currentColor');
    const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
    path.setAttribute('d', 'M1536 128v448q0 26-19 45t-45 19h-448q-42 0-59-40-17-39 14-69l138-138Q969 256 768 256q-104 0-198.5 40.5T406 406 296.5 569.5 256 768t40.5 198.5T406 1130t163.5 109.5T768 1280q119 0 225-52t179-147q7-10 23-12 14 0 25 9l137 138q9 8 9.5 20.5t-7.5 22.5q-109 132-264 204.5T768 1536q-156 0-298-61t-245-164-164-245T0 768t61-298 164-245T470 61 768 0q147 0 284.5 55.5T1297 212l130-129q29-31 70-14 39 17 39 59z');
    g.appendChild(path);
    svg.appendChild(g);
    g.append(path);
    svg.append(g);
    return svg;
    }
    })());
    rotateBtn.addEventListener('click', rotateVideo);
    document.addEventListener('keyup', evt => {
    if (NOVA.currentPage != 'watch' && NOVA.currentPage != 'embed') return;
    @@ -3295,7 +3220,7 @@ renderIcon();
    function renderIcon() {
    if (watchLaterIcon = watchLaterDefault.querySelector('.ytp-watch-later-icon')) {
    const iconClone = watchLaterIcon.cloneNode(true);
    watchLaterBtn.appendChild(iconClone);
    watchLaterBtn.append(iconClone);
    }
    }
    });
    @@ -3314,22 +3239,22 @@ NOVA.css.push(
    display: none !important;
    }`);
    cardBtn.className = `ytp-button ${SELECTOR_BTN_CLASS_NAME}`;
    cardBtn.appendChild(createSVG());
    cardBtn.append(createSvgIcon());
    if (user_settings.player_buttons_custom_card_switch) {
    switchState(movie_player.toggleAttribute(cardAttrName));
    }
    cardBtn.addEventListener('click', () => switchState(movie_player.toggleAttribute(cardAttrName)));
    function switchState(state = required()) {
    cardBtn.textContent = '';
    cardBtn.appendChild(createSVG(state));
    cardBtn.append(createSvgIcon(state));
    cardBtn.setAttribute('tooltip', `The cards are currently ${state ? 'hidden' : 'showing'}`);
    }
    function createSVG(alt) {
    const svg = document.createElement('svg');
    function createSvgIcon(alt) {
    const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
    svg.setAttribute('width', '100%');
    svg.setAttribute('height', '100%');
    svg.setAttribute('viewBox', '-200 0 912 512');
    const g = document.createElement('g');
    const g = document.createElementNS('http://www.w3.org/2000/svg', 'g');
    g.setAttribute('fill', 'currentColor');
    const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
    path.setAttribute('d', alt
    @@ -3339,7 +3264,7 @@ if (!alt) {
    const secondPath = document.createElementNS('http://www.w3.org/2000/svg', 'path');
    secondPath.setAttribute('d', 'M 377.487 163.483 C 374.218 165.287 369.915 169.616 368.282 172.742 C 366.19 176.75 366.338 184.412 368.58 188.208 C 369.584 189.906 375.931 195.589 382.686 200.835 C 406.774 219.545 444.116 252.522 462.487 271.308 L 470.487 279.489 L 458.987 290.523 C 374.754 371.349 302.814 418.519 259.95 421.028 C 239.247 422.24 213.725 413.237 179.562 392.671 C 167.844 385.617 166.151 384.89 161.435 384.89 C 154.689 384.89 149.078 388.434 146.108 394.569 C 143.518 399.919 143.459 403.579 145.877 408.819 C 148.257 413.975 150.455 415.89 161.987 422.854 C 198.507 444.909 230.542 456.311 255.987 456.311 C 305.049 456.311 376.65 414.416 461.987 335.777 C 483.571 315.886 509.639 289.37 510.918 286.004 C 512.681 281.361 512.205 274.195 509.876 270.319 C 507.481 266.336 484.239 243.06 463.496 223.871 C 437.951 200.241 398.954 167.885 391.287 163.958 C 387.643 162.092 380.457 161.845 377.487 163.483 M 316.949 223.569 C 313.697 225.209 311.863 227.039 310.187 230.319 C 307.175 236.212 307.768 240.325 313.792 255.319 C 317.854 265.428 318.457 267.908 318.777 275.819 C 319.541 294.697 313.773 309.555 300.509 322.876 C 287.269 336.173 274.151 341.727 255.987 341.727 C 244.203 341.727 238.379 340.346 226.824 334.808 C 214.528 328.915 204.983 330.882 200.237 340.285 C 191.288 358.016 210.326 372.539 247.471 376.317 C 301.128 381.775 350.05 340.079 353.651 285.819 C 354.605 271.455 351.026 250.247 345.564 237.898 C 342.92 231.92 337.048 224.919 332.862 222.754 C 328.769 220.638 322.082 220.98 316.949 223.569" transform="matrix(-1, 0, 0, -1, 512.000305, 558.092285)');
    secondPath.setAttribute('transform', 'matrix(-1, 0, 0, -1, 512.000305, 558.092285)');
    g.appendChild(secondPath);
    g.append(secondPath);
    }
    path.setAttribute('fill-rule', alt ? 'evenodd' : '');
    svg.append(g);
    @@ -3875,8 +3800,7 @@ btn.title = 'Save channel state';
    const btnTitle = document.createElement('span');
    btnTitle.id = SELECTOR_BTN_TITLE_ID;
    btnTitle.style.display = 'flex';
    btnTitle.appendChild(createSvgIcon());
    function createSvgIcon() {
    btnTitle.append((function createSvgIcon() {
    const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
    svg.setAttribute('width', '100%');
    svg.setAttribute('height', '100%');
    @@ -3885,10 +3809,10 @@ const g = document.createElementNS('http://www.w3.org/2000/svg', 'g');
    g.setAttribute('fill', 'currentColor');
    const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
    path.setAttribute('d', 'M23.4 24.2c-.3.8-1.1 1.4-2 1.4-.9 0-1.7-.6-2-1.4H9.3c-.3 0-.6-.3-.6-.6v-.3c0-.3.3-.6.6-.6h10.1c.3-.9 1.1-1.5 2.1-1.5s1.8.6 2.1 1.5h3.2c.3 0 .6.3.6.6v.3c0 .3-.3.6-.6.6h-3.4zm-7.7-5.3c-.3.9-1.1 1.5-2.1 1.5s-1.8-.6-2.1-1.5H9.3c-.3 0-.6-.3-.6-.6V18c0-.3.3-.6.6-.6h2.2c.3-.8 1.1-1.4 2.1-1.4s1.8.6 2.1 1.4h11.1c.3 0 .6.3.6.6v.3c0 .3-.3.6-.6.6H15.7zm7.9-5.4c-.3.8-1.1 1.4-2.1 1.4-.9 0-1.7-.6-2.1-1.4H9.3c-.3 0-.6-.3-.6-.6v-.3c0-.3.3-.6.6-.6h10.1c.3-.9 1.1-1.6 2.1-1.6s1.9.7 2.1 1.6h3.1c.3 0 .6.3.6.6v.3c0 .3-.3.6-.6.6h-3.1z');
    g.appendChild(path);
    svg.appendChild(g);
    g.append(path);
    svg.append(g);
    return svg;
    }
    })());
    btn.prepend(btnTitle);
    btn.append(genList());
    container.prepend(btn);
    @@ -3950,16 +3874,10 @@ checkbox.id = `checkbox-${el.name}`;
    checkbox.checked = Boolean(storage);
    checkbox.className = 'ytp-menuitem-toggle-checkbox';
    const li = document.createElement('li');
    const label = document.createElement('label');
    label.setAttribute('for', `checkbox-${el.name}`);
    const labelText = document.createTextNode(`${el.label || el.name}`);
    label.appendChild(labelText);
    if (el.hasOwnProperty('get_current_state') && storage) {
    const span = document.createElement('span');
    span.textContent = '';
    label.appendChild(span);
    }
    li.appendChild(label);
    li.innerHTML = NOVA.createSafeHTML(
    `<label for="checkbox-${el.name}">
    ${el.label || el.name} <span>${(el.hasOwnProperty('get_current_state') && storage) || ''}</span>
    </label>`);
    li.title = storage ? `Currently stored value ${storage}` : 'none';
    if (Boolean(storage) && el.hasOwnProperty('custom_apply') && typeof el.custom_apply === 'function') {
    el.custom_apply();
    @@ -3973,7 +3891,7 @@ else {
    NOVA.storage_obj_manager.remove(el.name);
    }
    li.title = state ? `Currently stored value "${state}"` : 'none';
    if (el.hasOwnProperty('get_current_state')) li.querySelector('span').textContent = state || '';
    if (el.hasOwnProperty('get_current_state') && storage) li.querySelector('span').textContent = state || '';
    btnTitleStateUpdate(Boolean(state));
    });
    li.prepend(checkbox);
    @@ -3994,13 +3912,13 @@ const li = document.createElement('li');
    const label = document.createElement('label');
    label.setAttribute('for', `checkbox-${SLIDER_STORAGE_NAME}`);
    const labelText = document.createTextNode(SLIDER_LABEL);
    label.appendChild(labelText);
    label.append(labelText);
    if (storage) {
    const span = document.createElement('span');
    span.textContent = storage;
    label.appendChild(span);
    label.append(span);
    }
    li.appendChild(label);
    li.append(label);
    li.title = 'Simple alternative SponsorBlock';
    slider.addEventListener('change', sliderChange);
    slider.addEventListener('input', sliderChange);
    @@ -4568,21 +4486,20 @@ ${SELECTOR_BTN_LIST} li:hover { background-color: #c00; }`);
    containerBtn.className = `ytp-button ${SELECTOR_BTN_CLASS_NAME} ${SELECTOR_BTN_CLASS_NAME}`;
    dropdownSpan.id = SELECTOR_BTN_TITLE_ID;
    dropdownSpan.setAttribute('tooltip', 'Nova video download 🡇');
    dropdownSpan.appendChild(createSvgIcon());
    function createSvgIcon() {
    dropdownSpan.append((function createSvgIcon() {
    const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
    svg.setAttribute('viewBox', '0 0 120 120');
    svg.setAttribute('width', '100%');
    svg.setAttribute('height', '100%');
    svg.setAttribute('viewBox', '0 0 120 120');
    svg.style.scale = '0.6';
    const g = document.createElementNS('http://www.w3.org/2000/svg', 'g');
    g.setAttribute('fill', 'currentColor');
    const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
    path.setAttribute('d', 'M96.215 105h-72.18c-3.33 0-5.94-2.61-5.94-5.94V75.03c0-3.33 2.61-5.94 5.94-5.94 3.33 0 5.94 2.61 5.94 5.94v18h60.03v-18c0-3.33 2.61-5.94 5.94-5.94 3.33 0 5.94 2.61 5.94 5.94v24.03c.27 3.33-2.34 5.94-5.67 5.94Zm-32.4-34.47c-2.07 1.89-5.4 1.89-7.56 0l-18.72-17.19c-2.07-1.89-2.07-4.86 0-6.84 2.07-1.98 5.4-1.89 7.56 0l8.91 8.19V20.94c0-3.33 2.61-5.94 5.94-5.94 3.33 0 5.94 2.61 5.94 5.94V54.6l8.91-8.19c2.07-1.89 5.4-1.89 7.56 0 2.07 1.89 2.07 4.86 0 6.84l-18.54 17.28Z');
    g.appendChild(path);
    svg.appendChild(g);
    g.append(path);
    svg.append(g);
    return svg;
    }
    })());
    dropdownMenu.id = SELECTOR_BTN_LIST_ID;
    containerBtn.prepend(dropdownSpan);
    containerBtn.append(dropdownMenu);
    @@ -4631,8 +4548,7 @@ downloadFile(item.link);
    }, { capture: true });
    menuList.append(menuItem);
    });
    const dropdownSpanClone = dropdownSpan.cloneNode(true);
    btn.appendChild(dropdownSpanClone);
    btn.append(dropdownSpan.cloneNode(true));
    }
    }
    });
    @@ -5281,7 +5197,7 @@ text-decoration: none;
    font-weight: bold;
    margin: 0 var(--ytd-subscribe-button-margin, 12px);
    }`);
    container.insertAdjacentHTML(position,
    container.insertAdjacentHTML(position, NOVA.createSafeHTML(
    `<button id="${BTN_SELECTOR_ID}" style="display:flex" title="Show Transcript" class="style-scope yt-formatted-string bold yt-spec-button-shape-next--tonal yt-spec-button-shape-next--mono yt-spec-button-shape-next--size-m">
    <span class="yt-spec-button-shape-next__icon" style="height:100%">
    <svg viewBox="0 0 24 24" height="100%" width="100%">
    @@ -5291,7 +5207,7 @@ container.insertAdjacentHTML(position,
    </svg>
    </span>
    <span class="yt-spec-button-shape-next__button-text-content" style="align-self:center;">Transcript</span>
    </button>`);
    </button>`));
    return document.getElementById(BTN_SELECTOR_ID);
    })());
    }
    @@ -5410,9 +5326,9 @@ outerSpan.style.marginRight = '5px';
    const innerSpan = document.createElement('span');
    innerSpan.id = SELECTOR_ID;
    innerSpan.textContent = text;
    outerSpan.appendChild(document.createTextNode(' • '));
    outerSpan.appendChild(innerSpan);
    container.appendChild(outerSpan);
    outerSpan.append(document.createTextNode(' • '));
    outerSpan.append(innerSpan);
    container.append(outerSpan);
    return innerSpan;
    })())
    .textContent = text;
    @@ -6164,8 +6080,7 @@ window.scrollTo({ top: topOffset });
    }
    function createArrowButton() {
    const scrollDownButton = document.createElement('button');
    scrollDownButton.appendChild(createSvgIcon());
    function createSvgIcon() {
    scrollDownButton.append((function createSvgIcon() {
    const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
    svg.setAttribute('viewBox', '0 0 16 16');
    svg.setAttribute('height', '100%');
    @@ -6174,10 +6089,10 @@ const g = document.createElementNS('http://www.w3.org/2000/svg', 'g');
    g.setAttribute('fill', 'currentColor');
    const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
    path.setAttribute('d', 'M3.35 4.97 8 9.62 12.65 4.97l.71.71L8 11.03l-5.35-5.35.7-.71z');
    g.appendChild(path);
    svg.appendChild(g);
    g.append(path);
    svg.append(g);
    return svg;
    }
    })());
    scrollDownButton.title = 'Scroll down';
    Object.assign(scrollDownButton.style, {
    cursor: 'pointer',
    @@ -6922,8 +6837,7 @@ link.id = SELECTOR_ID;
    link.target = '_blank';
    link.title = 'Nova RSS';
    link.className = `yt-spec-button-shape-next--overlay`;
    link.appendChild(createSvgIcon());
    function createSvgIcon() {
    link.append((function createSvgIcon() {
    const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
    svg.setAttribute('viewBox', '-35 -35 55 55');
    svg.setAttribute('height', '100%');
    @@ -6934,10 +6848,10 @@ g.setAttribute('fill', 'currentColor');
    const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
    path.setAttribute('fill', '#F60');
    path.setAttribute('d', 'M-17.392 7.875c0 3.025-2.46 5.485-5.486 5.485s-5.486-2.46-5.486-5.485c0-3.026 2.46-5.486 5.486-5.486s5.486 2.461 5.486 5.486zm31.351 5.486C14.042.744 8.208-11.757-1.567-19.736c-7.447-6.217-17.089-9.741-26.797-9.708v9.792C-16.877-19.785-5.556-13.535.344-3.66a32.782 32.782 0 0 1 4.788 17.004h8.827v.017zm-14.96 0C-.952 5.249-4.808-2.73-11.108-7.817c-4.821-3.956-11.021-6.184-17.255-6.15v8.245c6.782-.083 13.432 3.807 16.673 9.774a19.296 19.296 0 0 1 2.411 9.326h8.278v-.017z');
    g.appendChild(path);
    svg.appendChild(g);
    g.append(path);
    svg.append(g);
    return svg;
    }
    })());
    Object.assign(link.style, {
    height: '20px',
    display: 'inline-block',
    @@ -7516,13 +7430,13 @@ sliderCheckbox.title = 'Remember speed';
    const out = {};
    const right = document.createElement('div');
    right.className = 'ytp-menuitem-content';
    out.sliderCheckbox = right.appendChild(sliderCheckbox);
    out.slider = right.appendChild(slider);
    out.sliderCheckbox = right.append(sliderCheckbox);
    out.slider = right.append(slider);
    const speedMenu = document.createElement('div');
    speedMenu.className = 'ytp-menuitem';
    speedMenu.id = SELECTOR_ID;
    speedMenu.append(sliderIcon);
    out.sliderLabel = speedMenu.appendChild(sliderLabel);
    out.sliderLabel = speedMenu.append(sliderLabel);
    speedMenu.append(right);
    document.body.querySelector('.ytp-panel-menu')
    ?.append(speedMenu);
    @@ -10123,8 +10037,7 @@ opacity: .5,
    'min-width': getComputedStyle(container).width || '48px',
    });
    btn.title = 'Preload video';
    link.appendChild(createSvgIcon());
    function createSvgIcon() {
    link.append((function createSvgIcon() {
    const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
    svg.setAttribute('viewBox', '0 0 465 465');
    svg.setAttribute('height', '100%');
    @@ -10134,24 +10047,24 @@ const g = document.createElementNS('http://www.w3.org/2000/svg', 'g');
    g.setAttribute('fill', 'currentColor');
    const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
    path.setAttribute('d', 'M279.591,423.714c-3.836,0.956-7.747,1.805-11.629,2.52c-10.148,1.887-16.857,11.647-14.98,21.804 c0.927,4.997,3.765,9.159,7.618,11.876c3.971,2.795,9.025,4.057,14.175,3.099c4.623-0.858,9.282-1.867,13.854-3.008 c10.021-2.494,16.126-12.646,13.626-22.662C299.761,427.318,289.618,421.218,279.591,423.714z');
    g.appendChild(path);
    g.append(path);
    path.setAttribute('d', 'M417.887,173.047c1.31,3.948,3.811,7.171,6.97,9.398c4.684,3.299,10.813,4.409,16.662,2.475 c9.806-3.256,15.119-13.83,11.875-23.631c-1.478-4.468-3.118-8.95-4.865-13.314c-3.836-9.59-14.714-14.259-24.309-10.423 c-9.585,3.834-14.256,14.715-10.417,24.308C415.271,165.528,416.646,169.293,417.887,173.047z');
    g.appendChild(path);
    g.append(path);
    path.setAttribute('d', 'M340.36,397.013c-3.299,2.178-6.704,4.286-10.134,6.261c-8.949,5.162-12.014,16.601-6.854,25.546 c1.401,2.433,3.267,4.422,5.416,5.942c5.769,4.059,13.604,4.667,20.127,0.909c4.078-2.352,8.133-4.854,12.062-7.452 c8.614-5.691,10.985-17.294,5.291-25.912C360.575,393.686,348.977,391.318,340.36,397.013z');
    g.appendChild(path);
    g.append(path);
    path.setAttribute('d', 'M465.022,225.279c-0.407-10.322-9.101-18.356-19.426-17.953c-10.312,0.407-18.352,9.104-17.947,19.422 c0.155,3.945,0.195,7.949,0.104,11.89c-0.145,6.473,3.021,12.243,7.941,15.711c2.931,2.064,6.488,3.313,10.345,3.401 c10.322,0.229,18.876-7.958,19.105-18.285C465.247,234.756,465.208,229.985,465.022,225.279z');
    g.appendChild(path);
    g.append(path);
    path.setAttribute('d', 'M414.835,347.816c-8.277-6.21-19.987-4.524-26.186,3.738c-2.374,3.164-4.874,6.289-7.434,9.298 c-6.69,7.86-5.745,19.666,2.115,26.361c0.448,0.38,0.901,0.729,1.371,1.057c7.814,5.509,18.674,4.243,24.992-3.171 c3.057-3.59,6.037-7.323,8.874-11.102C424.767,365.735,423.089,354.017,414.835,347.816z');
    g.appendChild(path);
    g.append(path);
    path.setAttribute('d', 'M442.325,280.213c-9.855-3.09-20.35,2.396-23.438,12.251c-1.182,3.765-2.492,7.548-3.906,11.253 c-3.105,8.156-0.13,17.13,6.69,21.939c1.251,0.879,2.629,1.624,4.126,2.19c9.649,3.682,20.454-1.159,24.132-10.812 c1.679-4.405,3.237-8.906,4.646-13.382C457.66,293.795,452.178,283.303,442.325,280.213z');
    g.appendChild(path);
    g.append(path);
    path.setAttribute('d', 'M197.999,426.402c-16.72-3.002-32.759-8.114-47.968-15.244c-0.18-0.094-0.341-0.201-0.53-0.287 c-3.584-1.687-7.162-3.494-10.63-5.382c-0.012-0.014-0.034-0.023-0.053-0.031c-6.363-3.504-12.573-7.381-18.606-11.628 C32.24,331.86,11.088,209.872,73.062,121.901c13.476-19.122,29.784-35.075,47.965-47.719c0.224-0.156,0.448-0.311,0.67-0.468 c64.067-44.144,151.06-47.119,219.089-1.757l-14.611,21.111c-4.062,5.876-1.563,10.158,5.548,9.518l63.467-5.682 c7.12-0.64,11.378-6.799,9.463-13.675L387.61,21.823c-1.908-6.884-6.793-7.708-10.859-1.833l-14.645,21.161 C312.182,7.638,252.303-5.141,192.87,5.165c-5.986,1.036-11.888,2.304-17.709,3.78c-0.045,0.008-0.081,0.013-0.117,0.021 c-0.225,0.055-0.453,0.128-0.672,0.189C123.122,22.316,78.407,52.207,46.5,94.855c-0.269,0.319-0.546,0.631-0.8,0.978 c-1.061,1.429-2.114,2.891-3.145,4.353c-1.686,2.396-3.348,4.852-4.938,7.308c-0.199,0.296-0.351,0.597-0.525,0.896 C10.762,149.191-1.938,196.361,0.24,244.383c0.005,0.158-0.004,0.317,0,0.479c0.211,4.691,0.583,9.447,1.088,14.129 c0.027,0.302,0.094,0.588,0.145,0.89c0.522,4.708,1.177,9.427,1.998,14.145c8.344,48.138,31.052,91.455,65.079,125.16 c0.079,0.079,0.161,0.165,0.241,0.247c0.028,0.031,0.059,0.047,0.086,0.076c9.142,9.017,19.086,17.357,29.793,24.898 c28.02,19.744,59.221,32.795,92.729,38.808c10.167,1.827,19.879-4.941,21.703-15.103 C214.925,437.943,208.163,428.223,197.999,426.402z');
    g.appendChild(path);
    g.append(path);
    path.setAttribute('d', 'M221.124,83.198c-8.363,0-15.137,6.78-15.137,15.131v150.747l137.87,71.271c2.219,1.149,4.595,1.69,6.933,1.69 c5.476,0,10.765-2.982,13.454-8.185c3.835-7.426,0.933-16.549-6.493-20.384l-121.507-62.818V98.329 C236.243,89.978,229.477,83.198,221.124,83.198z');
    g.appendChild(path);
    svg.appendChild(g);
    g.append(path);
    svg.append(g);
    return svg;
    }
    })());
    btn.addEventListener('click', toggleLoop);
    container.after(btn);
    NOVA.waitSelector('#movie_player video')
    @@ -10767,8 +10680,7 @@ renderTitle = () => reverseBtn.title = `Reverse playlist order is ${window.nova_
    if (window.nova_playlistReversed) reverseBtn.className = CLASS_NAME_ACTIVE;
    reverseBtn.id = SELECTOR_ID;
    renderTitle();
    reverseBtn.appendChild(createSvgIcon());
    function createSvgIcon() {
    reverseBtn.append((function createSvgIcon() {
    const iconButton = document.createElement('yt-icon-button');
    const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
    svg.setAttribute('viewBox', '0 0 381.399 381.399');
    @@ -10777,11 +10689,11 @@ svg.setAttribute('width', '100%');
    const g = document.createElementNS('http://www.w3.org/2000/svg', 'g');
    const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
    path.setAttribute('d', 'M233.757,134.901l-63.649-25.147v266.551c0,2.816-2.286,5.094-5.104,5.094h-51.013c-2.82,0-5.099-2.277-5.099-5.094 V109.754l-63.658,25.147c-2.138,0.834-4.564,0.15-5.946-1.669c-1.389-1.839-1.379-4.36,0.028-6.187L135.452,1.991 C136.417,0.736,137.91,0,139.502,0c1.576,0,3.075,0.741,4.041,1.991l96.137,125.061c0.71,0.919,1.061,2.017,1.061,3.109 c0,1.063-0.346,2.158-1.035,3.078C238.333,135.052,235.891,135.735,233.757,134.901z M197.689,378.887h145.456v-33.62H197.689 V378.887z M197.689,314.444h145.456v-33.622H197.689V314.444z M197.689,218.251v33.619h145.456v-33.619H197.689z');
    g.appendChild(path);
    svg.appendChild(g);
    iconButton.appendChild(svg);
    g.append(path);
    svg.append(g);
    iconButton.append(svg);
    return iconButton;
    }
    })());
    reverseBtn.addEventListener('click', () => {
    reverseBtn.classList.toggle(CLASS_NAME_ACTIVE);
    window.nova_playlistReversed = !window.nova_playlistReversed;
    @@ -11044,7 +10956,7 @@ return;
    NOVA.waitSelector(`${SELECTOR_BELOW} #comments`, { destroy_after_page_leaving: true })
    .then(comments => {
    if (document.body.querySelector('#chat:not([collapsed])')) return;
    document.body.querySelector(`${SELECTOR_SECONDARY}`)?.appendChild(comments);
    document.body.querySelector(`${SELECTOR_SECONDARY}`)?.append(comments);
    comments.style.cssText = 'height:100vh; overflow-y:auto;';
    });
    moveSidebar();
    @@ -11054,7 +10966,7 @@ function moveSidebar() {
    NOVA.waitSelector(`${SELECTOR_SECONDARY} #related`, { destroy_after_page_leaving: true })
    .then(related => {
    if (document.body.querySelector('#chat:not([collapsed])')) return;
    document.body.querySelector('#below')?.appendChild(related);
    document.body.querySelector('#below')?.append(related);
    });
    }
    function moveChannelInfo() {
    @@ -11289,8 +11201,7 @@ NOVA.waitSelector('#filter-button, ytd-shelf-renderer #title-container a[href="/
    .then(container => {
    const filterBtn = document.createElement('button');
    filterBtn.className = 'style-scope yt-formatted-string bold yt-spec-button-shape-next--tonal yt-spec-button-shape-next--mono yt-spec-button-shape-next--size-m yt-spec-button-shape-next--text';
    filterBtn.appendChild(createFilterIcon());
    function createFilterIcon() {
    filterBtn.append((function createFilterIcon() {
    const span = document.createElement('span');
    span.classList.add('yt-spec-button-shape-next__icon');
    span.style.height = '100%';
    @@ -11302,11 +11213,11 @@ const g = document.createElementNS('http://www.w3.org/2000/svg', 'g');
    g.setAttribute('fill', 'currentColor');
    const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
    path.setAttribute('d', 'M128.25,175.6c1.7,1.8,2.7,4.1,2.7,6.6v139.7l60-51.3v-88.4c0-2.5,1-4.8,2.7-6.6L295.15,65H26.75L128.25,175.6z');
    g.appendChild(path);
    svg.appendChild(g);
    span.appendChild(svg);
    g.append(path);
    svg.append(g);
    span.append(svg);
    return span;
    }
    })());
    filterBtn.title = 'Toggle NOVA plugin [thumbs-hide]';
    Object.assign(filterBtn.style, {
    border: 0,
    @@ -11846,8 +11757,7 @@ color: var(--yt-spec-static-overlay-text-primary);
    function renderButton(thumb = required()) {
    const btn = document.createElement('button');
    btn.className = SELECTOR_CLASS_NAME;
    btn.appendChild(createSvgIcon());
    function createSvgIcon() {
    btn.append((function createSvgIcon() {
    const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
    svg.setAttribute('viewBox', '0 0 24 24');
    svg.setAttribute('height', '100%');
    @@ -11856,10 +11766,10 @@ const g = document.createElementNS('http://www.w3.org/2000/svg', 'g');
    g.setAttribute('fill', 'currentColor');
    const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
    path.setAttribute('d', 'M14.97 16.95 10 13.87V7h2v5.76l4.03 2.49-1.06 1.7zM12 3c-4.96 0-9 4.04-9 9s4.04 9 9 9 9-4.04 9-9-4.04-9-9-9m0-1c5.52 0 10 4.48 10 10s-4.48 10-10 10S2 17.52 2 12 6.48 2 12 2z');
    g.appendChild(path);
    svg.appendChild(g);
    g.append(path);
    svg.append(g);
    return svg;
    }
    })());
    btn.title = 'Watch Later';
    btn.addEventListener('click', async evt => {
    evt.preventDefault();
    @@ -12112,8 +12022,7 @@ color: var(--yt-spec-static-overlay-text-primary);
    function renderButton(thumb = required()) {
    const btn = document.createElement('button');
    btn.className = SELECTOR_CLASS_NAME;
    btn.appendChild(createSvgIcon());
    function createSvgIcon() {
    btn.append((function createSvgIcon() {
    const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
    svg.setAttribute('viewBox', '0 0 24 24');
    svg.setAttribute('height', '100%');
    @@ -12122,10 +12031,10 @@ const g = document.createElementNS('http://www.w3.org/2000/svg', 'g');
    g.setAttribute('fill', 'currentColor');
    const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
    path.setAttribute('d', 'M12 2c5.52 0 10 4.48 10 10s-4.48 10-10 10S2 17.52 2 12 6.48 2 12 2zM3 12c0 2.31.87 4.41 2.29 6L18 5.29C16.41 3.87 14.31 3 12 3c-4.97 0-9 4.03-9 9zm15.71-6L6 18.71C7.59 20.13 9.69 21 12 21c4.97 0 9-4.03 9-9 0-2.31-.87-4.41-2.29-6z');
    g.appendChild(path);
    svg.appendChild(g);
    g.append(path);
    svg.append(g);
    return svg;
    }
    })());
    btn.title = 'Not Interested';
    btn.addEventListener('click', async evt => {
    evt.preventDefault();
    @@ -12343,7 +12252,7 @@ cursor: 'pointer',
    });
    const boldText = document.createElement('strong');
    boldText.textContent = 'Nova block channel';
    btn.appendChild(boldText);
    btn.append(boldText);
    btn.title = 'Nova block channel';
    btn.addEventListener('click', () => {
    const currentCannelName = activeThumb?.querySelector('#channel-name a')?.textContent;
    @@ -12785,7 +12694,18 @@ SETTING_BTN_ID = 'nova_settings_button';
    btn.id = SETTING_BTN_ID;
    btn.href = configPage;
    btn.target = '_blank';
    btn.textContent = '►';
    btn.innerHTML = NOVA.createSafeHTML(
    `<yt-icon-button class="style-scope ytd-button-renderer style-default size-default">
    <svg viewBox="-4 0 20 16">
    <radialGradient id="nova-gradient" gradientUnits="userSpaceOnUse" cx="6" cy="22" r="18.5">
    <stop class="nova-gradient-start" offset="0"/>
    <stop class="nova-gradient-stop" offset="1"/>
    </radialGradient>
    <g fill="deepskyblue">
    <polygon points="0,16 14,8 0,0"/>
    </g>
    </svg>
    </yt-icon-button>`);
    Object.assign(btn.style, {
    'font-size': '24px',
    'color': 'deepskyblue !important',
    @@ -12797,7 +12717,7 @@ btn.title = title;
    const tooltip = document.createElement('tp-yt-paper-tooltip');
    tooltip.className = 'style-scope ytd-topbar-menu-button-renderer';
    tooltip.textContent = title;
    btn.appendChild(tooltip);
    btn.append(tooltip);
    NOVA.css.push(
    `#${SETTING_BTN_ID}[tooltip]:hover:after {
    position: absolute;
  10. raingart revised this gist Aug 6, 2024. 1 changed file with 130 additions and 48 deletions.
    178 changes: 130 additions & 48 deletions nova-dev.user.js
    Original file line number Diff line number Diff line change
    @@ -435,22 +435,67 @@ li = document.createElement('tr');
    let replyCount = 0;
    li.className = 'item';
    if (channelName && comment.authorChannelUrl.includes(channelName)) li.classList.add('author');
    li.innerHTML =
    `<td>${comment.likeCount}</td>
    <td sorttable_customkey="${comment.totalReplyCount}" class="${NOVA_REPLYS_SWITCH_CLASS_NAME}">
    ${comment.comments?.length
    ? `<a href="https://www.youtube.com/watch?v=${comment.videoId}&lc=${comment.id}" target="_blank" title="Open comment link">${comment.totalReplyCount}</a> <label for="${replyInputName}"></label>`
    : ''}</td>
    <td sorttable_customkey="${new Date(comment.publishedAt).getTime()}">${NOVA.formatTimeOut.ago(new Date(comment.publishedAt))}</td>
    <td>
    <a href="${comment.authorChannelUrl}" target="_blank" title="${comment.authorDisplayName}">
    <img src="${comment.authorProfileImageUrl}" alt="${comment.authorDisplayName}" />
    </a>
    </td>
    <td sorttable_customkey="${comment.textOriginal.length}">
    <span class="text-overflow-dynamic-ellipsis">${comment.textDisplay}</span>
    ${appendReplies()}
    </td>`;
    function createCommentCell(text, sortKey) {
    const td = document.createElement('td');
    td.textContent = text;
    if (sortKey) {
    td.setAttribute('sorttable_customkey', sortKey);
    }
    return td;
    }
    function createCommentLinkCell(comment, replyInputName) {
    const td = document.createElement('td');
    td.classList.add(NOVA_REPLYS_SWITCH_CLASS_NAME);
    if (comment.comments?.length) {
    const link = document.createElement('a');
    link.href = `https://www.youtube.com/watch?v=${comment.videoId}&lc=${comment.id}`;
    link.target = '_blank';
    link.title = 'Open comment link';
    link.textContent = comment.totalReplyCount;
    const label = document.createElement('label');
    label.htmlFor = replyInputName;
    td.appendChild(link);
    td.appendChild(label);
    }
    return td;
    }
    function createCommentDateCell(comment) {
    const td = document.createElement('td');
    td.setAttribute('sorttable_customkey', new Date(comment.publishedAt).getTime());
    td.textContent = NOVA.formatTimeOut.ago(new Date(comment.publishedAt));
    return td;
    }
    function createCommentAuthorCell(comment) {
    const td = document.createElement('td');
    const link = document.createElement('a');
    link.href = comment.authorChannelUrl;
    link.target = '_blank';
    link.title = comment.authorDisplayName;
    const img = document.createElement('img');
    img.src = comment.authorProfileImageUrl;
    img.alt = comment.authorDisplayName;
    link.appendChild(img);
    td.appendChild(link);
    return td;
    }
    function createCommentTextCell(comment) {
    const td = document.createElement('td');
    const span = document.createElement('span');
    span.classList.add('text-overflow-dynamic-ellipsis');
    span.textContent = comment.textDisplay;
    td.appendChild(span);
    return td;
    }
    const likeCountCell = createCommentCell(comment.likeCount);
    const replyCountCell = createCommentLinkCell(comment, replyInputName);
    const dateCell = createCommentDateCell(comment);
    const authorCell = createCommentAuthorCell(comment);
    const textCell = createCommentTextCell(comment);
    li.appendChild(likeCountCell);
    li.appendChild(replyCountCell);
    li.appendChild(dateCell);
    li.appendChild(authorCell);
    li.appendChild(textCell);
    ul.append(li);
    if (replyCount) {
    const checkbox = document.createElement('input');
    @@ -474,19 +519,29 @@ if (!(reply.snippet.textDisplay = filterStr(reply.snippet.textDisplay, reply.sni
    replyCount++;
    const li = document.createElement('tr');
    if (channelName && reply.snippet.authorChannelUrl.includes(channelName)) li.classList.add('author');
    li.innerHTML =
    `<td>
    <a href="${reply.snippet.authorChannelUrl}" target="_blank" title="${reply.snippet.authorDisplayName}">
    <img src="${reply.snippet.authorProfileImageUrl}" alt="${reply.snippet.authorDisplayName}" />
    </a>
    </td>
    <td>
    <span class="text-overflow-dynamic-ellipsis">
    <div class="nova-reply-time-text">${reply.snippet.likeCount
    ? `${reply.snippet.likeCount} likes` : ''}</div>
    <div>${reply.snippet.textDisplay}</div>
    </span>
    </td>`;
    const authorCell = document.createElement('td');
    const authorLink = document.createElement('a');
    authorLink.href = reply.snippet.authorChannelUrl;
    authorLink.target = '_blank';
    authorLink.title = reply.snippet.authorDisplayName;
    const authorImage = document.createElement('img');
    authorImage.src = reply.snippet.authorProfileImageUrl;
    authorImage.alt = reply.snippet.authorDisplayName;
    authorLink.appendChild(authorImage);
    authorCell.appendChild(authorLink);
    const contentCell = document.createElement('td');
    const contentSpan = document.createElement('span');
    contentSpan.classList.add('text-overflow-dynamic-ellipsis');
    const likeCountDiv = document.createElement('div');
    likeCountDiv.classList.add('nova-reply-time-text');
    likeCountDiv.textContent = reply.snippet.likeCount ? `${reply.snippet.likeCount} likes` : '';
    const textDiv = document.createElement('div');
    textDiv.textContent = reply.snippet.textDisplay;
    contentSpan.appendChild(likeCountDiv);
    contentSpan.appendChild(textDiv);
    contentCell.appendChild(contentSpan);
    li.appendChild(authorCell);
    li.appendChild(contentCell);
    table.append(li);
    });
    return table.outerHTML;
    @@ -523,19 +578,38 @@ return;
    return str;
    }
    const MODAL_CONTENT_FILTER_SELECTOR_ID = 'nova-search-comment';
    document.getElementById(MODAL_CONTENT_SELECTOR_ID).innerHTML =
    `<table class="sortable" border="0" cellspacing="0" cellpadding="0">
    <thead id="${MODAL_CONTENT_FILTER_SELECTOR_ID}">
    <tr>
    <th class="sorttable_numeric">likes</th>
    <th class="sorttable_numeric">replys</th>
    <th class="sorttable_numeric">date</th>
    <th class="sorttable_nosort">avatar</th>
    <th class="sorttable_numeric">comments (${commentList.length})</th>
    </tr>
    </thead>
    <!-- $ {ul.innerHTML} -->
    </table>`;
    const table = document.createElement('table');
    table.classList.add('sortable');
    table.border = '0';
    table.cellSpacing = '0';
    table.cellPadding = '0';
    const thead = document.createElement('thead');
    thead.id = MODAL_CONTENT_FILTER_SELECTOR_ID;
    const headerRow = document.createElement('tr');
    const thLikes = document.createElement('th');
    thLikes.classList.add('sorttable_numeric');
    thLikes.textContent = 'likes';
    headerRow.appendChild(thLikes);
    const thReplys = document.createElement('th');
    thReplys.classList.add('sorttable_numeric');
    thReplys.textContent = 'replys';
    headerRow.appendChild(thReplys);
    const thDate = document.createElement('th');
    thDate.classList.add('sorttable_numeric');
    thDate.textContent = 'date';
    headerRow.appendChild(thDate);
    const thAvatar = document.createElement('th');
    thAvatar.classList.add('sorttable_nosort');
    thAvatar.textContent = 'avatar';
    headerRow.appendChild(thAvatar);
    const thComment = document.createElement('th');
    thComment.classList.add('sorttable_numeric');
    thComment.textContent = `comments (${commentList.length})`;
    headerRow.appendChild(thComment);
    thead.appendChild(headerRow);
    table.appendChild(thead);
    document.getElementById(MODAL_CONTENT_SELECTOR_ID).textContent = '';
    document.getElementById(MODAL_CONTENT_SELECTOR_ID).appendChild(table);
    document.getElementById(MODAL_CONTENT_FILTER_SELECTOR_ID).after(ul);
    connectSortable.apply(this).makeSortable(document.body.querySelector('table.sortable'));
    document.body.querySelector(`table.sortable thead`)
    @@ -12038,12 +12112,20 @@ color: var(--yt-spec-static-overlay-text-primary);
    function renderButton(thumb = required()) {
    const btn = document.createElement('button');
    btn.className = SELECTOR_CLASS_NAME;
    btn.innerHTML =
    `<svg viewBox="0 0 24 24" height="100%" width="100%">
    <g fill="currentColor">
    <path d="M12 2c5.52 0 10 4.48 10 10s-4.48 10-10 10S2 17.52 2 12 6.48 2 12 2zM3 12c0 2.31.87 4.41 2.29 6L18 5.29C16.41 3.87 14.31 3 12 3c-4.97 0-9 4.03-9 9zm15.71-6L6 18.71C7.59 20.13 9.69 21 12 21c4.97 0 9-4.03 9-9 0-2.31-.87-4.41-2.29-6z" />
    </g>
    </svg>`;
    btn.appendChild(createSvgIcon());
    function createSvgIcon() {
    const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
    svg.setAttribute('viewBox', '0 0 24 24');
    svg.setAttribute('height', '100%');
    svg.setAttribute('width', '100%');
    const g = document.createElementNS('http://www.w3.org/2000/svg', 'g');
    g.setAttribute('fill', 'currentColor');
    const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
    path.setAttribute('d', 'M12 2c5.52 0 10 4.48 10 10s-4.48 10-10 10S2 17.52 2 12 6.48 2 12 2zM3 12c0 2.31.87 4.41 2.29 6L18 5.29C16.41 3.87 14.31 3 12 3c-4.97 0-9 4.03-9 9zm15.71-6L6 18.71C7.59 20.13 9.69 21 12 21c4.97 0 9-4.03 9-9 0-2.31-.87-4.41-2.29-6z');
    g.appendChild(path);
    svg.appendChild(g);
    return svg;
    }
    btn.title = 'Not Interested';
    btn.addEventListener('click', async evt => {
    evt.preventDefault();
  11. raingart revised this gist Aug 6, 2024. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion nova-dev.user.js
    Original file line number Diff line number Diff line change
    @@ -2521,7 +2521,7 @@ buffer.id = `${SELECTOR_ID}-buffer`;
    buffer.classList.add('ytp-load-progress');
    const progress = document.createElement('div');
    progress.id = `${SELECTOR_ID}-progress`;
    progress.classList.add('ytp-switch-background-color');
    progress.classList.add('ytp-swatch-background-color');
    const chapters = document.createElement('div');
    chapters.id = `${SELECTOR_ID}-chapters`;
    innerContainer.appendChild(buffer);
  12. raingart created this gist Aug 6, 2024.
    12,790 changes: 12,790 additions & 0 deletions nova-dev.user.js
    12,790 additions, 0 deletions not shown because the diff is too large. Please use a local Git client to view these changes.