JM Shelf - UI Recs

首页推荐卡片展示 — JM Shelf 推荐脚本的模块库,通过 @require 被主脚本引用。

Этот скрипт недоступен для установки пользователем. Он является библиотекой, которая подключается к другим скриптам мета-ключом // @require https://update.sleazyfork.org/scripts/581111/1842612/JM%20Shelf%20-%20UI%20Recs.js

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

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

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

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

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

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

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

Advertisement:

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

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

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

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

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

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

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

Advertisement:

// ==UserScript==
// @name         JM Shelf - UI Recs
// @namespace    jmshelf-lib
// @version      1.0.0
// @author       Kesdi
// @description  首页推荐卡片展示 — JM Shelf 推荐脚本的模块库,通过 @require 被主脚本引用。
// @license      MIT
// ==/UserScript==
// 
// 此文件是 GreasyFork 库(library),不直接安装。
// 请安装主脚本: JM Shelf 给杂鱼的个性化推荐
//

// ═══ [13] UI RECOMMENDATIONS ═══ (on homepage)
  // ============================================================
  function injectRecommendations(recommendations) {
    const existing = document.getElementById('jms-recommendations');
    if (existing) existing.remove();
    if (!recommendations || recommendations.length === 0) return;

    const displayItems = recommendations.slice(0, CONFIG.DISPLAY_COUNT);

    const wrapper = document.createElement('div');
    wrapper.id = 'jms-recommendations';
    wrapper.className = 'col-lg-12 col-md-12';

    const headerRow = document.createElement('div');
    headerRow.className = 'row';
    headerRow.innerHTML = '<div class="pull-left"><h4 class="talk-title"><img class="img-circle" src="https://cdn-msp3.18comic.vip/templates/frontend/airav/img/title-png/title-circle.png" alt=""><span class="p-r-15 p-l-15 p-t-5 p-b-5" style="margin-left:5px">' + '\uD83C\uDFAF' + ' 为你推荐</span></h4></div><div class="pull-right m-t-10" style="cursor:pointer" title="点击刷新推荐"><span class="talk-more-btn"><span id="jms-refresh-btn" class="p-r-10 p-l-10 p-t-5 p-b-5" style="cursor:pointer;color:#fff;margin-right:3px" title="点击刷新推荐">刷新</span><img class="img-circle" src="https://cdn-msp3.18comic.vip/templates/frontend/airav/img/title-png/more-ms-jm.webp" alt=""></span></div>';
    wrapper.appendChild(headerRow);

    const carouselRow = document.createElement('div');
    carouselRow.className = 'row m-b-10';
    const carousel = document.createElement('div');
    carousel.className = 'owl-comic-block';
    carousel.style.cssText = 'display:flex;flex-direction:row;overflow-x:auto;overflow-y:hidden;padding-left:1.5rem;list-style:none';
    carouselRow.appendChild(carousel);
    wrapper.appendChild(carouselRow);

    const firstHeading = document.querySelector('.talk-title');
    if (firstHeading) {
      const sd = firstHeading.closest('.col-lg-12,.col-md-12') || firstHeading.closest('.row') ? firstHeading.closest('.row').parentElement : null;
      if (sd && sd.parentNode) sd.parentNode.insertBefore(wrapper, sd);
      else (document.querySelector('.container') || document.body).insertBefore(wrapper, (document.querySelector('.container') || document.body).firstChild);
    } else {
      (document.querySelector('.container, [class*="content"], main') || document.body).insertBefore(wrapper, (document.querySelector('.container, [class*="content"], main') || document.body).firstChild);
    }

    setTimeout(() => {
      const pr = wrapper.querySelector('.pull-right.m-t-10');
      if (pr) {
        pr.addEventListener('click', (e) => {
          _cachedReranked = lightRescore();
          _lastLightRescore = Date.now(); _lastCachedT = CONFIG.TEMPERATURE;
          if (_cachedReranked.length > 0) {
            injectRecommendations(_cachedReranked);
          }
          showNotification('🔄 已刷新 (T='+CONFIG.TEMPERATURE.toFixed(2)+')');
        });
      }
    }, 100);

    // Build one card
    function _b(item) {
      const al = State.getAlbumCache()[item.id];
      const ti = al ? al.title || item.title || '' : item.title || '';
      const ta = al ? al.tags || [] : [];
      const au = al ? al.authors || [] : [];
      const tt = al && al.typeTags ? al.typeTags[0] || '' : '';
      const st = al && al.typeTags ? al.typeTags[1] || ta[0] || '' : ta[0] || '';
      const lc = al ? al.likes || al.views || 0 : 0;
      const ls = lc >= 1000 ? (lc/1000).toFixed(0)+'K' : String(lc);
      const mp = Math.round(item.score * 100);
      let rl = '\uD83D\uDCCA 综合', rc = '#0f9';
      var wm = item._wm || '';
      if (wm === 'author+') { rl = '\u270F\uFE0F 作者'; rc = '#e91e63'; }
      else if (wm === 'tag+') { rl = '\uD83C\uDFF7\uFE0F 标签'; rc = '#2196f3'; }
      else if (wm === 'surprise') { rl = '\uD83C\uDFB2 新发现'; rc = '#ff5722'; }
      else if ((item.channels||[]).includes('author')) { rl = '\u270F\uFE0F 作者'; rc = '#e91e63'; }
      else if ((item.channels||[]).some(function(x){return x==='explore'||x==='discover'})) { rl = '\uD83C\uDFB2 探索'; rc = '#9c27b0'; }
      else if ((item.channels||[]).length >= 5) { rl = '\uD83D\uDD17 多源'; rc = '#ff9800'; }
      var tu = 'https://cdn-msp2.18comic.vip/media/albums/' + item.id + '_3x4.jpg';
      var cd = document.createElement('div');
      cd.className = 'owl-item';
      cd.style.cssText = 'width:180px;margin-right:8px;flex-shrink:0';
      cd.innerHTML = '<div class="p-b-15 p-l-5 p-r-5" style="padding-top:0;background:initial;box-shadow:none;margin:0"><div class="thumb-overlay-albums"><a href="/album/' + item.id + '/"><img src="' + tu + '" title="' + escapeHtml(ti) + '" alt="' + escapeHtml(ti) + '" class="img-responsive img-rounded" style="width:100%;aspect-ratio:3/4;object-fit:cover" loading="lazy" referrerpolicy="no-referrer" onerror="var s=this,t=s.src;t.onerror=function(){t.onerror=null;t.src=\'https://cdn-msp.18comic.vip/media/albums/' + item.id + '_3x4.jpg\'};t.src=s.indexOf(\'cdn-msp2\')>-1?\'https://cdn-msp3.18comic.vip/media/albums/' + item.id + '_3x4.jpg\':s.indexOf(\'cdn-msp3\')>-1?\'https://cdn-msp.18comic.vip/media/albums/' + item.id + '_3x4.jpg\':\'https://cdn-msp2.18comic.vip/media/albums/' + item.id + '_3x4.jpg\'"></a><div class="label-loveicon"><a><i class="glyphicon glyphicon-heart"></i><span class="text-white">' + ls + '</span></a></div><div class="label-star" style="cursor:pointer" data-jms-dismiss="' + item.id + '" title="' + '\u4E0D\u611F\u5174\u8DA3' + '"><i style="color:#f66" class="far fa-times-circle"></i></div><div class="label-latest"><div class="label-sub" style="background-color:' + rc + ';font-size:10px">' + rl + '</div><br><div class="label-sub" style="background:#2a2a4a;color:#ffd166;padding:1px 4px;border-radius:2px;margin-top:2px">' + mp + '%</div></div>' + (tt ? '<div class="category-icon"><div class="label-category">' + escapeHtml(tt) + '</div>' + (st ? '<div class="label-sub" style="background-color:#ff7a00">' + escapeHtml(st) + '</div>' : '') + '</div>' : '') + '</div><span class="video-title title-truncate-index m-t-5" style="display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow:hidden;text-overflow:ellipsis;font-size:13px">' + escapeHtml(ti.substring(0, 80)) + '</span><div class="title-truncate-index hidden-xs" style="font-size:11px;color:#888">' + au.slice(0, 2).map(function(a){return '<a href="/search/photos?search_query=' + encodeURIComponent(a) + '&main_tag=2">' + escapeHtml(a) + '</a>'}).join(', ') + ' ' + ta.slice(0, 2).filter(function(t){return !getAllBlacklistedTags().includes(t)}).map(function(t){return '<a href="/search/photos?search_query=' + encodeURIComponent(t) + '">' + escapeHtml(t) + '</a>'}).join(' ') + '</div><div class="clearfix"></div></div>';
      var dis = cd.querySelector('[data-jms-dismiss]');
      if (dis) dis.addEventListener('click', function(e) { e.preventDefault(); e.stopPropagation(); dismissRecommendation(item.id); cd.remove(); });
      return cd;
    }

    var _i = 0, _B = 12;
    function _r() {
      var end = Math.min(_i + _B, displayItems.length);
      for (; _i < end; _i++) carousel.appendChild(_b(displayItems[_i]));
      if (_i < displayItems.length) { requestAnimationFrame(_r); }
    }
    requestAnimationFrame(_r);
  }

  function dismissRecommendation(id) {
    const bl = getBlacklist();
    bl.albums.push(id);
    setBlacklist(bl);
    const recs = State.getRecommendations();
    State.saveRecommendations(recs.filter(r => r.id !== id));
    showNotification('已隐藏,不再推荐此漫画');
  }

  function escapeHtml(str) {
    const div = document.createElement('div');
    div.textContent = str;
    return div.innerHTML;
  }