Allow to insert original chapters by prev/next chapter id

This commit is contained in:
2026-03-02 15:15:26 +08:00
parent 08c0fa0c3f
commit f0cbbe6d02
6 changed files with 159 additions and 42 deletions

View File

@@ -30,7 +30,9 @@
"builddbg": "node build.js --dev --debug",
"buildrel": "node build.js",
"lint": "eslint src",
"test": "node build_test.js && node --enable-source-maps test.cjs"
"build_test": "node build_test.js",
"test": "node build_test.js && node --enable-source-maps test.cjs",
"run_test": "node --enable-source-maps test.cjs"
},
"type": "module",
"types": "./data.d.ts",

View File

@@ -345,6 +345,8 @@ export class IndexedDb implements Db {
name: data.chapterInfo.chapterName,
bookId: data.bookId,
time: data.time,
prev: data.chapterInfo.prev,
next: data.chapterInfo.next,
};
} else if (!oldValue) {
currents.set(data.id, value);
@@ -354,6 +356,8 @@ export class IndexedDb implements Db {
name: data.chapterInfo.chapterName,
bookId: data.bookId,
time: data.time,
prev: data.chapterInfo.prev,
next: data.chapterInfo.next,
});
}
}, bookId, 'bookId');

View File

@@ -244,7 +244,7 @@ export class PocketBaseDb implements Db {
const currents: Map<number, [number, number]> = new Map();
const list = await this.client.collection(`${this.cfg.prefix}qd_chapters`).getFullList({
filter: `bookId = ${bookId}`,
fields: 'id,chapterId,bookId,time,data.chapterInfo.chapterName',
fields: 'id,chapterId,bookId,time,data.chapterInfo.chapterName,data.chapterInfo.prev,data.chapterInfo.next',
});
const re: QdChapterSimpleInfo[] = [];
for (const item of list) {
@@ -261,6 +261,8 @@ export class PocketBaseDb implements Db {
name: data.chapterInfo.chapterName,
bookId: item.bookId,
time: item.time,
prev: data.chapterInfo.prev,
next: data.chapterInfo.next,
};
} else if (!oldValue) {
currents.set(key, value);
@@ -270,6 +272,8 @@ export class PocketBaseDb implements Db {
name: data.chapterInfo.chapterName,
bookId: item.bookId,
time: item.time,
prev: data.chapterInfo.prev,
next: data.chapterInfo.next,
});
}
}

View File

@@ -28,6 +28,10 @@ export type QdChapterSimpleInfo = {
name: string;
bookId: number;
time: number;
/// Previous chapter ID
prev?: number;
/// Next chapter ID
next?: number;
}
export type QdChapterHistoryInfo = {

View File

@@ -83,4 +83,90 @@ export default function t() {
},
]);
});
test(filename, 'merge_volumes_2', () => {
const sourceList: Volume[] = [
{
id: 'test',
name: 'Test Volume',
isVip: false,
chapters: [
{ id: 3, name: 'Chapter 3' },
]
}
];
const targetList: QdChapterSimpleInfo[] = [
{
primaryKey: 0,
id: 1,
name: 'Chapter 1',
bookId: 0,
time: 10,
next: 3,
},
{
primaryKey: 0,
id: 4,
name: 'Chapter 4',
bookId: 0,
time: 11,
prev: 3,
},
{
primaryKey: 0,
id: 33,
name: 'Other',
bookId: 0,
time: 12,
},
];
expect(get_new_volumes(targetList, sourceList, ChapterShowMode.All)).toEqual([
{
id: 'vol_new',
name: '其他已保存章节',
isVip: false,
chapters: [
{ id: 33, name: 'Other', isSaved: true },
],
},
{
id: 'test',
name: 'Test Volume',
isVip: false,
chapters: [
{ id: 1, name: 'Chapter 1', isSaved: true },
{ id: 3, name: 'Chapter 3' },
{ id: 4, name: 'Chapter 4', isSaved: true },
]
}
]);
expect(get_new_volumes(targetList, sourceList, ChapterShowMode.SavedOnly)).toEqual([
{
id: 'vol_new',
name: '其他已保存章节',
isVip: false,
chapters: [
{ id: 33, name: 'Other' },
],
},
{
id: 'test',
name: 'Test Volume',
isVip: false,
chapters: [
{ id: 1, name: 'Chapter 1' },
{ id: 4, name: 'Chapter 4' },
],
},
]);
expect(get_new_volumes(targetList, sourceList, ChapterShowMode.UnsavedOnly)).toEqual([
{
id: 'test',
name: 'Test Volume',
isVip: false,
chapters: [
{ id: 3, name: 'Chapter 3' },
],
},
]);
});
}

