Customized LingQ Reader Theme & Add-ons

I made some adjustments to see a large-sized video with its script without clicking the page flip button.

Adjust the value of variables to fit your resolution or env.

// ==UserScript==
// @name         LingQ Addon
// @description  Custom embedded player, and sort course automatically.
// @match        https://www.lingq.com/*/learn/*/web/reader/*
// @match        https://www.lingq.com/*/learn/*/web/library
// @version      1.3
// ==/UserScript==

(function () {
  "use strict";
  
  // Style settings
  var style = document.createElement("style");
  style.textContent = `
  :root {
    --width_small: 440px;
    --height_small: 260px;
    --right_pos: 0.5%;
    --bottom_pos: 5.5%;

    --width_big: calc(100vw - 424px - 20px);
    --height_big: 460px; /*video height*/
    
    --font_size: 1.1rem; /*reader font size*/
    --font_color: #e0e0e0; /*font color*/
    --line_height: 1.7;
    
    --lingq_background: hsl(41 43% 30% / 0.7);
    --lingq_border: hsl(43 99% 64% / 0.3);
    --lingq_border_learned: hsl(43 99% 64% / 0.5);
    --blue_border: hsl(213 99% 64% / 0.5);
    
    --is_playing_underline: #ffffff;
    
    --grid-layout: 1fr calc(var(--height_big) + 25px) 80px;
    --video_margin: 0 0 90px 10px !important;
    --article_height: calc(var(--app-height) - 210px - var(--height_big));
  }
  
  #lesson-reader {
    grid-template-rows: var(--grid-layout);
    overflow-y: hidden;
  }
  
  .sentence-text {
  height: var(--article_height) !important;
  }

  .reader-container-wrapper {
    height: 100% !important;
  }

  /*video viewer*/
  
  .main-footer.has-video {
    grid-area: 3 / 1 / 3 / 1 !important;
    align-self: end;
  }
  
  .widget-area {
    grid-area: 2 / 2 / -1 / 2 !important;
  }
  
  .main-content {
    grid-template-rows: 45px 1fr !important;
    overflow: hidden; 
    grid-area: 1 / 1 / 1 / -1 !important;
  }
  
  .modal-container .modls {
    pointer-events: none;
    justify-content: end !important;
    align-items: flex-start;
  }
  
  .modal-background {
    background-color: rgb(26 28 30 / 0%) !important;
  }
  
  .modal-section.modal-section--head {
    display: none !important;
  }
  
  .video-player .video-wrapper,
  .sent-video-player .video-wrapper {
    height: var(--height_big);
    overflow: hidden;
    pointer-events: auto;
  }

  .modal.video-player .modal-content {
    max-width: var(--width_big);
    margin: var(--video_margin);
  }

  .video-player.is-minimized .video-wrapper,
  .sent-video-player.is-minimized .video-wrapper {
    height: var(--height_small);
    width: var(--width_small);
  }

  .video-player.is-minimized .modal-content,
  .sent-video-player.is-minimized .modal-content {
    max-width: var(--width_small);
    margin-bottom: 0;
  }

  .video-player.is-minimized,
  .sent-video-player.is-minimized {
    left: auto;
    top: auto;
    right: var(--right_pos);
    bottom: var(--bottom_pos);
    z-index: 99999999;
    overflow: visible
  }

  /*make prev/next page buttons compact*/

  .reader-component {
    grid-template-columns: 0.5rem 1fr 0rem !important;
  }

  .reader-component>div>a.button>span {
    width: 0.5rem !important;
  }

  .reader-component>div>a.button>span>svg {
    width: 15px !important;
    height: 15px !important;
  }

  /*font settings*/

  .reader-container {
    margin: 0 !important;
    float: left !important;
    line-height: var(--line_height) !important;
    padding: 0 !important;
    font-size: var(--font_size) !important;
    columns: unset !important;
    overflow-y: scroll !important;
    max-width: unset !important;
  }

  .reader-container p {
    margin-top: 0 !important;
  }

  .reader-container p span.sentence-item {
    color: var(--font_color) !important;
  }

  .sentence.is-playing,
  .sentence.is-playing span {
    text-underline-offset: .2em !important;
    text-decoration-color: var(--is_playing_underline) !important;
  }
  
  /*LingQ highlightings*/

  .phrase-item {
    padding: 0 !important;
  }

  .phrase-item:not(.phrase-item-status--4, .phrase-item-status--4x2) {
    background-color: var(--lingq_background) !important;
  }

  .phrase-item.phrase-item-status--4, 
  .phrase-item.phrase-item-status--4x2 {
    background-color: rgba(0, 0, 0, 0) !important;
  }

  .phrase-cluster:not(:has(.phrase-item-status--4, .phrase-item-status--4x2)) {
    border: 1px solid var(--lingq_border) !important;
    border-radius: .25rem;
  }

  .phrase-cluster:has(.phrase-item-status--4, .phrase-item-status--4x2) {
    border: 1px solid var(--lingq_border_learned) !important;
    border-radius: .25rem;
  }

  .reader-container .sentence .lingq-word:not(.is-learned) {
    border: 1px solid var(--lingq_border) !important;
    background-color: var(--lingq_background) !important;
  }

  .reader-container .sentence .lingq-word.is-learned {
    border: 1px solid var(--lingq_border_learned) !important;
  }
  
  .reader-container .sentence .blue-word {
    border: 1px solid var(--blue_border) !important;
  }
  
  .phrase-cluster:hover,
  .phrase-created:hover {
    padding: 0 !important;
  }
  
  .phrase-cluster:hover .phrase-item,
  .phrase-created .phrase-item {
    padding: 0 !important;
  }
  
  .reader-container .sentence .selected-text {
    padding: 0 !important;
  }
  `;
  document.querySelector("head").appendChild(style);
  
  
  
  // Add new shortcuts
  document.addEventListener('keydown', function(event) {
    const targetElement = event.target;
    const isTextInput = (targetElement.type === 'text' || targetElement.type === 'textarea');
    if (isTextInput) {
      return;
    }
    
    // video full screen toggle (x key)
    if (event.key === 'x' || event.key === 'X') {
      const full_screen_btn = document.querySelector('.modal-section > div > button:nth-child(2)');
      if (full_screen_btn) {
        full_screen_btn.click();
      }
    }
  });
  

  // Custom embedded player
  function replaceNoCookie() {
    document.querySelectorAll('iframe').forEach(function(iframe) {
        let src = iframe.getAttribute('src');

        if (src && src.includes('autoplay=0')) {
            src = src.replace('autoplay=0', 'autoplay=1'); // video will automatically start to play.
            src = src.replace('disablekb=1', 'disablekb=0'); // keyboard controls are enabled.
            src = src + '&cc_load_policy=1' // caption is shown by default.
            src = src + '&controls=0' // player controls do not display in the player.
            iframe.setAttribute('src', src);
            console.log(src);
        }
    });
  }

  const iframeObserver = new MutationObserver(function(mutationsList, observer) {
      for (const mutation of mutationsList) {
          if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
              mutation.addedNodes.forEach(node => {
                  if (node.nodeName === 'IFRAME') {
                      replaceNoCookie();
                  } else if (node.querySelectorAll) {
                      node.querySelectorAll('iframe').forEach(replaceNoCookie);
                  }
              });
          } else if (mutation.type === 'attributes' && mutation.attributeName === 'src' && mutation.target.nodeName === 'IFRAME') {
              replaceNoCookie();
          }
      }
  });

  if (document.URL.includes("/reader/")) {
    iframeObserver.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['src'] });
  }


  // Change the amount of a scroll
  setTimeout(() => {
    console.log('scroll event!');
    const myDiv = document.querySelector('.reader-container');
    myDiv.addEventListener('wheel', (event) => {
      event.preventDefault();
      const delta = event.deltaY;
      const scrollAmount = 0.3;
      myDiv.scrollTop += delta * scrollAmount;
    });
  }, 3000);


  // Sort courses
  function addClickListener() {
    document.querySelectorAll("div.library-item-wrap").forEach((item) => {
      if (!item.dataset.listenerAdded) {
        item.addEventListener("click", function () {
          setTimeout(() => {
            /* Change the number in .dropdown-item:nth-child(3) by your preference
               1: All lessons
               2: Oldest to Newest
               3: Newest to Oldest
               4: Last Opened
               5: New Words %
               6: A-Z */
            document.querySelector(
              ".library-item--menu-box .collection-section--controllers .dropdown-item:nth-child(5)"
            ).click();
          }, 500);
        });
        item.dataset.listenerAdded = true;
      }
    });
  }

  const libraryObserver = new MutationObserver(addClickListener);
  if (document.URL.includes("/library")) {
    libraryObserver.observe(document.body, { childList: true, subtree: true });
    addClickListener();
  }
})();

You can adjust video size and layout by changing “–height_big” variable’s value only, others are automatically changed.

If it’s useful, plz hit the like button below.

2 Likes