Greasy Fork

Greasy Fork is available in English.

Letterboxd Titles

Force titles to be displayed above movie posters (on some pages only)

当前为 2025-05-06 提交的版本,查看 最新版本

// ==UserScript==
// @name         Letterboxd Titles
// @namespace    https://letterboxd.com/
// @version      3.0
// @description  Force titles to be displayed above movie posters (on some pages only)
// @author       n00bCod3r
// @match        https://letterboxd.com/*
// @grant        GM_addStyle
// ==/UserScript==

GM_addStyle(`
:root {
  --w: 50px;
}

.fancy-title {
  text-align: center;
  overflow-wrap: break-word;
  width: var(--w);
  font-size: 10px;
  margin: 0 auto;
}

ul.poster-list, ul.grid {
  align-items: flex-end; //force elements to be vertically aligned at the end
}
`);

'use strict';

const body = document.querySelector('body');
const r = document.querySelector(':root');
const config = {
  childList: true,
  subtree: true
}

let titlesLoaded = false;

const observer = new MutationObserver(mutationRecords => {
  // While every title has not been loaded
  if (!titlesLoaded) {
    const movies = [...document.querySelectorAll('li>div[class*="film-poster-"]')]; // NodeList to Array

    const allMoviesLoaded = movies.length && movies.every(elem => { // Every movie has a title
      const spanElem = elem.querySelector('span.frame-title');
      return Boolean(spanElem.textContent);
    });

    if (allMoviesLoaded) {
      addTitles();
      titlesLoaded = true;
      observer.disconnect();
    }

  }
});

observer.observe(body, config);

document.addEventListener('scroll', () => {
  if (!titlesLoaded) {
    addTitles()
  }
}, false);

function addTitles() {

  const maxTitleLength = 23;
  let movieInfo = "";


  document.querySelectorAll('li>div[class*="film-poster-"]')
    .forEach(elem => {
      r.style.setProperty('--w', `${elem.offsetWidth}px`)

      let spanElem = elem.querySelector('span.frame-title');
      console.log('text', spanElem.textContent)

      //prevent the script to break on scrolling
      if (spanElem.textContent && !elem.dataset.tweaked) {
        //mark tweaked posters for when user will scroll
        elem.dataset.tweaked = 'true';

        let text = `${spanElem.textContent}`;

        // Get title and year
        const [title, year] = text.split(' (');

        // Trim title when too long
        const shortenedTitle = title.length > maxTitleLength ? `${title.substring(0, maxTitleLength-3) + '...'}` : title;

        // Costruisci il risultato
        const movieInfo = `${shortenedTitle} (${year.slice(0, -1)})`;

        // ALTERNATIVE VERSION: SCROLL VIA MARQUEE
        //   const divNode = document.createElement("marquee");
        //   divNode.classList.add('scrolling-title');
        //     divNode.setAttribute('scrollamount', '0'); // Do not scroll initially

        //     divNode.addEventListener('mouseenter', () => { // Scroll on hover
        //         divNode.setAttribute('scrollamount', '10');
        //         divNode.start();
        //     });

        //     divNode.addEventListener('mouseleave', () => { // Stop scrolling on leave
        //         divNode.setAttribute('scrollamount', '0');
        //         divNode.stop();
        //     });
        // END OF ALTERNATIVE VERSION

        // Create elem, add classes, content and place it
        const divNode = document.createElement("div");
        divNode.classList.add('fancy-title');
        divNode.textContent = movieInfo;
        elem.parentNode.insertBefore(divNode, elem); //insert div above the poster image
      }
    });
}