JavLibrary - Enlarge Thumbnails

Replace DMM thumbnails with their respective full cover images

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

Advertisement:

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

Advertisement:

// ==UserScript==
// @license MIT
// @name         JavLibrary - Enlarge Thumbnails
// @namespace    https://sleazyfork.org/en/scripts/584159-javlibrary-enlarge-thumbnails
// @version      1.5
// @description  Replace DMM thumbnails with their respective full cover images
// @match        *://www.javlibrary.com/*
// @match        *://javlibrary.com/*
// @grant        none
// @run-at       document-end
// ==/UserScript==

(function () {
  'use strict';

  // ====== CONFIG ======
  // Minimum card width (px). Number of columns is calculated automatically based on screen width.
  // Larger value = bigger cards & fewer columns, and vice versa.
  const MIN_CARD_WIDTH = 560;
  const GAP = 12; // gap between cards (px)

  // Card text font size. Leave null to AUTO-match the sidebar's font-size.
  // Set manually (e.g. '15px') to force a specific size.
  const FONT_SIZE = null;
  const FONT_SIZE_FALLBACK = '16px'; // used as fallback if sidebar font cannot be detected

  // Maximum number of title lines shown (rest is clipped with ellipsis).
  const TITLE_LINES = 2;
  // ====================

  /* ---------- 1. Swap image ps.jpg -> pl.jpg ---------- */
  function upgradeImg(img) {
    if (img.dataset.jlUpgraded) return;

    const src = img.currentSrc || img.src || img.getAttribute('src') || '';
    if (!/pics\.dmm\.co\.jp/i.test(src)) return;

    const upgraded = src.replace(/ps(\.jpe?g)(\?.*)?$/i, 'pl$1$2');
    if (upgraded === src) return; // not a ps.jpg thumbnail, skip

    img.dataset.jlUpgraded = '1';
    img.dataset.jlOriginal = src;

    img.removeAttribute('width');
    img.removeAttribute('height');
    img.style.setProperty('width', '100%', 'important');
    img.style.setProperty('height', 'auto', 'important');
    img.style.setProperty('max-width', 'none', 'important');
    img.style.setProperty('display', 'block', 'important');

    // fallback: if pl.jpg returns 404, revert to original thumbnail
    img.addEventListener('error', function onErr() {
      if (img.dataset.jlReverted) return;
      img.dataset.jlReverted = '1';
      img.removeEventListener('error', onErr);
      img.src = img.dataset.jlOriginal;
      img.style.setProperty('width', 'auto', 'important');
      img.style.setProperty('max-width', '100%', 'important');
    });

    img.src = upgraded;
  }

  /* ---------- 2. Layout: responsive grid + remove hardcoded heights ---------- */
  function findContainer(cell) {
    // find the ancestor whose direct children include >= 2 .video cards (that is the grid)
    let el = cell.parentElement;
    while (el && el.querySelectorAll(':scope > .video').length < 2) {
      el = el.parentElement;
    }
    return el || cell.parentElement;
  }

  function fixLayout() {
    const cells = document.querySelectorAll('.video');
    if (!cells.length) return;

    const container = findContainer(cells[0]);
    if (container && !container.dataset.jlGrid) {
      container.dataset.jlGrid = '1';
      container.style.setProperty('display', 'grid', 'important');
      container.style.setProperty(
        'grid-template-columns',
        `repeat(auto-fill, minmax(min(${MIN_CARD_WIDTH}px, 100%), 1fr))`,
        'important'
      );
      container.style.setProperty('gap', GAP + 'px', 'important');
      container.style.setProperty('align-items', 'start', 'important');
      container.style.setProperty('height', 'auto', 'important');
      container.style.setProperty('max-height', 'none', 'important');
      container.style.setProperty('overflow', 'visible', 'important');

      // remove hardcoded heights from ancestor elements
      let p = container.parentElement;
      for (let i = 0; i < 3 && p; i++, p = p.parentElement) {
        p.style.setProperty('height', 'auto', 'important');
        p.style.setProperty('max-height', 'none', 'important');
        p.style.setProperty('overflow', 'visible', 'important');
      }
    }

    cells.forEach((cell) => {
      if (cell.dataset.jlCell) return;
      cell.dataset.jlCell = '1';
      cell.style.setProperty('float', 'none', 'important');
      cell.style.setProperty('width', 'auto', 'important');
      cell.style.setProperty('height', 'auto', 'important');
      cell.style.setProperty('margin', '0', 'important');
      cell.style.setProperty('overflow', 'visible', 'important');
    });
  }

  /* ---------- 3. Font size + text width matching the image ---------- */
  function detectSidebarFontSize() {
    const labels = ['Home', 'New Comments', 'New Releases', 'New Entries',
                    'Most Wanted', 'Best Rated', 'Categories', 'Ranking',
                    'Directory', 'Best Reviews', 'Forum'];
    for (const a of document.querySelectorAll('a')) {
      if (labels.includes((a.textContent || '').trim())) {
        return getComputedStyle(a).fontSize;
      }
    }
    return null;
  }

  function applyTextStyle() {
    const size = FONT_SIZE || detectSidebarFontSize() || FONT_SIZE_FALLBACK;
    let st = document.getElementById('jl-font-style');
    if (!st) {
      st = document.createElement('style');
      st.id = 'jl-font-style';
      (document.head || document.documentElement).appendChild(st);
    }
    st.textContent = `
      /* Card: flex column so text stretches to full image width */
      div.video {
        display: flex !important;
        flex-direction: column !important;
        align-items: stretch !important;
      }

      /* Text divs: fill the full card width (= image width) */
      div.video .title,
      div.video .id {
        width: 100% !important;
        max-width: 100% !important;
        box-sizing: border-box !important;
      }

      /* Film ID: single line, ellipsis if too long */
      div.video .id, div.video .id a {
        font-size:     ${size} !important;
        line-height:   1.4 !important;
        white-space:   nowrap !important;
        overflow:      hidden !important;
        text-overflow: ellipsis !important;
        display:       block !important;
      }

      /* Title: max ${TITLE_LINES} lines, rest clipped */
      div.video .title, div.video .title a {
        font-size:          ${size} !important;
        line-height:        1.4 !important;
        display:            -webkit-box !important;
        -webkit-line-clamp: ${TITLE_LINES} !important;
        -webkit-box-orient: vertical !important;
        overflow:           hidden !important;
        white-space:        normal !important;
        text-overflow:      ellipsis !important;
      }
    `;
  }

  /* ---------- 4. Run ---------- */
  function run(root) {
    root.querySelectorAll('img').forEach(upgradeImg);
    fixLayout();
    applyTextStyle();
  }

  run(document);

  let scheduled = false;
  const mo = new MutationObserver((mutations) => {
    for (const m of mutations) {
      m.addedNodes.forEach((node) => {
        if (node.nodeType !== 1) return;
        if (node.tagName === 'IMG') upgradeImg(node);
        else if (node.querySelectorAll) node.querySelectorAll('img').forEach(upgradeImg);
      });
    }
    if (!scheduled) {
      scheduled = true;
      requestAnimationFrame(() => { scheduled = false; fixLayout(); applyTextStyle(); });
    }
  });
  mo.observe(document.documentElement, { childList: true, subtree: true });
})();