jianzhihuixiang/alacarte-novel-website/generate-chapters.js

119 lines
4.3 KiB
JavaScript
Raw Normal View History

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();