Files
Stardream e3bf77296c
Build and Push Docker Image / build (push) Successful in 17s
Initial release
2026-06-16 20:27:36 +10:00

332 lines
33 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Bangumi 保管库</title>
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1024 1024' fill='%23FF6CB8'><path d='M228.115268 615.399298a12.300795 12.300795 0 0 0 11.35458 7.569719 12.471113 12.471113 0 0 0 4.749999-0.965139l147.609537-61.882459a12.300795 12.300795 0 0 0 0.26494-22.557765l-147.609537-66.235049a12.300795 12.300795 0 1 0-10.067727 22.444219l121.740019 54.634453-121.456155 50.906366a12.300795 12.300795 0 0 0-6.585656 16.085655zM399.020617 627.965033H239.469848a12.300795 12.300795 0 0 0 0 24.601589h159.550769a12.300795 12.300795 0 0 0 0-24.601589zM399.020617 667.460046H239.469848a12.300795 12.300795 0 0 0 0 24.601589h159.550769a12.300795 12.300795 0 0 0 0-24.601589zM872.941851 476.892349l-133.283841 58.381464a12.300795 12.300795 0 0 0-0.397411 22.349598l133.302766 64.058754a12.073703 12.073703 0 0 0 5.317729 1.23008 12.300795 12.300795 0 0 0 5.336652-23.390435l-109.15536-52.42031L882.896033 499.469038a12.300795 12.300795 0 1 0-9.954182-22.576689zM877.881094 627.965033h-148.101569a12.300795 12.300795 0 0 0 0 24.601589h148.101569a12.300795 12.300795 0 0 0 0-24.601589zM877.881094 667.460046h-148.101569a12.300795 12.300795 0 0 0 0 24.601589h148.101569a12.300795 12.300795 0 0 0 0-24.601589zM644.866193 537.128395h-162.919295a12.28187 12.28187 0 0 0-10.711153 18.318722l81.374488 145.130453a12.300795 12.300795 0 0 0 21.460155 0l81.374489-145.130453a12.300795 12.300795 0 0 0-10.730078-18.318722z m-81.374488 132.299778l-60.444213-107.698189h120.888426z'/><path d='M891.411968 334.960102H648.405037c-6.812748-15.13944-19.813742-28.386449-36.864535-38.018917L803.092262 19.283861a12.300795 12.300795 0 0 0-20.249001-13.966133L588.566402 286.873457a147.723082 147.723082 0 0 0-45.418319-7.001991 151.507942 151.507942 0 0 0-31.887445 3.368526L239.980804 4.712151A12.300795 12.300795 0 0 0 222.437978 21.87649l262.726051 269.803739c-22.14143 9.821711-39.116527 25.112546-47.310749 43.242025H132.547555A91.763929 91.763929 0 0 0 40.764702 426.705107v414.44216A91.763929 91.763929 0 0 0 132.547555 932.967969h268.024855l-19.908363 46.989036c-12.641432 29.881469 22.614538 57.094612 48.294812 37.299794L538.473781 932.967969h352.938187a91.763929 91.763929 0 0 0 91.782853-91.782853v-414.442161a91.763929 91.763929 0 0 0-91.782853-91.782853z m34.839635 463.815658a60.709153 60.709153 0 0 1-60.709153 60.709153H585.670984L487.870204 932.967969l-77.002975 57.851583 24.412346-57.851583 31.016927-73.483056H198.082405A60.728077 60.728077 0 0 1 137.27863 798.737912V440.330602a60.728077 60.728077 0 0 1 60.728077-60.728077h667.460046a60.709153 60.709153 0 0 1 60.709153 60.728077z'/></svg>" />
<meta name="description" content="Bangumi 本地保险箱:收藏、标签、吐槽、评分、进度、历史版本与离线导出。" />
<link rel="stylesheet" href="css/fonts.css" />
<link rel="stylesheet" href="css/styles.css" />
</head>
<body>
<div id='loadingScreen'><div class='ld-inner'><div class='bangumi-logo'><svg viewBox='0 0 1024 1024' fill='#FF6CB8' aria-hidden='true'><path d='M228.115268 615.399298a12.300795 12.300795 0 0 0 11.35458 7.569719 12.471113 12.471113 0 0 0 4.749999-0.965139l147.609537-61.882459a12.300795 12.300795 0 0 0 0.26494-22.557765l-147.609537-66.235049a12.300795 12.300795 0 1 0-10.067727 22.444219l121.740019 54.634453-121.456155 50.906366a12.300795 12.300795 0 0 0-6.585656 16.085655zM399.020617 627.965033H239.469848a12.300795 12.300795 0 0 0 0 24.601589h159.550769a12.300795 12.300795 0 0 0 0-24.601589zM399.020617 667.460046H239.469848a12.300795 12.300795 0 0 0 0 24.601589h159.550769a12.300795 12.300795 0 0 0 0-24.601589zM872.941851 476.892349l-133.283841 58.381464a12.300795 12.300795 0 0 0-0.397411 22.349598l133.302766 64.058754a12.073703 12.073703 0 0 0 5.317729 1.23008 12.300795 12.300795 0 0 0 5.336652-23.390435l-109.15536-52.42031L882.896033 499.469038a12.300795 12.300795 0 1 0-9.954182-22.576689zM877.881094 627.965033h-148.101569a12.300795 12.300795 0 0 0 0 24.601589h148.101569a12.300795 12.300795 0 0 0 0-24.601589zM877.881094 667.460046h-148.101569a12.300795 12.300795 0 0 0 0 24.601589h148.101569a12.300795 12.300795 0 0 0 0-24.601589zM644.866193 537.128395h-162.919295a12.28187 12.28187 0 0 0-10.711153 18.318722l81.374488 145.130453a12.300795 12.300795 0 0 0 21.460155 0l81.374489-145.130453a12.300795 12.300795 0 0 0-10.730078-18.318722z m-81.374488 132.299778l-60.444213-107.698189h120.888426z'></path><path d='M891.411968 334.960102H648.405037c-6.812748-15.13944-19.813742-28.386449-36.864535-38.018917L803.092262 19.283861a12.300795 12.300795 0 0 0-20.249001-13.966133L588.566402 286.873457a147.723082 147.723082 0 0 0-45.418319-7.001991 151.507942 151.507942 0 0 0-31.887445 3.368526L239.980804 4.712151A12.300795 12.300795 0 0 0 222.437978 21.87649l262.726051 269.803739c-22.14143 9.821711-39.116527 25.112546-47.310749 43.242025H132.547555A91.763929 91.763929 0 0 0 40.764702 426.705107v414.44216A91.763929 91.763929 0 0 0 132.547555 932.967969h268.024855l-19.908363 46.989036c-12.641432 29.881469 22.614538 57.094612 48.294812 37.299794L538.473781 932.967969h352.938187a91.763929 91.763929 0 0 0 91.782853-91.782853v-414.442161a91.763929 91.763929 0 0 0-91.782853-91.782853z m34.839635 463.815658a60.709153 60.709153 0 0 1-60.709153 60.709153H585.670984L487.870204 932.967969l-77.002975 57.851583 24.412346-57.851583 31.016927-73.483056H198.082405A60.728077 60.728077 0 0 1 137.27863 798.737912V440.330602a60.728077 60.728077 0 0 1 60.728077-60.728077h667.460046a60.709153 60.709153 0 0 1 60.709153 60.728077z'></path></svg></div><div class='ld-title'>Bangumi 保管库</div><div class='ld-dots'><span></span><span></span><span></span></div></div></div>
<div class="login-shell" id="loginShell">
<form class="login-card" id="loginForm">
<div class="field"><label>管理员密码</label><input id="adminPasswordInput" type="password" autocomplete="current-password" autofocus /></div>
<button class="btn primary" id="loginSubmitBtn" type="submit" style="width:100%">登录</button>
<div class="auth-status" id="loginStatus"></div>
</form>
</div>
<div class="side-backdrop" id="sideBackdrop"></div>
<div class="app" id="appRoot">
<aside class="side">
<div class="brand">
<a class="bangumi-logo" href="https://bgm.tv" target="_blank" rel="noopener" title="Bangumi 番组计划">
<svg viewBox="0 0 1024 1024" fill="#FF6CB8" aria-label="Bangumi" role="img"><path d="M228.115268 615.399298a12.300795 12.300795 0 0 0 11.35458 7.569719 12.471113 12.471113 0 0 0 4.749999-0.965139l147.609537-61.882459a12.300795 12.300795 0 0 0 0.26494-22.557765l-147.609537-66.235049a12.300795 12.300795 0 1 0-10.067727 22.444219l121.740019 54.634453-121.456155 50.906366a12.300795 12.300795 0 0 0-6.585656 16.085655zM399.020617 627.965033H239.469848a12.300795 12.300795 0 0 0 0 24.601589h159.550769a12.300795 12.300795 0 0 0 0-24.601589zM399.020617 667.460046H239.469848a12.300795 12.300795 0 0 0 0 24.601589h159.550769a12.300795 12.300795 0 0 0 0-24.601589zM872.941851 476.892349l-133.283841 58.381464a12.300795 12.300795 0 0 0-0.397411 22.349598l133.302766 64.058754a12.073703 12.073703 0 0 0 5.317729 1.23008 12.300795 12.300795 0 0 0 5.336652-23.390435l-109.15536-52.42031L882.896033 499.469038a12.300795 12.300795 0 1 0-9.954182-22.576689zM877.881094 627.965033h-148.101569a12.300795 12.300795 0 0 0 0 24.601589h148.101569a12.300795 12.300795 0 0 0 0-24.601589zM877.881094 667.460046h-148.101569a12.300795 12.300795 0 0 0 0 24.601589h148.101569a12.300795 12.300795 0 0 0 0-24.601589zM644.866193 537.128395h-162.919295a12.28187 12.28187 0 0 0-10.711153 18.318722l81.374488 145.130453a12.300795 12.300795 0 0 0 21.460155 0l81.374489-145.130453a12.300795 12.300795 0 0 0-10.730078-18.318722z m-81.374488 132.299778l-60.444213-107.698189h120.888426z"></path><path d="M891.411968 334.960102H648.405037c-6.812748-15.13944-19.813742-28.386449-36.864535-38.018917L803.092262 19.283861a12.300795 12.300795 0 0 0-20.249001-13.966133L588.566402 286.873457a147.723082 147.723082 0 0 0-45.418319-7.001991 151.507942 151.507942 0 0 0-31.887445 3.368526L239.980804 4.712151A12.300795 12.300795 0 0 0 222.437978 21.87649l262.726051 269.803739c-22.14143 9.821711-39.116527 25.112546-47.310749 43.242025H132.547555A91.763929 91.763929 0 0 0 40.764702 426.705107v414.44216A91.763929 91.763929 0 0 0 132.547555 932.967969h268.024855l-19.908363 46.989036c-12.641432 29.881469 22.614538 57.094612 48.294812 37.299794L538.473781 932.967969h352.938187a91.763929 91.763929 0 0 0 91.782853-91.782853v-414.442161a91.763929 91.763929 0 0 0-91.782853-91.782853z m34.839635 463.815658a60.709153 60.709153 0 0 1-60.709153 60.709153H585.670984L487.870204 932.967969l-77.002975 57.851583 24.412346-57.851583 31.016927-73.483056H198.082405A60.728077 60.728077 0 0 1 137.27863 798.737912V440.330602a60.728077 60.728077 0 0 1 60.728077-60.728077h667.460046a60.709153 60.709153 0 0 1 60.709153 60.728077z"></path></svg>
</a>
<div><h1>Bangumi 保管库</h1><p>本地收藏备份</p></div>
</div>
<div class="stats">
<div class="stat"><b id="statTotal">0</b><span>本地条目</span></div>
<div class="stat"><b id="statMissing">0</b><span>疑似消失</span></div>
<div class="stat stat-clickable" id="statCommentsCard" role="button" tabindex="0" title="点击只看有吐槽的条目(再点取消)"><b id="statComments">0</b><span>有吐槽</span></div>
<div class="stat"><b id="statTags">0</b><span>标签数</span></div>
</div>
<div class="nav-title">收藏状态</div>
<div class="nav" id="statusNav"></div>
<div class="nav-title tag-head"><span>标签筛选</span><button class="tag-search-toggle" id="tagSearchToggle" type="button" aria-label="搜索标签"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><circle cx="11" cy="11" r="7"/><line x1="20.5" y1="20.5" x2="16.5" y2="16.5"/></svg></button></div>
<div class="tag-filter" id="tagFilter">
<svg class="tag-filter-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><circle cx="11" cy="11" r="7"/><line x1="20.5" y1="20.5" x2="16.5" y2="16.5"/></svg>
<input id="tagFilterInput" class="tag-filter-input" type="text" autocomplete="off" spellcheck="false" aria-label="过滤标签" />
</div>
<div class="tag-list" id="tagList"></div>
<button class="tag-list-more" id="tagListMore" type="button">展开更多</button>
<div class="drawer-actions" id="drawerActions">
<div class="nav-title">操作</div>
<button class="btn" id="mSettings" type="button">设置</button>
<button class="btn primary" id="mSyncRecent" type="button">同步最近</button>
<button class="btn warn" id="mFullSync" type="button">完整同步</button>
<button class="btn" id="mExport" type="button">导出备份</button>
<button class="btn" id="mImport" type="button">导入备份</button>
<button class="btn" id="mCacheCovers" type="button">缓存当前封面</button>
<button class="btn" id="mEnrichTags" type="button">刷新公共标签</button>
</div>
<div class="footer-note">
<div id="lastSyncText">尚未同步</div>
<div style="margin-top:8px">Powered by <a href="https://git.stdm.moe/Stardream/Bangumi-Vault" target="_blank" rel="noopener" style="color:inherit;text-decoration:none">Bangumi-Vault</a></div>
<div style="margin-top:8px">Copyright © 2026 AKISATO · MIT License</div>
</div>
</aside>
<main class="main">
<div class="top">
<div class="row">
<button class="btn menu-btn" id="menuBtn" type="button" aria-label="菜单"></button>
<div class="title">目录</div>
<span class="pill" id="accountPill">未登录</span>
<span class="pill" id="countPill">0 条</span>
<div id="syncMiniBar" title="点击展开同步进度">
<div class="smi-ring"></div>
<div class="smi-body"><div class="smi-label" id="syncMiniLabel">同步中…</div><div class="smi-track"><div class="smi-fill" id="syncMiniFill"></div></div></div>
</div>
<div class="grow"></div>
<button class="btn primary" id="syncBtn" title="只扫描最近自定义月份内更新过的收藏;不会触发安全箱删除判断,适合日常同步。">同步最近</button>
<button class="btn warn" id="fullSyncBtn" title="完整分页核对全部收藏;只有这个模式会做远端缺失检查并把疑似消失条目放入安全箱。">完整同步</button>
<button class="btn" id="exportBtn">导出备份</button>
<button class="btn" id="settingsBtn">设置</button>
<button class="btn small theme-toggle" id="themeBtnTop" type="button" aria-label="切换明暗"></button>
</div>
<div class="row" style="margin-top:14px">
<div class="search"><input id="searchInput" placeholder="搜索标题、吐槽、简介、年份…" /></div>
<div class="selectish" id="typeDropdown">
<button class="btn select-trigger" id="typeDropdownBtn" type="button"><span id="typeDropdownLabel">全部分类</span><span class="chev" aria-hidden="true"></span></button>
<div class="select-menu" role="menu">
<button data-value="0" class="active">全部分类</button><button data-value="2">动画</button><button data-value="1">书籍</button><button data-value="4">游戏</button><button data-value="3">音乐</button><button data-value="6">三次元</button>
</div>
</div>
<select class="hidden" id="typeFilter" aria-label="条目分类"><option value="0">全部分类</option><option value="2">动画</option><option value="1">书籍</option><option value="4">游戏</option><option value="3">音乐</option><option value="6">三次元</option></select>
<div class="selectish" id="sortDropdown">
<button class="btn select-trigger" id="sortDropdownBtn" type="button"><span id="sortDropdownLabel">按更新时间新到旧</span><span class="chev" aria-hidden="true"></span></button>
<div class="select-menu" role="menu">
<button data-value="updated_desc" class="active">按更新时间新到旧</button><button data-value="updated_asc">按更新时间旧到新</button><button data-value="rate_desc">按我的评分高到低</button><button data-value="name_asc">按标题 A-Z</button><button data-value="first_seen_desc">按首次备份新到旧</button>
</div>
</div>
<select class="hidden" id="sortSelect" aria-label="排序"><option value="updated_desc">按更新时间新到旧</option><option value="updated_asc">按更新时间旧到新</option><option value="rate_desc">按我的评分高到低</option><option value="name_asc">按标题 A-Z</option><option value="first_seen_desc">按首次备份新到旧</option></select>
<button class="btn theme-toggle" id="themeBtn" type="button" aria-label="切换明暗"></button>
</div>
</div>
<div class="filters">
<div class="filters-left">
<span class="pill" id="filterSummary">全部条目</span>
<span class="pill sync-explain" id="syncModePill" title="显示上次同步的触发方式和模式">最近同步 · 手动增量</span>
</div>
<div class="filters-right">
<button class="btn small" id="nsfwBtn" title="点击循环切换:全部显示 → 屏蔽 NSFW → 仅 NSFW">NSFW</button>
<button class="btn small" id="cacheCoversBtn">缓存当前封面到目录</button>
<button class="btn small" id="enrichTagsBtn" title="为当前筛选结果刷新条目的公共标签;会覆盖旧的公共标签缓存。">刷新公共标签</button>
<button class="btn small" id="importBtn">导入备份</button>
</div>
</div>
<div class="content" id="content"></div>
</main>
</div>
<div class="drawer-mask" id="drawerMask"></div>
<section class="drawer" id="drawer"></section>
<div class='modal-mask' id='settingsModal'>
<div class='modal-card settings-card'>
<div class='row'><h2 style='margin:0'>设置</h2><div class='grow'></div><button class='btn small' data-close='settingsModal'>关闭</button></div>
<div class='settings-tabs'>
<button class='settings-tab active' data-tab='stab-account'>账户</button>
<button class='settings-tab' data-tab='stab-sync'>同步</button>
<button class='settings-tab' data-tab='stab-appearance'>外观</button>
<button class='settings-tab' data-tab='stab-advanced'>高级</button>
<button class='settings-tab hidden' data-tab='stab-login'>管理员登录</button>
</div>
<div class='settings-pane active' id='stab-account'>
<div class='split'>
<div class='field'><label>Access Token</label><textarea id='tokenInput' rows='4' placeholder='粘贴 Bangumi Access Token' class='masked-input'></textarea></div>
<div class='field'><label>用户名</label><input id='usernameInput' placeholder='可留空,点击测试登录自动读取' /><div class='help'>用户名用于读取 /v0/users/{username}/collections。</div></div>
</div>
<div class='row' style='margin-top:16px'>
<button class='btn' id='openTokenBtn'>获取 Access Token</button>
<button class='btn good' id='testLoginBtn'>测试登录</button>
</div>
<div class='sync-log' id='loginLog' style='margin-top:14px' aria-live='polite'></div>
</div>
<div class='settings-pane' id='stab-sync'>
<div class='sync-master'>
<div>
<div class='setting-title' style='margin-bottom:2px'>自动同步</div>
<div class='setting-help' style='margin-top:0'>按设定的调度频率在后台自动同步收藏。</div>
</div>
<label style='flex:none;cursor:pointer;display:inline-flex;align-items:center'><input type='checkbox' id='autoSyncEnabledInput' class='switch-input' /><span class='switch'></span></label>
</div>
<div id='autoSyncOptions'>
<div class='setting-card'>
<div class='setting-title'>同步方式</div>
<div class='segmented' data-segment='autoSyncMode' style='width:max-content;margin-top:6px'>
<button data-value='recent'>增量</button>
<button data-value='full'>完整</button>
</div>
<div class='setting-help'>增量只读取最近更新的收藏;完整会核对全部收藏并触发安全箱判断。</div>
</div>
<div class='setting-card'>
<div class='setting-title'>同步范围</div>
<div class='row' style='gap:8px;align-items:center;flex-wrap:nowrap;margin-top:6px;max-width:440px'>
<input id='recentMonthsInput' type='number' min='1' max='999' step='1' placeholder='例如 3' style='flex:1;min-width:0' />
<div class='selectish' id='autoSyncRangeUnitDropdown' style='flex:none'>
<button class='btn select-trigger' type='button' style='min-width:84px;padding-left:12px'><span id='autoSyncRangeUnitLabel'></span><span class='chev' aria-hidden='true'></span></button>
<div class='select-menu' role='menu' style='min-width:120px'>
<button data-value='minute'>分钟</button>
<button data-value='hour'>小时</button>
<button data-value='day'></button>
<button data-value='month'></button>
</div>
</div>
<select class='hidden' id='autoSyncRangeUnit'><option value='minute'>分钟</option><option value='hour'>小时</option><option value='day'></option><option value='month'></option></select>
<button class='btn small' id='autoSyncRangeBtn' type='button' style='flex:none'>跟随调度频率</button>
</div>
<div class='setting-help' id='autoSyncRangeHelp'>仅用于自动同步:从每个分类/状态读取最近 N 个月更新的条目。手动“同步最近”的范围在它的弹窗里单独设置。</div>
</div>
<div class='setting-card'>
<div class='setting-title'>调度频率</div>
<div class='segmented' id='cronFreqRow' style='flex-wrap:wrap;width:max-content;max-width:100%;margin-top:6px'>
<button data-freq='minutes'>每隔分钟</button>
<button data-freq='hours'>每隔小时</button>
<button data-freq='daily'>每天</button>
<button data-freq='weekly'>每周</button>
<button data-freq='custom'>自定义</button>
</div>
<div id='cronFreqMinutes' style='margin-top:10px;display:none;align-items:center;gap:10px'>
<span>每隔</span><input id='cronEveryMinutes' type='number' min='1' max='59' step='1' value='30' class='btn' style='width:84px' /><span>分钟</span>
</div>
<div id='cronFreqHours' style='margin-top:10px;display:none;align-items:center;gap:10px;flex-wrap:wrap'>
<span>每隔</span><input id='cronEveryHours' type='number' min='1' max='23' step='1' value='4' class='btn' style='width:84px' /><span>小时,第</span><input id='cronHourMinute' type='number' min='0' max='59' step='1' value='0' class='btn' style='width:84px' /><span></span>
</div>
<div id='cronFreqDaily' style='margin-top:10px;display:none;align-items:center;gap:10px'>
<span>每天</span><input id='cronDailyTime' type='time' value='02:00' class='btn' style='padding:8px 12px;border-radius:14px;width:auto;cursor:pointer' />
</div>
<div id='cronFreqWeekly' style='margin-top:10px;display:none'>
<div class='segmented' id='cronWeeklyDaysRow'>
<button data-day='1'>周一</button>
<button data-day='2'>周二</button>
<button data-day='3'>周三</button>
<button data-day='4'>周四</button>
<button data-day='5'>周五</button>
<button data-day='6'>周六</button>
<button data-day='0'>周日</button>
</div>
<div style='margin-top:8px;display:flex;align-items:center;gap:6px'><span>时间</span><input id='cronWeeklyTime' type='time' value='08:00' class='btn' style='padding:8px 12px;border-radius:14px;width:auto;cursor:pointer' /></div>
</div>
<div id='cronFreqCustom' style='margin-top:10px;display:none'>
<input id='cronCustomInput' type='text' placeholder='分 时 日 月 周,例如 0 */4 * * *' class='btn' style='width:100%;max-width:320px;font-family:monospace' />
<div class='setting-help' style='margin-top:4px'>5 字段标准 cron:分 时 日 月 周(周 0–6,0=周日)。</div>
</div>
<div class='cron-summary'>
<div class='setting-help' id='autoSyncHelp' style='margin-top:0;color:var(--soft)'></div>
<div class='setting-help' id='cronNextText' style='margin-top:3px'></div>
<div class='setting-help' style='margin-top:3px;opacity:.7'>cron <code id='cronExprText' style='font-family:monospace'></code></div>
</div>
</div>
<label class='setting-card sync-cover' style='cursor:pointer'>
<div>
<div class='setting-title' style='margin-bottom:2px'>同步后缓存封面</div>
<div class='setting-help' style='margin-top:0'>自动同步时把封面下载到本地文件卷。</div>
</div>
<input type='checkbox' id='autoCacheCoversInput' class='switch-input' />
<span class='switch'></span>
</label>
</div>
</div>
<div class='settings-pane' id='stab-appearance'>
<div class='settings-grid split'>
<div class='setting-card'><div class='setting-title'>主题模式</div><div class='segmented' data-segment='themeMode'><button data-value='system'>跟随系统</button><button data-value='dark'>深色</button><button data-value='light'>浅色</button></div><div class='setting-help'>点击切换时会用扩散动画过渡;跟随系统会随系统明暗变化。</div></div>
<div class='setting-card'><div class='setting-title'>字体风格</div><div class='segmented' data-segment='fontFamily'><button data-value='system'>系统默认</button><button data-value='rounded'>圆润</button><button data-value='jp'>日系</button><button data-value='wenkai'>文楷</button><button data-value='mono'>等宽</button></div><div class='setting-help'>使用本机已安装字体;没有对应字体时自动回退。</div></div>
<div class='setting-card'><div class='setting-title'>海报卡片</div><div class='segmented' data-segment='cardSize'><button data-value='compact'>紧凑</button><button data-value='standard'>标准</button><button data-value='large'>大卡</button></div><div class='setting-help'>卡片文字区固定高度,悬停时弹起展开,不再把整行撑乱。</div></div>
<div class='setting-card'><div class='setting-title'>动效强度</div><div class='segmented' data-segment='motion'><button data-value='full'>完整</button><button data-value='soft'>轻量</button><button data-value='off'>关闭</button></div><div class='setting-help'>页面卡顿时可改成轻量或关闭;不会影响备份数据。</div></div>
<div class='setting-card'><div class='setting-title'>主题配色</div><div class='segmented accent-buttons' data-segment='accentColor'><button data-value='pink'>樱粉</button><button data-value='wisteria'>紫藤</button><button data-value='cyan'>青蓝</button><button data-value='amber'>暖黄</button><button data-value='mint'>薄荷</button></div><div class='setting-help'>影响背景、边框、选中态、按钮与动效光晕的强调色。</div></div>
</div>
<div class='number-grid' style='margin-top:14px'>
<div class='field'><label>每次显示条数</label><input id='pageSizeInput' type='number' min='40' max='360' step='20' placeholder='例如 120' /><div class='help'>越小越顺滑;越大越少点”再显示”。</div></div>
<div class='field'><label>卡片吐槽预览行数</label><input id='commentLinesInput' type='number' min='0' max='6' step='1' placeholder='例如 3' /><div class='help'>设为 0 可基本隐藏预览,只在详情里看完整吐槽。</div></div>
<div class='field'><label>UI 白边粗细(px</label><input id='borderWidthInput' type='number' min='0' max='4' step='0.5' placeholder='0 到 4' /><div class='help'>控制卡片、弹窗、按钮等玻璃边框;设为 0 可关闭大部分白边。</div></div>
</div>
</div>
<div class='settings-pane' id='stab-advanced'>
<div class='advanced-grid'>
<div class='setting-card'>
<div class='setting-title'>修改管理员密码</div>
<div class='split'>
<div class='field'><label>当前密码</label><input id='currentAdminPasswordInput' type='password' autocomplete='current-password' /></div>
<div class='field'><label>新密码</label><input id='newAdminPasswordInput' type='password' autocomplete='new-password' minlength='10' /></div>
</div>
<button class='btn warn' id='changePasswordBtn' type='button' style='margin-top:12px'>修改密码</button>
</div>
<div class='setting-card'>
<div class='setting-title'>公开访问</div>
<label class='pill'>公开保管库(访客无需密码即可浏览)<input type='checkbox' id='publicModeInput' class='switch-input' /><span class='switch'></span></label>
<label class='pill'>允许访客查看 NSFW 条目<input type='checkbox' id='publicNsfwInput' class='switch-input' /><span class='switch'></span></label>
</div>
<details class='setting-card net-collapse'>
<summary class='setting-title'>网络与镜像</summary>
<div class='field'><label>自定义 API 地址</label><input id='bgmApiBaseInput' type='text' placeholder='https://api.bgm.tv' /><div class='help'>留空用默认 api.bgm.tv。<b>自定义会把 Access Token 发往该地址,只填可信镜像。</b></div></div>
<div class='field'><label>图片反代 / 镜像</label><input id='imageProxyInput' type='text' placeholder='留空不代理;可含 {url} 占位' /><div class='help'>未缓存的在线封面走此代理;含 <code>{url}</code> 时替换原图地址,否则前缀拼接。</div></div>
<div class='field'><label>Bangumi 站点地址</label><input id='siteBaseInput' type='text' placeholder='https://bgm.tv' /><div class='help'>打开条目/标签页用;留空用 bgm.tv。例:https://bgmmi.anibt.net</div></div>
</details>
</div>
</div>
<div class='settings-pane' id='stab-login'>
<div class='field'><label>管理员密码</label><input id='publicLoginPasswordInput' type='password' autocomplete='current-password' /></div>
<button class='btn primary' id='publicLoginSubmitBtn' type='button' style='width:100%'>登录</button>
<div class='auth-status' id='publicLoginStatus' style='margin-top:10px'></div>
</div>
<div class='settings-footer row'>
<button class='btn primary' id='saveSettingsBtn'>保存设置</button>
<button class='btn bad' id='logoutBtn' type='button'>退出登录</button>
</div>
</div>
</div>
<div class="modal-mask" id="syncConfirmModal">
<div class="modal-card">
<div class="row"><h2 style="margin:0" id="syncConfirmTitle">同步最近</h2><div class="grow"></div><button class="btn small" data-close="syncConfirmModal">取消</button></div>
<p class="help" id="syncConfirmHelp"></p>
<div class="field" id="syncConfirmMonthsRow" style="max-width:280px;margin-top:4px">
<label>最近同步范围(月)</label>
<input id="syncConfirmMonthsInput" type="number" min="1" max="120" step="1" placeholder="例如 3" />
<div class="help">只读取最近 N 个月更新过的收藏;确认后该范围会保存为默认值,自动增量同步也会沿用。</div>
</div>
<label class="pill" style="cursor:pointer;margin-top:12px;display:flex;align-items:center;justify-content:space-between;gap:10px">刷新缓存封面到本地<input type="checkbox" id="syncConfirmCoversInput" class="switch-input" /><span class="switch"></span></label>
<div id="syncConfirmSubjectsRow">
<label class="pill" style="cursor:pointer;margin-top:8px;display:flex;align-items:center;justify-content:space-between;gap:10px">刷新条目数据(NSFW / 平台 / 公共标签等)<input type="checkbox" id="syncConfirmSubjectsInput" class="switch-input" /><span class="switch"></span></label>
<div class="help" style="margin-top:8px">关闭“刷新条目数据”可大幅加快同步;已备份条目的元数据会保留,新条目稍后再刷新即可补全。</div>
</div>
<div class="row" style="margin-top:16px"><button class="btn primary" id="syncConfirmStartBtn">开始同步</button></div>
</div>
</div>
<div class="modal-mask" id="syncModal">
<div class="modal-card">
<div class="row"><h2 style="margin:0" id="syncTitle">同步中</h2><div class="grow"></div><button class="btn small" id="cancelSyncBtn">停止显示</button></div>
<p class="help" id="syncHelp">日常同步只读取最近几个月的更新并增量写入;完整同步才做全量核对和安全箱判断。</p>
<div class="progress"><div class="bar" id="syncBar"></div></div>
<div class="sync-log" id="syncLog" style="margin-top:14px"></div>
</div>
</div>
<div class="modal-mask" id="exportModal">
<div class="modal-card">
<div class="row"><h2 style="margin:0">导出与导入</h2><div class="grow"></div><button class="btn small" data-close="exportModal">关闭</button></div>
<p class="help">完整 ZIP 包含 JSON、CSV、离线 HTML、Word、XLSX 表格、历史版本、同步记录与 images 封面目录。为避免大量 Base64 导致浏览器卡死,完整 ZIP 使用真实图片文件;请先解压 ZIP,再打开 offline-poster-wall.html。</p>
<div class="row">
<button class="btn primary" id="exportZipBtn">导出完整 ZIP(快速)</button>
<button class="btn" id="exportJsonBtn">导出 JSON</button>
<button class="btn" id="exportCsvBtn">导出 CSV</button>
<button class="btn" id="exportHtmlBtn">导出单文件 HTML(慢)</button>
<button class="btn" id="exportDocxBtn">导出文档 .docx</button>
<button class="btn" id="exportXlsxBtn">导出表格 .xlsx</button>
</div>
<div class="notice" style="margin-top:14px">封面离线取决于是否已缓存。完整 ZIP 会把服务端文件卷里的封面作为 images/ 文件夹打包,速度更快、不会塞满 HTML;请解压后打开 offline-poster-wall.html。单文件 HTML 会把封面内嵌成 Base64,条目很多时会很慢,只建议少量条目使用。</div>
<input type="file" id="importFile" accept="application/json,.json" class="hidden" />
</div>
</div>
<div class="toast" id="toast"></div>
<script src="js/core.js"></script>
<script src="js/model.js"></script>
<script src="js/view.js"></script>
<script src="js/cron.js"></script>
<script src="js/sync.js"></script>
<script src="js/covers.js"></script>
<script src="js/exports.js"></script>
<script src="js/main.js"></script>
</body>
</html>