记录清理机制

This commit is contained in:
mtvpls
2026-04-18 10:01:32 +08:00
parent 6360b0fa18
commit 3520fd1ed3
5 changed files with 26 additions and 6 deletions
+1
View File
@@ -437,6 +437,7 @@ dockge/komodo 等 docker compose UI 也有自动更新功能
| VIDEOINFO_CACHE_MINUTES | 私人影库视频信息在内存中的缓存时长(分钟) | 正整数 | 14401天) |
| NEXT_PUBLIC_ENABLE_SOURCE_SEARCH | 是否开启源站寻片功能 | true/false | true |
| MAX_PLAY_RECORDS_PER_USER | 单个用户播放记录清理阈值(超过此数量将自动清理旧记录) | 正整数 | 100 |
| MAX_MANGA_HISTORY_PER_USER | 单个用户漫画阅读历史保留上限 | 正整数 | 100 |
| INIT_CONFIG | 初始配置(JSON 格式,包含 api_site、custom_category、lives 等) | JSON 字符串 | (空) |
| CONFIG_SUBSCRIPTION_URL | 配置订阅 URL(Base58 编码的配置文件地址,优先级高于 INIT_CONFIG | URL | (空) |
| TMDB_API_KEY | TMDB API 密钥 | 任意字符串 | (空) |
+2 -1
View File
@@ -2068,13 +2068,14 @@ export class D1Storage implements IStorage {
async cleanupOldMangaReadRecords(userName: string): Promise<void> {
try {
const maxRecords = parseInt(process.env.MAX_MANGA_HISTORY_PER_USER || '100', 10);
const threshold = maxRecords + 10;
const countResult = await this.db
.prepare('SELECT COUNT(*) as count FROM manga_read_records WHERE username = ?')
.bind(userName)
.first();
const count = Number(countResult?.count || 0);
if (count <= maxRecords) return;
if (count <= threshold) return;
await this.db
.prepare(`
+19 -4
View File
@@ -95,6 +95,8 @@ const PLAY_RECORDS_KEY = 'moontv_play_records';
const FAVORITES_KEY = 'moontv_favorites';
const MANGA_SHELF_KEY = 'moontv_manga_shelf';
const MANGA_HISTORY_KEY = 'moontv_manga_history';
const DEFAULT_MAX_MANGA_HISTORY_RECORDS = 100;
const DEFAULT_MAX_MANGA_HISTORY_THRESHOLD = DEFAULT_MAX_MANGA_HISTORY_RECORDS + 10;
const SEARCH_HISTORY_KEY = 'moontv_search_history';
const MUSIC_PLAY_RECORDS_KEY = 'moontv_music_play_records';
@@ -1701,6 +1703,17 @@ export async function clearAllMangaShelf(): Promise<void> {
window.dispatchEvent(new CustomEvent('mangaShelfUpdated', { detail: {} }));
}
function trimMangaReadRecords(records: Record<string, MangaReadRecord>): Record<string, MangaReadRecord> {
const entries = Object.entries(records);
if (entries.length <= DEFAULT_MAX_MANGA_HISTORY_THRESHOLD) return records;
return Object.fromEntries(
entries
.sort(([, a], [, b]) => b.saveTime - a.saveTime)
.slice(0, DEFAULT_MAX_MANGA_HISTORY_RECORDS)
);
}
export async function getAllMangaReadRecords(): Promise<Record<string, MangaReadRecord>> {
if (typeof window === 'undefined') return {};
@@ -1748,8 +1761,9 @@ export async function saveMangaReadRecord(sourceId: string, mangaId: string, rec
if (STORAGE_TYPE !== 'localstorage') {
const cached = cacheManager.getCachedMangaReadRecords() || {};
cached[key] = record;
cacheManager.cacheMangaReadRecords(cached);
window.dispatchEvent(new CustomEvent('mangaHistoryUpdated', { detail: cached }));
const trimmedRecords = trimMangaReadRecords(cached);
cacheManager.cacheMangaReadRecords(trimmedRecords);
window.dispatchEvent(new CustomEvent('mangaHistoryUpdated', { detail: trimmedRecords }));
try {
await fetchWithAuth('/api/manga/history', {
@@ -1766,8 +1780,9 @@ export async function saveMangaReadRecord(sourceId: string, mangaId: string, rec
const allRecords = await getAllMangaReadRecords();
allRecords[key] = record;
localStorage.setItem(MANGA_HISTORY_KEY, JSON.stringify(allRecords));
window.dispatchEvent(new CustomEvent('mangaHistoryUpdated', { detail: allRecords }));
const trimmedRecords = trimMangaReadRecords(allRecords);
localStorage.setItem(MANGA_HISTORY_KEY, JSON.stringify(trimmedRecords));
window.dispatchEvent(new CustomEvent('mangaHistoryUpdated', { detail: trimmedRecords }));
}
export async function deleteMangaReadRecord(sourceId: string, mangaId: string): Promise<void> {
+2 -1
View File
@@ -2037,13 +2037,14 @@ export class PostgresStorage implements IStorage {
async cleanupOldMangaReadRecords(userName: string): Promise<void> {
try {
const maxRecords = parseInt(process.env.MAX_MANGA_HISTORY_PER_USER || '100', 10);
const threshold = maxRecords + 10;
const countResult = await this.db
.prepare('SELECT COUNT(*) as count FROM manga_read_records WHERE username = $1')
.bind(userName)
.first();
const count = Number(countResult?.count || 0);
if (count <= maxRecords) return;
if (count <= threshold) return;
await this.db
.prepare(`
+2
View File
@@ -1557,6 +1557,8 @@ export abstract class BaseRedisStorage implements IStorage {
async cleanupOldMangaReadRecords(userName: string): Promise<void> {
const records = await this.getAllMangaReadRecords(userName);
const maxRecords = parseInt(process.env.MAX_MANGA_HISTORY_PER_USER || '100', 10);
const threshold = maxRecords + 10;
if (Object.keys(records).length <= threshold) return;
const keys = Object.entries(records)
.sort(([, a], [, b]) => b.saveTime - a.saveTime)
.slice(maxRecords)