jianzhihuixiang/alacarte-novel-website/generate-chapters.js
2026-03-29 13:57:51 +08:00

119 lines
4.3 KiB
JavaScript
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.

const fs = require('fs');
const path = require('path');
// 读取模板
const templatePath = path.join(__dirname, 'template.html');
const template = fs.readFileSync(templatePath, 'utf-8');
// 读取章节索引
const indexPath = path.join(__dirname, 'data/chapters-index.json');
const indexData = JSON.parse(fs.readFileSync(indexPath, 'utf-8'));
const chaptersIndex = indexData.chapters;
// 生成侧边栏章节列表
function generateSidebarChapters(currentId, chapters) {
const start = Math.max(1, currentId - 30);
const end = Math.min(chapters.length, currentId + 30);
let html = '';
for (let i = start; i <= end; i++) {
const chapter = chapters.find(c => c.id === i);
if (chapter) {
const isCurrent = i === currentId ? 'current' : '';
html += `<a href="chapter-${i}.html" class="sidebar-chapter ${isCurrent}">第${i}章:${chapter.title}</a>\n`;
}
}
return html;
}
// 处理章节内容转换JSON格式为HTML段落
function processContent(content) {
// 分割成段落
const paragraphs = content.split('\n').filter(p => p.trim());
let html = '';
for (const para of paragraphs) {
const trimmed = para.trim();
if (trimmed.startsWith('---')) {
// 分隔线转为特殊标记
html += `<p style="text-align: center; color: var(--text-secondary); text-indent: 0;">···</p>\n`;
} else if (trimmed.startsWith('') && trimmed.endsWith('')) {
// 结尾标记
html += `<p style="text-align: center; color: var(--text-secondary); text-indent: 0; margin-top: 2em;">${trimmed}</p>\n`;
} else {
html += `<p>${trimmed}</p>\n`;
}
}
return html;
}
// 生成HTML页面
function generateChapterHtml(chapter, chapters) {
const prevChapter = chapter.id > 1 ? `chapter-${chapter.id - 1}.html` : '#';
const nextChapter = chapter.id < chapters.length ? `chapter-${chapter.id + 1}.html` : '#';
const prevDisabled = chapter.id <= 1 ? 'disabled' : '';
const nextDisabled = chapter.id >= chapters.length ? 'disabled' : '';
let html = template;
html = html.replace('{{CHAPTER_ID}}', chapter.id);
html = html.replace('{{CHAPTER_NUMBER}}', `Chapter ${chapter.id}`);
html = html.replace('{{CHAPTER_TITLE}}', chapter.title);
html = html.replace('{{CHAPTER_CONTENT}}', processContent(chapter.content));
html = html.replace('{{PREV_CHAPTER}}', prevChapter);
html = html.replace('{{NEXT_CHAPTER}}', nextChapter);
html = html.replace('{{PREV_DISABLED}}', prevDisabled);
html = html.replace('{{NEXT_DISABLED}}', nextDisabled);
html = html.replace('{{SIDEBAR_CHAPTERS}}', generateSidebarChapters(chapter.id, chapters));
return html;
}
// 主函数
function main() {
const chaptersDir = path.join(__dirname, 'chapters');
const dataDir = path.join(__dirname, 'data');
// 确保目录存在
if (!fs.existsSync(chaptersDir)) {
fs.mkdirSync(chaptersDir, { recursive: true });
}
let generated = 0;
let skipped = 0;
for (const chapterMeta of chaptersIndex) {
const outputPath = path.join(chaptersDir, `chapter-${chapterMeta.id}.html`);
const jsonPath = path.join(dataDir, `chapter-${chapterMeta.id}.json`);
// 检查JSON文件是否存在
if (!fs.existsSync(jsonPath)) {
console.log(`⏭️ 跳过: chapter-${chapterMeta.id}.html (JSON不存在)`);
skipped++;
continue;
}
try {
// 读取完整章节内容
const jsonData = JSON.parse(fs.readFileSync(jsonPath, 'utf-8'));
const chapter = {
id: chapterMeta.id,
title: jsonData.title || chapterMeta.title,
content: jsonData.content || ''
};
const html = generateChapterHtml(chapter, chaptersIndex);
fs.writeFileSync(outputPath, html, 'utf-8');
generated++;
console.log(`✅ 生成: chapter-${chapter.id}.html (${chapter.title})`);
} catch (err) {
console.error(`❌ 错误: chapter-${chapterMeta.id}.html - ${err.message}`);
skipped++;
}
}
console.log(`\n📊 统计: 生成 ${generated} 个文件, 跳过 ${skipped}`);
}
main();