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