JM Shelf - UI Recs

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

สคริปต์นี้ไม่ควรถูกติดตั้งโดยตรง มันเป็นคลังสำหรับสคริปต์อื่น ๆ เพื่อบรรจุด้วยคำสั่งเมทา // @require https://update.sleazyfork.org/scripts/581111/1842612/JM%20Shelf%20-%20UI%20Recs.js

คุณจะต้องติดตั้งส่วนขยาย เช่น Tampermonkey, Greasemonkey หรือ Violentmonkey เพื่อติดตั้งสคริปต์นี้

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

คุณจะต้องติดตั้งส่วนขยาย เช่น Tampermonkey หรือ Violentmonkey เพื่อติดตั้งสคริปต์นี้

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==
// @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;
  }