View File

@@ -38,28 +38,72 @@ export function hash_qdchapter_info(info: QdChapterInfo): string {
export function get_new_volumes(chapterLists: QdChapterSimpleInfo[], volumes: Volume[], keepMode: ChapterShowMode): Volume[] {
const vols: Volume[] = [];
if (keepMode == ChapterShowMode.All) {
const volMap: Map<number, Chapter> = new Map();
if (keepMode == ChapterShowMode.All || keepMode == ChapterShowMode.SavedOnly) {
const chMap: Map<number, Chapter> = new Map();
const volMap: Map<number, Volume> = new Map();
for (const vo of volumes) {
const vol = structuredClone(vo);
for (const ch of vol.chapters) {
volMap.set(ch.id, ch);
chMap.set(ch.id, ch);
volMap.set(ch.id, vol);
}
vols.push(vol);
}
const volCh: Chapter[] = [];
let needed: QdChapterSimpleInfo[] = [];
for (const ch of chapterLists) {
const chInfo = volMap.get(ch.id);
const chInfo = chMap.get(ch.id);
if (!chInfo) {
volCh.push({
id: ch.id,
name: ch.name,
isSaved: true,
});
needed.push(ch);
} else {
chInfo.isSaved = true;
}
}
let changed = false;
do {
const current_len = needed.length;
const not_found: QdChapterSimpleInfo[] = [];
for (const ch of needed) {
if (ch.prev) {
const prevVol = volMap.get(ch.prev);
if (prevVol) {
const chIndex = prevVol.chapters.findIndex(c => c.id === ch.prev);
if (chIndex !== -1) {
prevVol.chapters.splice(chIndex + 1, 0, {
id: ch.id,
name: ch.name,
isSaved: true,
});
continue;
}
}
}
if (ch.next) {
const nextVol = volMap.get(ch.next);
if (nextVol) {
const chIndex = nextVol.chapters.findIndex(c => c.id === ch.next);
if (chIndex !== -1) {
nextVol.chapters.splice(chIndex, 0, {
id: ch.id,
name: ch.name,
isSaved: true,
});
continue;
}
}
}
not_found.push(ch);
}
needed = not_found;
changed = current_len !== needed.length;
} while (changed && needed.length > 0);
for (const ch of needed) {
volCh.push({
id: ch.id,
name: ch.name,
isSaved: true,
});
}
if (volCh.length > 0) {
vols.unshift({
id: 'vol_new',
@@ -68,39 +112,12 @@ export function get_new_volumes(chapterLists: QdChapterSimpleInfo[], volumes: Vo
isVip: false,
});
}
} else if (keepMode == ChapterShowMode.SavedOnly) {
const chIds = new Set(chapterLists.map(ch => ch.id));
for (const vol of volumes) {
const newChs = vol.chapters.filter(ch => chIds.has(ch.id));
if (newChs.length > 0) {
vols.push({
...vol,
chapters: newChs,
});
if (keepMode == ChapterShowMode.SavedOnly) {
for (const vol of vols) {
vol.chapters = vol.chapters.filter(ch => ch.isSaved);
vol.chapters.forEach(ch => delete ch.isSaved);
}
}
for (const vol of vols) {
for (const ch of vol.chapters) {
chIds.delete(ch.id);
}
}
if (chIds.size > 0) {
const volCh: Chapter[] = [];
for (const ch of chapterLists) {
if (chIds.has(ch.id)) {
volCh.push({
id: ch.id,
name: ch.name,
});
}
}
vols.unshift({
id: 'vol_new',
name: '其他已保存章节',
chapters: volCh,
isVip: false,
});
}
} else if (keepMode == ChapterShowMode.UnsavedOnly) {
const chIds = new Set(chapterLists.map(ch => ch.id));
for (const vol of volumes) {