john před 7 měsíci
rodič
revize
c35c4a9b6e

+ 16 - 0
epub_node/db/book.js

@@ -43,3 +43,19 @@ export async function book_mate_insert({
   });
 }
 
+
+
+
+export async function getBookInfo(book_md5) {
+  const sql = `select * from book where book_md5 = '${book_md5}';`;
+  return new Promise((resolve, reject) => {
+    connection.execute(sql, [], (error, result) => {
+      if (error) {
+        console.error('Database error:', error);
+        return reject(error); // 使用 reject 处理错误
+      }
+      resolve(result.length ? result[0] : false); // 返回查询结果
+    });
+  });
+}
+

+ 1 - 1
epub_node/db/chapter.js

@@ -119,7 +119,7 @@ export async function getChaptersByBookId(book_id, level = 0, parent_id = '') {
 
 
 /* 获取当前书籍下一章的file_id */
-export async function getChaptersByBookId(book_id, level = 0, parent_id = '') {
+export async function getChaptersByBookId2(book_id, level = 0, parent_id = '') {
     return new Promise((resolve, reject) => {
         /*
          SELECT files.*, chapter.*

+ 6 - 3
epub_node/db/files.js

@@ -17,15 +17,18 @@ export async function files_insert({
                                        size = "",
                                        name = "",
                                        path = "",
+                                       content = "",
+                                       old_path = "",
+                                       book_id = "",
                                    }) {
     return new Promise(async (resolve, reject) => {
         try {
             const sql = `
-                INSERT INTO files (file_id, source_id, md5, mimetype, size, name, path)
-                VALUES (?, ?, ?, ?, ?, ?, ?) ON DUPLICATE KEY
+                INSERT INTO files (file_id, source_id, md5, mimetype, size, name, path, content, old_path, book_id)
+                VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON DUPLICATE KEY
                 UPDATE file_id = file_id;
             `;
-            const values = [file_id, source_id, md5, mimetype, size, name, path];
+            const values = [file_id, source_id, md5, mimetype, size, name, path, content, old_path, book_id];
             // 直接接收 execute 返回的内容
             const result = await connection.execute(sql, values);
             return resolve(result);

+ 12 - 0
epub_node/jsconfig.json

@@ -0,0 +1,12 @@
+{
+  "compilerOptions": {
+    "baseUrl": ".",
+    "paths": {
+      "#db": ["./db/index.js"],
+      "#utils": ["./utils/index.js"],
+      "#environment": ["./environment/index.js"],
+      "#logger": ["./utils/logger.js"]
+    }
+  },
+  "include": ["./"]
+}

+ 41 - 0
epub_node/router/epub/files.js

@@ -0,0 +1,41 @@
+import logger from "#logger";
+import {chapter_insert, searchChapterInfoForPath, getChaptersByBookId, getFileBymd5, getBookInfo} from "#db";
+import cliProgress from 'cli-progress';
+import {EPub} from "epub2";
+import path from "node:path";
+import fs from "node:fs";
+
+
+export async function saveAllFiles(epub, zipEpubExtract, file_md5, author_id) {
+    // const chapterIndex = epub.flow.findIndex(elm => elm.id === fileRow.source_id);
+    const bookInfo =  await getBookInfo(file_md5)
+    // 图片、css、html
+    console.log(12, bookInfo)
+    /*files_insert({
+        file_id = "",
+        source_id = "",
+        md5 = "",
+        mimetype = "",
+        size = "",
+        name = "",
+        path = "",
+        content = "",
+        old_path
+        book_id = "",
+    })*/
+    // console.log("\nSPINE:\n");
+    // console.log(epub.flow)
+    for (let i = 0; i < epub.flow.length; i++) {
+        const elm = epub.flow[i]
+        const isImage = elm.mediaType.includes('image')
+        const isCss = elm.mediaType.includes('text/css')
+        // console.log(15, {
+        //     title: elm.title,
+        //     id: elm.id,
+        //     order: elm.order,
+        //     mediaType: elm.mediaType
+        // })
+        console.log(elm)
+
+    }
+}

+ 5 - 1
epub_node/router/epub/index.js

@@ -20,6 +20,7 @@ import {htmlParser, saveMateInfo} from "./txt.js";
 import {saveAllCSS} from "./style.js";
 import {saveAllFount} from "./font.js";
 import {saveToc, fetchAndBuildTree, fetchNextFile} from "./toc.js";
+import {saveAllFiles} from "./files.js";
 
 const router = express.Router();
 
@@ -206,6 +207,9 @@ router.put("/", async function (req, res) {
     await saveMateInfo(epub, uploadPath, file_md5, author_id);
     logger.info("书籍的基础数据处理完毕");
     console.log("书籍的基础数据处理完毕");
+    await waittime(1000);
+    await saveAllFiles(epub, uploadPath, file_md5, author_id);
+    /*
     await saveImgs(epub, uploadPath, file_md5, author_id);
     logger.info("书籍的图片数据处理完毕");
     console.log("书籍的图片数据处理完毕");
@@ -224,7 +228,7 @@ router.put("/", async function (req, res) {
     logger.info("书籍的目录处理完毕");
     logger.info("书籍处理完毕");
     console.log("书籍的目录处理完毕");
-    console.log("书籍处理完毕");
+    console.log("书籍处理完毕");*/
 });
 
 export default router;

+ 84 - 0
epub_node/router/epub_old/font.js

@@ -0,0 +1,84 @@
+import logger from "#logger";
+import { files_insert, files_insert_link_epub } from "#db";
+import { dirExists } from "#utils";
+import fs from "node:fs";
+import { calculateMD5 } from "./image.js";
+import cliProgress from 'cli-progress';
+
+export async function saveAllFount(epub, uploadPath, book_id, author_id) {
+  dirExists(uploadPath);
+  dirExists(`${uploadPath}font/`);
+
+  // 获取原始数据源
+  const getAllCss = epub.zip.names.filter((elm) => elm.includes("ttf"));
+  const base_path = `${uploadPath}epub-extract/`;
+
+  if (getAllCss.length) {
+    const cssRes = await Promise.allSettled(
+      getAllCss.map((img) => {
+        return fs.promises.readFile(base_path + img, "utf8");
+      })
+    );
+
+    const allCss_fulfilled = cssRes
+      .map((img_fulfilled, index) => {
+        const cssPath = getAllCss[index];
+        if (img_fulfilled.status === "fulfilled") {
+          return {
+            index,
+            path: base_path + cssPath,
+            cssPath,
+            css_data: img_fulfilled.value,
+            mimeType: "font/ttf",
+          };
+        }
+        return null;
+      })
+      .filter(Boolean);
+
+    // Initialize the progress bar
+    const progressBar = new cliProgress.SingleBar({}, cliProgress.Presets.shades_classic);
+    progressBar.start(allCss_fulfilled.length, 0);
+
+    await Promise.allSettled(
+      allCss_fulfilled.map(async (elm, index) => {
+        try {
+          const md5 = await calculateMD5(elm.path);
+          const elmName = elm.cssPath.split('/').pop();
+
+          // Write the font data to a file
+          await fs.promises.writeFile(`${uploadPath}font/${md5}.ttf`, elm.css_data);
+          logger.info("Font data saved to " + md5);
+
+          const params = {
+            file_id: md5,
+            md5: md5,
+            mimetype: elm.mimeType,
+            size: elm.css_data.length,
+            name: elmName,
+            path: `${uploadPath}font/${md5}.ttf`,
+            source_id: md5,
+          };
+
+          await files_insert(params);
+          await files_insert_link_epub({
+            file_id: md5,
+            book_id,
+            author_id,
+          });
+
+          // Update the progress bar
+          progressBar.update(index + 1);
+        } catch (err) {
+          logger.error("Error processing font:", err);
+        }
+      })
+    );
+
+    // Update the progress bar
+    progressBar.update(allCss_fulfilled.length);
+
+    // Stop the progress bar
+    progressBar.stop();
+  }
+}

+ 137 - 0
epub_node/router/epub_old/image.js

@@ -0,0 +1,137 @@
+import { dirExists } from "#utils";
+import { files_insert, files_insert_link_epub } from "#db";
+import crypto from "crypto";
+import fs from "node:fs";
+import logger from "#logger";
+import cliProgress from 'cli-progress';
+
+export async function saveImgs(epub, uploadPath, book_id, author_id) {
+  dirExists(uploadPath + "image/");
+  const imgs = epub.listImage();
+
+  if (imgs.length) {
+    const imgRes = await Promise.allSettled(
+      imgs.map((img) => epub.getImageAsync(img.id))
+    );
+    const imgs_fulfilled = imgs.map((img, index) => {
+      const img_fulfilled = imgRes[index];
+      if (img_fulfilled.status === "fulfilled") {
+        const [img_data, img_mimeType] = img_fulfilled.value;
+        return {
+          ...img,
+          img_data,
+          img_mimeType,
+        };
+      }
+      return null;
+    }).filter(Boolean);
+
+    // Initialize the progress bar
+    const progressBar = new cliProgress.SingleBar({}, cliProgress.Presets.shades_classic);
+    progressBar.start(imgs_fulfilled.length, 0);
+
+    await Promise.allSettled(
+      imgs_fulfilled.map(async (elm, index) => {
+        try {
+          const img_md5 = await calculateMD5FromBuffer(elm.img_data);
+          const imgUploadPath = `${uploadPath}image/${img_md5}`;
+          const params = {
+            file_id: img_md5,
+            md5: img_md5,
+            mimetype: elm.img_mimeType,
+            size: elm.img_data.length,
+            name: elm.id,
+            path: elm.href,
+            source_id: elm.id,
+          };
+
+          await fs.promises.writeFile(imgUploadPath, elm.img_data);
+          logger.info("Img data saved to " + img_md5);
+
+          await files_insert(params);
+          await files_insert_link_epub({
+            file_id: img_md5,
+            book_id,
+            author_id,
+          });
+
+          // Update the progress bar
+          progressBar.update(index + 1);
+        } catch (err) {
+          logger.error("Error processing image:", err);
+        }
+      })
+    );
+
+    // Update the progress bar
+    progressBar.update(imgs_fulfilled.length);
+    // Stop the progress bar
+    progressBar.stop();
+  }
+}
+
+export function calculateMD5(filePath) {
+  return new Promise((resolve, reject) => {
+    try {
+      const hash = crypto.createHash("md5");
+      const stream = fs.createReadStream(filePath);
+
+      stream.on("data", (chunk) => {
+        hash.update(chunk);
+      });
+
+      stream.on("end", () => {
+        resolve(hash.digest("hex"));
+      });
+    } catch (err) {
+      resolve("");
+    }
+  });
+}
+
+export async function calculateMD5FromStream(fileStream) {
+  return new Promise((resolve, reject) => {
+    const hash = crypto.createHash("md5");
+
+    // 错误处理
+    fileStream.on("error", (err) => {
+      console.error("Error reading file stream:", err);
+      reject("生成失败!"); // 使用 reject 传递错误信息
+    });
+
+    // 监听 'data' 事件,更新 MD5 哈希
+    fileStream.on("data", (chunk) => {
+      hash.update(chunk);
+    });
+
+    // 监听 'end' 事件,文件读取完毕后输出 MD5 值
+    fileStream.on("end", () => {
+      const md5 = hash.digest("hex");
+      resolve(md5); // 正常计算完 MD5 后 resolve
+    });
+  });
+}
+
+export function calculateMD5FromBuffer(buffer) {
+  return new Promise((resolve, reject) => {
+    try {
+      // 使用 crypto 计算 MD5
+      const hash = crypto.createHash("md5");
+      hash.update(buffer); // 直接更新 hash
+
+      const md5 = hash.digest("hex"); // 获取最终的 MD5 值
+      resolve(md5); // 返回 MD5
+    } catch (err) {
+      console.error("Error calculating MD5:", err);
+      reject("生成失败!");
+    }
+  });
+}
+
+const formatSize = (bytes) => {
+  if (bytes < 1024) return `${bytes} 字节`;
+  else if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(2)} KB`;
+  else if (bytes < 1024 * 1024 * 1024)
+    return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
+  else return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`;
+};

+ 230 - 0
epub_node/router/epub_old/index.js

@@ -0,0 +1,230 @@
+// 添加账本
+import {
+    getFileBymd5,
+    searchFileByPath,
+    author_insert,
+    get_author_info,
+    clear_all_data,
+    getChaptersByBookId,
+} from "#db";
+import logger from "#logger";
+import path from "node:path";
+import fs from "node:fs";
+import express from "express";
+import {EPub} from "epub2";
+import {v4 as uuidv4} from "uuid";
+import {dirExists, isFileSync, isDir, waittime} from "#utils";
+
+import {saveImgs, calculateMD5} from "./image.js";
+import {htmlParser, saveMateInfo} from "./txt.js";
+import {saveAllCSS} from "./style.js";
+import {saveAllFount} from "./font.js";
+import {saveToc, fetchAndBuildTree, fetchNextFile} from "./toc.js";
+
+const router = express.Router();
+
+// middleware that is specific to this router
+router.use(function timeLog(req, res, next) {
+    next();
+});
+// define the home page route
+router.get("/", async function (req, res) {
+    res.send("epub types");
+});
+
+// 获取所有的章节数据
+router.get("/chapter_all/:book_id", async function (req, res) {
+    const book_id = req.params.book_id; // 获取 fileId 参数
+    // res.send("epub types" + book_id);
+    // console.log(393939, '开始查询')
+    console.time('getChaptersByBookId')
+    const chapter_all = await fetchAndBuildTree(book_id);
+    // const chapter_all = await getChaptersByBookId(book_id);;
+    // console.log(393939, chapter_all.length, chapter_all);
+    fs.writeFileSync(`./chapter_${book_id}.json`, JSON.stringify(chapter_all));
+    res.json(chapter_all)
+    console.timeEnd('getChaptersByBookId')
+});
+
+// 获取下一页的数据
+router.get("/next_chapter/:file_id", async function (req, res) {
+    const file_id = req.params.file_id; // 获取 fileId 参数
+    // res.send("epub types" + book_id);
+    console.log(393939, '开始查询')
+    console.time('getChaptersByBookId')
+    await fetchNextFile(file_id);
+    res.json({code: 200})
+    // // const chapter_all = await getChaptersByBookId(book_id);;
+    // console.log(393939, chapter_all.length, chapter_all);
+    // fs.writeFileSync(`./chapter_${book_id}.json`, JSON.stringify(chapter_all));
+    
+    console.timeEnd('getChaptersByBookId')
+});
+router.get("/clear", async function (req, res) {
+    await clear_all_data();
+    // 使用 fs.rmSync 删除非空目录及其内容
+    fs.rmSync("./base_files", {recursive: true, force: true});
+    res.send("epub types");
+});
+
+// define the about route
+router.get("/about", function (req, res) {
+    res.send("About types");
+});
+
+router.get("/html/:fileId", async function (req, res) {
+    const fileId = req.params.fileId; // 获取 fileId 参数
+    logger.info(`Found ${fileId}`);
+    const fileRow = await getFileBymd5(fileId);
+    
+    if (!fileRow) {
+        return res.status(404).send("文件查询失败");
+    }
+    const uploadPath =
+        "./base_files/" + fileRow.book_id + "/Text/" + fileId + ".html";
+    
+    const filePath = path.resolve(uploadPath);
+    // 检查文件是否存在
+    if (!fs.existsSync(filePath)) {
+        return res.status(404).send("服务器中不存在该文件");
+    }
+    const htmlStr = fs.readFileSync(filePath, "utf8");
+    const newHtmlStr = htmlStr.replace(/href="[^#"]*#/g, `href="${fileId}#`);
+    
+    // 返回文件
+    res.setHeader("Content-Type", fileRow.mimetype);
+    // res.sendFile(filePath);
+    res.send(newHtmlStr);
+});
+
+router.get("/img/:fileId", async function (req, res) {
+    const fileId = req.params.fileId; // 获取 fileId 参数
+    logger.info(`Found ${fileId}`);
+    
+    const fileRow = await getFileBymd5(fileId);
+    
+    if (!fileRow) {
+        return res.status(404).send("文件查询失败");
+    }
+    const uploadPath = "./base_files/" + fileRow.book_id + "/image/" + fileId;
+    const filePath = path.resolve(uploadPath);
+    
+    // 检查文件是否存在
+    if (!fs.existsSync(filePath)) {
+        return res.status(404).send("服务器中不存在该文件");
+    }
+    
+    // 返回文件
+    res.setHeader("Content-Type", fileRow.mimetype);
+    res.sendFile(filePath);
+});
+
+router.get("/css/:fileId", async function (req, res) {
+    const fileId = req.params.fileId; // 获取 fileId 参数
+    logger.info(`Found ${fileId}`);
+    const fileRow = await getFileBymd5(fileId);
+    
+    if (!fileRow) {
+        return res.status(404).send("文件查询失败");
+    }
+    const uploadPath =
+        "./base_files/" + fileRow.book_id + "/style/" + fileId + ".css";
+    
+    const filePath = path.resolve(uploadPath);
+    // 检查文件是否存在
+    if (!fs.existsSync(filePath)) {
+        return res.status(404).send("服务器中不存在该文件");
+    }
+    
+    // 返回文件
+    res.setHeader("Content-Type", fileRow.mimetype);
+    res.sendFile(filePath);
+});
+
+// define the about route
+router.put("/", async function (req, res) {
+    let sampleFile;
+    let uploadPath;
+    let epubData;
+    let zipEpubExtract;
+    let epubFilePath;
+    let epub;
+    
+    if (!req.files || Object.keys(req.files).length === 0) {
+        return res.status(400).send("No files were uploaded.");
+    }
+    
+    sampleFile = req.files.file;
+    
+    let file_md5 = sampleFile.md5;
+    uploadPath = `./base_files/${file_md5}/`;
+    epubFilePath = uploadPath + sampleFile.md5 + ".epub";
+    zipEpubExtract = uploadPath + "epub-extract/";
+    
+    dirExists(uploadPath);
+    
+    await waittime(200);
+    
+    const isFile = isFileSync(epubFilePath);
+    
+    // 移动上传文件至指定目录
+    if (!isFile) {
+        sampleFile.mv(epubFilePath, function (err) {
+            console.log("移动上传文件至指定目录的反馈", err);
+        });
+        epubData = sampleFile.data;
+    } else {
+        epubData = fs.readFileSync(epubFilePath);
+        file_md5 = await calculateMD5(epubFilePath);
+    }
+    
+    /* 是否需要解压文件 */
+    if (!(await isDir(zipEpubExtract))) {
+        epub = await EPub.createAsync(epubData, null, "");
+        dirExists(zipEpubExtract);
+        epub.zip.admZip.extractAllTo(zipEpubExtract);
+    } else {
+        epub = await EPub.createAsync(epubData, null, "");
+    }
+    
+    // 生成作者的数据
+    let authorInfo = await get_author_info(epub.metadata.creator);
+    let author_id = authorInfo.author_id;
+    if (!authorInfo) {
+        author_id = uuidv4();
+        await author_insert({name: epub.metadata.creator, author_id: author_id});
+    }
+    /*
+        1、读取图片信息
+        2、替换文件中的图片内容
+        3、存储html数据
+        4、存储css数据
+        5、存储章节数据
+       */
+    res.send("书籍正在处理中,请稍后!");
+    console.log("书籍正在处理中,请稍后!");
+    await saveMateInfo(epub, uploadPath, file_md5, author_id);
+    logger.info("书籍的基础数据处理完毕");
+    console.log("书籍的基础数据处理完毕");
+    await saveImgs(epub, uploadPath, file_md5, author_id);
+    logger.info("书籍的图片数据处理完毕");
+    console.log("书籍的图片数据处理完毕");
+    await saveAllFount(epub, uploadPath, file_md5, author_id);
+    logger.info("书籍的字体数据处理完毕");
+    console.log("书籍的字体数据处理完毕");
+    await saveAllCSS(epub, uploadPath, file_md5, author_id);
+    logger.info("书籍的css数据处理完毕");
+    console.log("书籍的css数据处理完毕");
+    // 存储html数据
+    await htmlParser(epub, zipEpubExtract, file_md5, author_id);
+    logger.info("书籍的章节数据处理完毕");
+    console.log("书籍的章节数据处理完毕");
+    // 章节数据
+    await saveToc(epub, zipEpubExtract, file_md5, author_id);
+    logger.info("书籍的目录处理完毕");
+    logger.info("书籍处理完毕");
+    console.log("书籍的目录处理完毕");
+    console.log("书籍处理完毕");
+});
+
+export default router;

+ 99 - 0
epub_node/router/epub_old/style.js

@@ -0,0 +1,99 @@
+import logger from "#logger";
+import { files_insert, files_insert_link_epub } from "#db";
+import { dirExists } from "#utils";
+import fs from "node:fs";
+import { calculateMD5 } from "./image.js";
+import cliProgress from "cli-progress";
+
+export async function saveAllCSS(epub, uploadPath, book_id, author_id) {
+  dirExists(uploadPath);
+  dirExists(`${uploadPath}style/`);
+
+  // 获取原始数据源
+  const getAllCss = epub.zip.names.filter((elm) => elm.includes("css"));
+  const base_path = `${uploadPath}epub-extract/`;
+
+  if (getAllCss.length) {
+    const cssRes = await Promise.allSettled(
+      getAllCss.map((css) => fs.promises.readFile(base_path + css, "utf8"))
+    );
+
+    const allCss_fulfilled = cssRes
+      .map((css_fulfilled, index) => {
+        const cssPath = getAllCss[index];
+        if (css_fulfilled.status === "fulfilled") {
+          return {
+            index,
+            path: base_path + cssPath,
+            cssPath,
+            css_data: css_fulfilled.value,
+            mimeType: "text/css",
+          };
+        }
+        return null;
+      })
+      .filter(Boolean);
+
+    // Initialize the progress bar
+    const progressBar = new cliProgress.SingleBar(
+      {},
+      cliProgress.Presets.shades_classic
+    );
+    progressBar.start(allCss_fulfilled.length, 0);
+
+    await Promise.allSettled(
+      allCss_fulfilled.map(async (elm, index) => {
+        try {
+          const md5 = await calculateMD5(elm.path);
+          const elmName = elm.cssPath.split("/").pop();
+
+          // Write the CSS data to a file
+          await fs.promises.writeFile(
+            `${uploadPath}style/${md5}.css`,
+            elm.css_data
+          );
+          logger.info("CSS data saved to " + md5);
+
+          let file_path;
+          let source_id;
+          for (const m_key in epub.manifest) {
+            const mElm = epub.manifest[m_key];
+            if (mElm.href.includes(elmName) && !source_id) {
+              source_id = mElm.id;
+              file_path = mElm.href;
+              break;
+            }
+          }
+
+          const params = {
+            file_id: md5,
+            md5: md5,
+            mimetype: elm.mimeType,
+            size: elm.css_data.length,
+            name: elmName,
+            path: file_path,
+            source_id: source_id,
+          };
+
+          await files_insert(params);
+          await files_insert_link_epub({
+            file_id: md5,
+            book_id,
+            author_id,
+          });
+
+          // Update the progress bar
+          progressBar.update(index + 1);
+        } catch (err) {
+          logger.error("Error processing CSS:", err);
+        }
+      })
+    );
+
+    // Update the progress bar
+    progressBar.update(allCss_fulfilled.length);
+
+    // Stop the progress bar
+    progressBar.stop();
+  }
+}

+ 122 - 0
epub_node/router/epub_old/toc.js

@@ -0,0 +1,122 @@
+import logger from "#logger";
+import {chapter_insert, searchChapterInfoForPath, getChaptersByBookId, getFileBymd5} from "#db";
+import cliProgress from 'cli-progress';
+import {EPub} from "epub2";
+import path from "node:path";
+import fs from "node:fs";
+
+export async function saveToc(epub, uploadPath, book_id, author_id) {
+    const progressBar = new cliProgress.SingleBar({}, cliProgress.Presets.shades_classic);
+    progressBar.start(epub.toc.length, 0);
+    
+    // 栈用于追踪父章节的 ID
+    const parentStack = [];
+    
+    for (const elm of epub.toc) {
+        try {
+            const match = `${elm.href}`.match(/\/(.*\.html).*/);
+            let chapterInfo = {file_id: ''};
+            let path = '';
+            
+            if (match) {
+                path = match[1];
+                chapterInfo = await searchChapterInfoForPath(path, book_id);
+            }
+            
+            const name = `${elm.title}`;
+            elm.title = '';
+            
+            // 确定 parent_id
+            let parent_id = null;
+            while (parentStack.length > 0 && parentStack[parentStack.length - 1].level >= elm.level) {
+                parentStack.pop();
+            }
+            if (parentStack.length > 0) {
+                parent_id = parentStack[parentStack.length - 1].id;
+            }
+            
+            const params = {
+                name: name,
+                book_id: book_id,
+                author_id: author_id,
+                content: JSON.stringify(elm),
+                level: elm.level,
+                order_index: elm.order,
+                order_id: chapterInfo.file_id,
+                old_path: elm.href,
+                path: `./base_files/${book_id}/Text/${chapterInfo.file_id}.html`,
+                parent_id: parent_id,
+            };
+            logger.info(params);
+            
+            // 插入数据库并获取新插入的章节 ID
+            const insertedId = await chapter_insert(params);
+            
+            // 将当前章节推入栈中
+            parentStack.push({id: insertedId.insertId, level: elm.level});
+        } catch (e) {
+            logger.error(e);
+        } finally {
+            progressBar.increment();
+        }
+    }
+    
+    progressBar.stop();
+}
+
+function buildTree(data) {
+    const nodeMap = new Map();
+    const tree = [];
+    
+    // 初始化节点映射
+    data.forEach(node => {
+        // 确保每个节点都有 children 属性
+        node.children = [];
+        nodeMap.set(node.id, node);
+    });
+    
+    // 构建树
+    data.forEach(node => {
+        if (node.parent_id !== null) {
+            // 确保 id 和 parent_id 类型一致
+            const parentNode = nodeMap.get(Number(node.parent_id));
+            if (parentNode) {
+                parentNode.children.push(node);
+            }
+        } else {
+            // 顶层节点
+            tree.push(node);
+        }
+    });
+    
+    return tree;
+}
+
+export async function fetchAndBuildTree(book_id) {
+    // 从数据库中获取所有章节数据
+    const chapters = await getChaptersByBookId(book_id);
+    return buildTree(chapters)
+}
+
+export async function fetchNextFile(fileId, type) {
+    // 从数据库中获取所有章节数据
+    logger.info(`Found ${fileId}`);
+    const fileRow = await getFileBymd5(fileId);
+    console.log('fileRow', fileRow);
+    console.log('\n')
+    console.log('\n')
+    const epubPath = `./base_files/${fileRow.book_id}/${fileRow.book_id}.epub`
+    if (fs.existsSync(epubPath)) {
+        console.time('chapterIndex')
+        const epubData = fs.readFileSync(epubPath)
+        const epub = await EPub.createAsync(epubData, null, "");
+        console.log("\nSPINE:\n");
+        console.log('epubFlowLength', epub.flow.length);
+        // console.log('epubFlowLength', epub.flow[30]);
+        
+        const chapterIndex = epub.flow.findIndex(elm => elm.id === fileRow.source_id);
+        
+        console.log(117, epub.flow[chapterIndex - 1], epub.flow[chapterIndex], epub.flow[chapterIndex + 1])
+        console.timeEnd('chapterIndex')
+    }
+}

+ 145 - 0
epub_node/router/epub_old/txt.js

@@ -0,0 +1,145 @@
+import fs from "node:fs";
+import path from "node:path";
+import {dirExists, isFileSync, isDir, waittime} from "#utils";
+import {
+    getFileBymd5,
+    searchFileByPath,
+    searchFileByName,
+    files_insert_link_epub,
+    files_insert,
+    book_mate_insert,
+} from "#db";
+import {calculateMD5} from "./image.js";
+
+const imageExtensions = [".png", ".jpg", ".jpeg", ".svg", ".gif"];
+
+async function processFiles(elmData, file_md5) {
+    const rows = elmData.toString().split(/\n/);
+    const results = [];
+    
+    for (const rowtext of rows) {
+        if (
+            rowtext.includes("<img ") &&
+            imageExtensions.some((ext) => rowtext.includes(ext))
+        ) {
+            const match = rowtext.match(/src=("|')(.*\/(.*\.[a-zA-Z]+))("|')/);
+            if (match) {
+                const [, , imgPath, imageSrc] = match;
+                const imgRow = await searchFileByPath(imageSrc);
+                if (imgRow) {
+                    results.push(
+                        rowtext.replace(imgPath, `/api/v1/epub/img/${imgRow.file_id}`) +
+                        "\n"
+                    );
+                    continue;
+                }
+            }
+        } else if (rowtext.includes(".css")) {
+            const match = rowtext.match(/.*="(.*\/?(.*\.css))/);
+            const [elmPath, elmName] = `${rowtext}`.match(/.*\/(.*\.css)/);
+            if (match) {
+                const [, cssPath, cssSrc] = match;
+                const imgNameRow = await searchFileByName(elmName, file_md5);
+                if (imgNameRow) {
+                    results.push(
+                        rowtext.replace(cssPath, `/api/v1/epub/css/${imgNameRow.file_id}`) +
+                        "\n"
+                    );
+                    continue;
+                }
+            }
+        } else if (rowtext.includes(".ttf")) {
+            const match = rowtext.match(/.*\((.*\/?(.*ttf))\)./);
+            if (match) {
+                const [, cssPath, cssSrc] = match;
+                try {
+                    const imgRow = await searchFileByPath(cssSrc, file_md5);
+                    if (imgRow) {
+                        results.push(
+                            rowtext.replace(cssPath, `/api/v1/epub/css/${imgRow.file_id}`) +
+                            "\n"
+                        );
+                        continue;
+                    } else {
+                        console.warn(`Font file not found for path: ${cssSrc}`);
+                    }
+                } catch (error) {
+                    console.error("Error searching for font file:", error);
+                }
+            }
+        }
+        
+        results.push(rowtext + "\n");
+    }
+    
+    return results.join("");
+}
+
+export async function htmlParser(epub, zipEpubExtract, file_md5, author_id) {
+    const needSetImage = epub.zip.names.filter(
+        (elm) => elm.endsWith(".html") || elm.endsWith(".css")
+    );
+    
+    const needSetFont = epub.zip.names.filter((elm) => elm.endsWith(".ttf"));
+    const basePath = path.join("./base_files", file_md5, "Text");
+    const styleBasePath = path.join("./base_files", file_md5, "style");
+    dirExists(basePath);
+    dirExists(styleBasePath);
+    
+    for (const elm of needSetImage) {
+        console.log('Processing:', elm);
+        const filePath = path.join(zipEpubExtract, elm);
+        const elmData = fs.readFileSync(filePath);
+        const htmlStr = await processFiles(elmData, file_md5);
+        let file_path;
+        let source_id;
+        
+        if (htmlStr) {
+            fs.writeFileSync(filePath, htmlStr);
+            
+            const htmlMd5 = await calculateMD5(filePath);
+            const isCss = elm.endsWith(".css");
+            const newFilePath = path.join(
+                isCss ? styleBasePath : basePath,
+                `${htmlMd5}.${isCss ? "css" : "html"}`
+            );
+            
+            for (const m_key of Object.keys(epub.manifest)) {
+                const mElm = epub.manifest[m_key];
+                if (mElm.href.indexOf(elm) > -1 && !source_id) {
+                    source_id = mElm.id;
+                    file_path = mElm.href;
+                }
+            }
+            
+            const params = {
+                file_id: htmlMd5,
+                md5: htmlMd5,
+                mimetype: isCss ? "text/css" : "text/html",
+                size: Buffer.byteLength(htmlStr),
+                name: `${htmlMd5}.${isCss ? "css" : "html"}`,
+                path: file_path,
+                source_id: source_id,
+            };
+            await files_insert(params);
+            await files_insert_link_epub({
+                file_id: htmlMd5,
+                book_id: file_md5,
+                author_id,
+            });
+            await fs.promises.writeFile(newFilePath, htmlStr);
+        }
+    }
+}
+
+// saveMateInfo
+export async function saveMateInfo(epub, zipEpubExtract, file_md5, author_id) {
+    const params = {
+        book_name: epub.metadata.title,
+        book_id: file_md5,
+        book_md5: file_md5,
+        author_id: author_id,
+    };
+    
+    const res = await book_mate_insert(params);
+}