Bangumi 社区月刊组件

在首页右侧显示「Bangumi社区月刊」卡片,点击查看最新一期月刊。

// ==UserScript==
// @name         Bangumi 社区月刊组件
// @version      1.0.0
// @description  在首页右侧显示「Bangumi社区月刊」卡片,点击查看最新一期月刊。
// @author       zintop
// @match        https://bgm.tv/
// @match        https://bangumi.tv/
// @match        https://chii.in/
// @run-at       document-idle
// @connect      raw.githubusercontent.com
// @license      MIT
// @namespace https://greasyfork.org/users/1386262
// ==/UserScript==

(async function () {
  'use strict';

  // 获取数据
  const JSON_URL = 'https://raw.githubusercontent.com/zintop/bangumi_monthly/main/bangumi_monthly.json';
  let COVER_IMG = '', ISSUE_TITLE = '', ISSUE_URL_CHAHUA = '', ISSUE_URL_YUEKAN = '';

  try {
    const res = await fetch(JSON_URL);
    const data = await res.json();
    COVER_IMG = data.coverImg || '';
    ISSUE_TITLE = data.issueTitle || '';
    ISSUE_URL_CHAHUA = data.issueUrlChahua || '';
    ISSUE_URL_YUEKAN = data.issueUrlYuekan || '';
  } catch (err) {
    console.error('无法加载 Bangumi 月刊 JSON:', err);
    return;
  }

  // 注入样式
  const injectStyles = () => {
    const styles = `
      #monthly-card {
        cursor: pointer;
        border-radius: 5px;
        height: 50px;
        padding: 10px;
        overflow: hidden;
        position: relative;
        background-color: #f0f0f0;
        user-select: none;
        margin-bottom: 10px;
        background-size: cover;
        background-position: center;
        transition: filter 0.2s ease;
      }
      #monthly-card:hover { filter: brightness(1.1); }
      #monthly-card::before {
        content: '';
        position: absolute;
        inset: 0;
        background: linear-gradient(transparent, rgba(0,0,0,0.3));
        border-radius: 5px;
        z-index: 1;
      }
      #monthly-card .card-text {
        position: relative;
        z-index: 2;
        text-align: right;
        margin-top: 30px;
        font-size: 1.3em;
        text-shadow: 0 0 10px rgba(0,0,0,0.3);
        line-height: 150%;
        font-weight: 900;
        color: white;
        font-family: 'PingFang SC','Lucida Grande','Helvetica Neue',Helvetica,Arial,Verdana,sans-serif,"Hiragino Sans GB";
        pointer-events: none;
      }
      /* 弹窗 */
      #monthly-modal {
        display: none;
        position: fixed;
        inset: 0;
        background: rgba(0,0,0,0.15);
        z-index: 9999;
        justify-content: center;
        align-items: center;
        backdrop-filter: blur(8px);
        animation: fadeIn 0.3s ease;
      }
      #monthly-modal.active { display: flex; }
      #monthly-modal .modal-content {
        position: relative;
        border-radius: 20px;
        padding: 8px;
        background: rgba(255,255,255,0.02);
        backdrop-filter: blur(25px);
        max-width: 90%;
        max-height: 90%;
        display: flex;
        flex-direction: column;
        overflow: hidden;
        box-shadow: 0 12px 40px rgba(0,0,0,0.35);
        animation: slideUp 0.35s ease;
      }
      #monthly-modal .inner-frame {
        border-radius: 16px;
        border: 6px solid rgba(255,255,255,0.12);
        padding: 4px;
        background: rgba(255,255,255,0.02);
        display: flex;
        justify-content: center;
        align-items: center;
      }
      #monthly-modal .inner-frame img {
        border-radius: 12px;
        max-width: 100%;
        max-height: 80vh;
        object-fit: contain;
      }
      #monthly-modal .footer {
        padding: 10px 12px;
        text-align: center;
        font-size: 1em;
        color: #e0e0e0;
        font-weight: 700;
      }
      #monthly-modal .footer a {
        color: #a0d8ff;
        text-decoration: none;
        margin: 0 6px;
        transition: color 0.2s, text-shadow 0.2s;
      }
      #monthly-modal .footer a:hover {
        color: #66bfff;
        text-shadow: 0 0 4px rgba(0,0,0,0.5);
      }
      @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
      @keyframes slideUp { from { transform: translateY(20px); opacity: 0; } to { transform: translateY(0); opacity: 1; } }
    `;
    document.head.appendChild(Object.assign(document.createElement("style"), { textContent: styles }));
  };

  // 注入卡片
  const injectCard = () => {
    if (window.location.pathname === "/") {
      setTimeout(() => {
        const sideInner = document.querySelector("#columnHomeB .sideInner");
        if (sideInner) {
          const cardHTML = `
            <div id="monthly-card" class="SidePanelMini" style="background-image: url('${COVER_IMG}');">
              <p class="card-text">${ISSUE_TITLE}</p>
            </div>
            <div id="monthly-modal">
              <div class="modal-content">
                <div class="inner-frame">
                  <img src="${COVER_IMG}" alt="社区月刊封面" />
                </div>
                <div class="footer">
                  新一期月刊已发布,点击前往:
                  <a href="${ISSUE_URL_CHAHUA}" target="_blank">靠谱人生茶话会</a> |
                  <a href="${ISSUE_URL_YUEKAN}" target="_blank">Bangumi社区月刊</a>
                </div>
              </div>
            </div>
          `;
          sideInner.insertAdjacentHTML("beforebegin", cardHTML);

          const card = document.getElementById("monthly-card");
          const modal = document.getElementById("monthly-modal");
          card.addEventListener("click", () => modal.classList.add("active"));
          modal.addEventListener("click", (e) => { if (e.target === modal) modal.classList.remove("active"); });
        }
      }, 0);
    }
  };

  injectStyles();
  injectCard();
})();