增加漫画展馆功能
This commit is contained in:
@@ -18,6 +18,7 @@ import {
|
||||
MovieRequest,
|
||||
} from './types';
|
||||
import { AdminConfig } from './admin.types';
|
||||
import { MangaReadRecord, MangaShelfItem } from './manga.types';
|
||||
import { DatabaseAdapter } from './d1-adapter';
|
||||
import { MusicV2HistoryRecord, MusicV2PlaylistItem, MusicV2PlaylistRecord } from './music-v2';
|
||||
|
||||
@@ -1747,6 +1748,268 @@ export class PostgresStorage implements IStorage {
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 漫画书架 ====================
|
||||
|
||||
async getMangaShelf(userName: string, key: string): Promise<MangaShelfItem | null> {
|
||||
try {
|
||||
const result = await this.db
|
||||
.prepare('SELECT * FROM manga_shelf WHERE username = $1 AND key = $2')
|
||||
.bind(userName, key)
|
||||
.first();
|
||||
|
||||
if (!result) return null;
|
||||
return {
|
||||
title: result.title as string,
|
||||
cover: (result.cover as string) || '',
|
||||
sourceId: result.source_id as string,
|
||||
sourceName: result.source_name as string,
|
||||
mangaId: result.manga_id as string,
|
||||
saveTime: Number(result.save_time || 0),
|
||||
description: (result.description as string) || undefined,
|
||||
author: (result.author as string) || undefined,
|
||||
status: (result.status as string) || undefined,
|
||||
lastChapterId: (result.last_chapter_id as string) || undefined,
|
||||
lastChapterName: (result.last_chapter_name as string) || undefined,
|
||||
};
|
||||
} catch (err) {
|
||||
console.error('PostgresStorage.getMangaShelf error:', err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
async setMangaShelf(userName: string, key: string, item: MangaShelfItem): Promise<void> {
|
||||
try {
|
||||
await this.db
|
||||
.prepare(`
|
||||
INSERT INTO manga_shelf (
|
||||
username, key, source_id, source_name, manga_id, title, cover, save_time,
|
||||
description, author, status, last_chapter_id, last_chapter_name
|
||||
)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)
|
||||
ON CONFLICT (username, key) DO UPDATE SET
|
||||
source_id = EXCLUDED.source_id,
|
||||
source_name = EXCLUDED.source_name,
|
||||
manga_id = EXCLUDED.manga_id,
|
||||
title = EXCLUDED.title,
|
||||
cover = EXCLUDED.cover,
|
||||
save_time = EXCLUDED.save_time,
|
||||
description = EXCLUDED.description,
|
||||
author = EXCLUDED.author,
|
||||
status = EXCLUDED.status,
|
||||
last_chapter_id = EXCLUDED.last_chapter_id,
|
||||
last_chapter_name = EXCLUDED.last_chapter_name
|
||||
`)
|
||||
.bind(
|
||||
userName,
|
||||
key,
|
||||
item.sourceId,
|
||||
item.sourceName,
|
||||
item.mangaId,
|
||||
item.title,
|
||||
item.cover || '',
|
||||
item.saveTime,
|
||||
item.description || null,
|
||||
item.author || null,
|
||||
item.status || null,
|
||||
item.lastChapterId || null,
|
||||
item.lastChapterName || null
|
||||
)
|
||||
.run();
|
||||
} catch (err) {
|
||||
console.error('PostgresStorage.setMangaShelf error:', err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
async getAllMangaShelf(userName: string): Promise<{ [key: string]: MangaShelfItem }> {
|
||||
try {
|
||||
const results = await this.db
|
||||
.prepare('SELECT * FROM manga_shelf WHERE username = $1 ORDER BY save_time DESC')
|
||||
.bind(userName)
|
||||
.all();
|
||||
|
||||
const shelves: { [key: string]: MangaShelfItem } = {};
|
||||
if (!results.results) return shelves;
|
||||
|
||||
for (const row of results.results) {
|
||||
shelves[row.key as string] = {
|
||||
title: row.title as string,
|
||||
cover: (row.cover as string) || '',
|
||||
sourceId: row.source_id as string,
|
||||
sourceName: row.source_name as string,
|
||||
mangaId: row.manga_id as string,
|
||||
saveTime: Number(row.save_time || 0),
|
||||
description: (row.description as string) || undefined,
|
||||
author: (row.author as string) || undefined,
|
||||
status: (row.status as string) || undefined,
|
||||
lastChapterId: (row.last_chapter_id as string) || undefined,
|
||||
lastChapterName: (row.last_chapter_name as string) || undefined,
|
||||
};
|
||||
}
|
||||
|
||||
return shelves;
|
||||
} catch (err) {
|
||||
console.error('PostgresStorage.getAllMangaShelf error:', err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
async deleteMangaShelf(userName: string, key: string): Promise<void> {
|
||||
try {
|
||||
await this.db
|
||||
.prepare('DELETE FROM manga_shelf WHERE username = $1 AND key = $2')
|
||||
.bind(userName, key)
|
||||
.run();
|
||||
} catch (err) {
|
||||
console.error('PostgresStorage.deleteMangaShelf error:', err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 漫画阅读历史 ====================
|
||||
|
||||
async getMangaReadRecord(userName: string, key: string): Promise<MangaReadRecord | null> {
|
||||
try {
|
||||
const result = await this.db
|
||||
.prepare('SELECT * FROM manga_read_records WHERE username = $1 AND key = $2')
|
||||
.bind(userName, key)
|
||||
.first();
|
||||
|
||||
if (!result) return null;
|
||||
return {
|
||||
title: result.title as string,
|
||||
cover: (result.cover as string) || '',
|
||||
sourceId: result.source_id as string,
|
||||
sourceName: result.source_name as string,
|
||||
mangaId: result.manga_id as string,
|
||||
chapterId: result.chapter_id as string,
|
||||
chapterName: result.chapter_name as string,
|
||||
pageIndex: Number(result.page_index || 0),
|
||||
pageCount: Number(result.page_count || 0),
|
||||
saveTime: Number(result.save_time || 0),
|
||||
};
|
||||
} catch (err) {
|
||||
console.error('PostgresStorage.getMangaReadRecord error:', err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
async setMangaReadRecord(userName: string, key: string, record: MangaReadRecord): Promise<void> {
|
||||
try {
|
||||
await this.db
|
||||
.prepare(`
|
||||
INSERT INTO manga_read_records (
|
||||
username, key, source_id, source_name, manga_id, title, cover,
|
||||
chapter_id, chapter_name, page_index, page_count, save_time
|
||||
)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
|
||||
ON CONFLICT (username, key) DO UPDATE SET
|
||||
source_id = EXCLUDED.source_id,
|
||||
source_name = EXCLUDED.source_name,
|
||||
manga_id = EXCLUDED.manga_id,
|
||||
title = EXCLUDED.title,
|
||||
cover = EXCLUDED.cover,
|
||||
chapter_id = EXCLUDED.chapter_id,
|
||||
chapter_name = EXCLUDED.chapter_name,
|
||||
page_index = EXCLUDED.page_index,
|
||||
page_count = EXCLUDED.page_count,
|
||||
save_time = EXCLUDED.save_time
|
||||
`)
|
||||
.bind(
|
||||
userName,
|
||||
key,
|
||||
record.sourceId,
|
||||
record.sourceName,
|
||||
record.mangaId,
|
||||
record.title,
|
||||
record.cover || '',
|
||||
record.chapterId,
|
||||
record.chapterName,
|
||||
record.pageIndex,
|
||||
record.pageCount,
|
||||
record.saveTime
|
||||
)
|
||||
.run();
|
||||
} catch (err) {
|
||||
console.error('PostgresStorage.setMangaReadRecord error:', err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
async getAllMangaReadRecords(userName: string): Promise<{ [key: string]: MangaReadRecord }> {
|
||||
try {
|
||||
const results = await this.db
|
||||
.prepare('SELECT * FROM manga_read_records WHERE username = $1 ORDER BY save_time DESC')
|
||||
.bind(userName)
|
||||
.all();
|
||||
|
||||
const records: { [key: string]: MangaReadRecord } = {};
|
||||
if (!results.results) return records;
|
||||
|
||||
for (const row of results.results) {
|
||||
records[row.key as string] = {
|
||||
title: row.title as string,
|
||||
cover: (row.cover as string) || '',
|
||||
sourceId: row.source_id as string,
|
||||
sourceName: row.source_name as string,
|
||||
mangaId: row.manga_id as string,
|
||||
chapterId: row.chapter_id as string,
|
||||
chapterName: row.chapter_name as string,
|
||||
pageIndex: Number(row.page_index || 0),
|
||||
pageCount: Number(row.page_count || 0),
|
||||
saveTime: Number(row.save_time || 0),
|
||||
};
|
||||
}
|
||||
|
||||
return records;
|
||||
} catch (err) {
|
||||
console.error('PostgresStorage.getAllMangaReadRecords error:', err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
async deleteMangaReadRecord(userName: string, key: string): Promise<void> {
|
||||
try {
|
||||
await this.db
|
||||
.prepare('DELETE FROM manga_read_records WHERE username = $1 AND key = $2')
|
||||
.bind(userName, key)
|
||||
.run();
|
||||
} catch (err) {
|
||||
console.error('PostgresStorage.deleteMangaReadRecord error:', err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
async cleanupOldMangaReadRecords(userName: string): Promise<void> {
|
||||
try {
|
||||
const maxRecords = parseInt(process.env.MAX_MANGA_HISTORY_PER_USER || '100', 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;
|
||||
|
||||
await this.db
|
||||
.prepare(`
|
||||
DELETE FROM manga_read_records
|
||||
WHERE username = $1
|
||||
AND key NOT IN (
|
||||
SELECT key FROM manga_read_records
|
||||
WHERE username = $1
|
||||
ORDER BY save_time DESC
|
||||
LIMIT $2
|
||||
)
|
||||
`)
|
||||
.bind(userName, maxRecords)
|
||||
.run();
|
||||
} catch (err) {
|
||||
console.error('PostgresStorage.cleanupOldMangaReadRecords error:', err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 跳过配置 ====================
|
||||
|
||||
async getSkipConfig(userName: string, source: string, id: string): Promise<SkipConfig | null> {
|
||||
@@ -2205,6 +2468,8 @@ export class PostgresStorage implements IStorage {
|
||||
'play_records',
|
||||
'favorites',
|
||||
'search_history',
|
||||
'manga_shelf',
|
||||
'manga_read_records',
|
||||
'skip_configs',
|
||||
'music_play_records',
|
||||
'music_playlists',
|
||||
|
||||
Reference in New Issue
Block a user