JavLibrary - Enlarge Thumbnails

Replace DMM thumbnails with their respective full cover images

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey, Greasemonkey или Violentmonkey.

Для установки этого скрипта вам необходимо установить расширение, такое как Tampermonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Violentmonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Userscripts.

Чтобы установить этот скрипт, сначала вы должны установить расширение браузера, например Tampermonkey.

Чтобы установить этот скрипт, вы должны установить расширение — менеджер скриптов.

(у меня уже есть менеджер скриптов, дайте мне установить скрипт!)

Advertisement:

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

(у меня уже есть менеджер стилей, дайте мне установить скрипт!)

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 });
})();