Guest User

Untitled

a guest
Aug 27th, 2020
274
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // ==UserScript==
  2. // @name       Streaming live translate
  3. // @namespace  youtube.com
  4. // @version    0.8
  5. // @author     u/BakuhatsuK
  6. // @description  Get streaming translation comments easily. Based on extension made by u/konokalahola
  7. // @include    https://*.youtube.com/watch*
  8. // @run-at     document-start
  9. // ==/UserScript==
  10.  
  11. (function () {
  12. 'use strict';
  13.  
  14. const MSG_REGEX = /^\[[\s\S]*en[\s\S]*\].*/i;
  15. const DEBUG = false;
  16.  
  17. const sleep = ms => new Promise(res => setTimeout(res, ms));
  18.  
  19. const whenAvailable = async (selector, wnd = window) => {
  20.     while (true) {
  21.         const elem = wnd.document.querySelector(selector);
  22.         if (!elem) {
  23.             if (DEBUG) console.warn('whenAvailable: Could not find selector', selector);
  24.             await sleep(1000);
  25.         } else {
  26.             if (DEBUG) console.log('whenAvailable: Found selector', selector);
  27.             return elem;
  28.         }
  29.     }
  30. };
  31.  
  32. let ytApp;
  33.  
  34. async function main() {
  35.     if (DEBUG) console.log('main: Running on', window.location.href);
  36.     ytApp = await whenAvailable('ytd-app');
  37.     const appObserver = new MutationObserver(onPageChange);
  38.     appObserver.observe(ytApp, {attributes: true, attributeFilter: ['is-watch-page']});
  39.     // Trigger initial setup as if page had just changed
  40.     onPageChange();
  41. }
  42.  
  43. let chatObserver;
  44.  
  45. async function onPageChange() {
  46.     if (DEBUG) console.log('onPageChange: Init');
  47.     const changedToWatchPage = ytApp.hasAttribute('is-watch-page');
  48.     if (changedToWatchPage) {
  49.         const chatFrame = await whenAvailable('#chatframe');
  50.         const chat = await whenAvailable('#item-offset', chatFrame.contentWindow);
  51.  
  52.         if (DEBUG) console.log('onPageChange: Setting chat observer');
  53.         chatObserver = new MutationObserver(onChatChange);
  54.         chatObserver.observe(chat, {childList: true, subtree: true});
  55.  
  56.         if (DEBUG) console.log('onPageChange: Setting up translation container');
  57.         resetContainer();
  58.         setupContainer();
  59.     } else {
  60.         if (DEBUG) console.log('onPageChange: Disconnecting chat observer');
  61.         if (chatObserver) {
  62.             chatObserver.disconnect();
  63.         }
  64.  
  65.         if (DEBUG) console.log('onPageChange: Removing translation container');
  66.         resetContainer();
  67.     }
  68. }
  69.  
  70. function onChatChange(mutations) {
  71.     for (const mutation of mutations) {
  72.         if (mutation.type !== 'childList') continue;
  73.  
  74.         const chatElems = [...mutation.addedNodes]
  75.             .filter(node => node.nodeType === Node.ELEMENT_NODE)
  76.             .filter(elem => elem.classList.contains('yt-live-chat-item-list-renderer'));
  77.  
  78.         if (chatElems.length === 0) continue;
  79.  
  80.         if (DEBUG) console.log('onChatChange: New messages ', chatElems.length);
  81.  
  82.         for (const chatElem of chatElems) {
  83.             onMessage(chatElem);
  84.         }
  85.     }
  86. }
  87.  
  88. function onMessage(chatElem) {
  89.     const msgElem = chatElem.querySelector('#message');
  90.     if (!msgElem) {
  91.         if (DEBUG) console.warn('onMessage: Could not find message within chatElem', chatElem);
  92.         return;
  93.     }
  94.  
  95.     const msgText = msgElem.textContent;
  96.     const isAMatch = MSG_REGEX.test(msgText)
  97.     if (!isAMatch) return;
  98.  
  99.     const author = chatElem.querySelector('#author-name');
  100.     const authorText = author ? author.textContent : '???';
  101.     if (DEBUG) console.log('onMessage: Matched text', msgText);
  102.  
  103.     updateContainer(msgText, authorText);
  104. }
  105.  
  106. function updateContainer(msg, author) {
  107.     const container = document.getElementById("translate_container");
  108.     if (!container) {
  109.         if (DEBUG) console.warn('updateContainer: Wasn\'t able to show message beacuse container is not ready');
  110.         return;
  111.     }
  112.     const position = container.scrollHeight - container.offsetHeight - 5;
  113.  
  114.     container.insertAdjacentHTML('beforeend', `
  115.         <div style="margin-top: 15px;">
  116.             <b>${author}:</b>&nbsp;&nbsp;${msg}
  117.         </div>
  118.     `);
  119.  
  120.     if (container.scrollTop >= position) {
  121.         container.scrollTo(0, container.scrollHeight);
  122.     }
  123. }
  124.  
  125. function resetContainer() {
  126.     const button = document.getElementById("translate_live_button");
  127.     if (button) button.remove();
  128.  
  129.     const container = document.getElementById("translate_container");
  130.     if (container) container.remove();
  131. }
  132.  
  133. async function setupContainer() {
  134.     const upnext = await whenAvailable("#upnext");
  135.     upnext.insertAdjacentHTML('beforeend', '<svg id="translate_live_button" viewBox="0 0 20 20" width="20" height="20" class="adjustments w-6 h-6" style="vertical-align: middle; margin-left: 7px;"><path fill-rule="evenodd" d="M7 2a1 1 0 011 1v1h3a1 1 0 110 2H9.578a18.87 18.87 0 01-1.724 4.78c.29.354.596.696.914 1.026a1 1 0 11-1.44 1.389c-.188-.196-.373-.396-.554-.6a19.098 19.098 0 01-3.107 3.567 1 1 0 01-1.334-1.49 17.087 17.087 0 003.13-3.733 18.992 18.992 0 01-1.487-2.494 1 1 0 111.79-.89c.234.47.489.928.764 1.372.417-.934.752-1.913.997-2.927H3a1 1 0 110-2h3V3a1 1 0 011-1zm6 6a1 1 0 01.894.553l2.991 5.982a.869.869 0 01.02.037l.99 1.98a1 1 0 11-1.79.895L15.383 16h-4.764l-.724 1.447a1 1 0 11-1.788-.894l.99-1.98.019-.038 2.99-5.982A1 1 0 0113 8zm-1.382 6h2.764L13 11.236 11.618 14z" clip-rule="evenodd"></path></svg>');
  136.     document.getElementById("translate_live_button").style.fill = "gray";
  137.     document.getElementById("upnext").style.display = "flex";
  138.     document.getElementById("translate_live_button").style.display = "block";
  139.     document.getElementById("translate_live_button").onclick = function() {
  140.         if (document.getElementById("translate_live_button").style.fill === "gray") {
  141.             let divTemp = document.getElementById("translate_container");
  142.             document.getElementById("translate_live_button").style.fill = "#c00";
  143.             document.getElementById("info-contents").style.display = "none";
  144.             divTemp.style.display = "block";
  145.             divTemp.scrollTo(0, divTemp.scrollHeight);
  146.         }
  147.         else {
  148.             document.getElementById("translate_live_button").style.fill = "gray";
  149.             document.getElementById("info-contents").style.display = "block";
  150.             document.getElementById("translate_container").style.display = "none";
  151.         }
  152.     };
  153.  
  154.     document.getElementById("info-contents").insertAdjacentHTML('afterend', '<div id="translate_container" style="display: none; font-size: 13px; width: 100%; height: 120px; background-color: white; overflow: hidden; overflow-y: scroll; padding-bottom: 15px; margin-top: 5px; padding-left: 10px; padding-right: 10px;"></div>');
  155.  
  156.     if (DEBUG) console.log("setupContainer: End setup");
  157. }
  158.  
  159. if (document.readyState == "complete" || document.readyState == "loaded" || document.readyState == "interactive") {
  160.     main();
  161. } else {
  162.     document.addEventListener("DOMContentLoaded", main);
  163. }
  164.  
  165. })();
Add Comment
Please, Sign In to add comment