Răsfoiți Sursa

重构前的备份

john 7 luni în urmă
părinte
comite
99074699af

+ 52 - 4
epub_node/db/chapter.js

@@ -21,13 +21,14 @@ export async function chapter_insert({
                                          order_id = "",
                                          old_path = "",
                                          path = "",
+                                         parent_id= ''
                                      }) {
     // 假设最大长度为255,您可能需要根据实际数据库定义调整
     const maxLength = 255;
     
     const sql = `
-        INSERT INTO chapter (name, book_id, author_id, content, level, order_index, order_id, old_path, path)
-        VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);
+        INSERT INTO chapter (name, book_id, author_id, content, level, order_index, order_id, old_path, path, parent_id)
+        VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
     `;
     const values = [
         name,
@@ -39,6 +40,7 @@ export async function chapter_insert({
         order_id,
         old_path,
         path,
+        parent_id
     ];
     
     return new Promise((resolve, reject) => {
@@ -78,8 +80,7 @@ export async function searchChapterInfoForPath(path, book_id) {
 }
 
 /*获取当前书籍的所有数据*/
-export async function searchChapterForBookId({book_id, level = 10}) {
-    console.log(8282, level);
+export async function getChaptersByBookId(book_id, level = 0, parent_id = '') {
     return new Promise((resolve, reject) => {
         /*
          SELECT files.*, chapter.*
@@ -89,13 +90,58 @@ export async function searchChapterForBookId({book_id, level = 10}) {
          WHERE book_link_file.book_id = '28b639bf9362ad3b2cd5a24cb6d811c0'
          AND files.mimetype = 'text/html' AND chapter.level = '1';
          */
+        /*const query = `
+            SELECT files.*, chapter.*
+            FROM files
+                     INNER JOIN book_link_file ON files.file_id = book_link_file.file_id
+                     INNER JOIN chapter ON files.path LIKE CONCAT('%', chapter.order_id, '%')
+            WHERE book_link_file.book_id = '${book_id}'
+              AND files.mimetype = 'text/html' ${level === 10 ? '' : `AND chapter.level = '${level}'`};
+        `;*/
+        // SELECT id,name,level,order_id,parent_id,path
         const query = `
+            SELECT *
+            FROM chapter
+              WHERE book_id = '${book_id}'
+        `;
+        console.log(9292, query)
+        connection.query(query, [], (err, rows) => {
+            if (err) {
+                return reject(err);
+            }
+            resolve(rows);
+        });
+    });
+}
+
+
+
+
+
+/* 获取当前书籍下一章的file_id */
+export async function getChaptersByBookId(book_id, level = 0, parent_id = '') {
+    return new Promise((resolve, reject) => {
+        /*
+         SELECT files.*, chapter.*
+         FROM files
+         INNER JOIN book_link_file ON files.file_id = book_link_file.file_id
+         INNER JOIN chapter ON files.path like CONCAT('%', chapter.order_id, '%')
+         WHERE book_link_file.book_id = '28b639bf9362ad3b2cd5a24cb6d811c0'
+         AND files.mimetype = 'text/html' AND chapter.level = '1';
+         */
+        /*const query = `
             SELECT files.*, chapter.*
             FROM files
                      INNER JOIN book_link_file ON files.file_id = book_link_file.file_id
                      INNER JOIN chapter ON files.path LIKE CONCAT('%', chapter.order_id, '%')
             WHERE book_link_file.book_id = '${book_id}'
               AND files.mimetype = 'text/html' ${level === 10 ? '' : `AND chapter.level = '${level}'`};
+        `;*/
+        // SELECT id,name,level,order_id,parent_id,path
+        const query = `
+            SELECT *
+            FROM chapter
+              WHERE book_id = '${book_id}'
         `;
         console.log(9292, query)
         connection.query(query, [], (err, rows) => {
@@ -106,3 +152,5 @@ export async function searchChapterForBookId({book_id, level = 10}) {
         });
     });
 }
+
+

+ 54 - 1
epub_node/db/update.sql

@@ -118,4 +118,57 @@ alter table chapter
 
 
 
-CREATE INDEX idx_files_source_id ON files (source_id);
+CREATE INDEX idx_files_source_id ON files (source_id);
+
+
+
+
+
+SELECT f.*, chapter.*
+FROM files f
+         JOIN epub_manage.book_link_file blf ON f.file_id = blf.file_id
+         JOIN epub_manage.chapter chapter ON chapter.order_id = blf.file_id
+WHERE blf.book_id = '1a7c3ccc61a0c00998d588971ee4e567'
+  AND f.id > (
+    SELECT f2.id
+    FROM files f2
+             JOIN epub_manage.book_link_file blf2 ON f2.file_id = blf2.file_id
+    WHERE f2.file_id = '65403824ba1ebed1576f950d4fb495c0'
+      AND blf2.book_id = '1a7c3ccc61a0c00998d588971ee4e567'
+    LIMIT 1
+    )
+ORDER BY f.id ASC
+    LIMIT 1;
+
+
+select * from files f JOIN epub_manage.book_link_file blf ON f.file_id = blf.file_id and blf.book_id = '1a7c3ccc61a0c00998d588971ee4e567' where f.file_id = '65403824ba1ebed1576f950d4fb495c0' ORDER BY f.id ASC
+    LIMIT 2;
+
+
+SELECT *
+FROM files f
+         JOIN epub_manage.book_link_file blf ON f.file_id = blf.file_id
+WHERE blf.book_id = '1a7c3ccc61a0c00998d588971ee4e567' -- 需要根据实际情况填写
+  AND f.id > (
+    SELECT MAX(f2.id)
+    FROM files f2
+             JOIN epub_manage.book_link_file blf2 ON f2.file_id = blf2.file_id
+    WHERE f2.file_id = '65403824ba1ebed1576f950d4fb495c0'
+)
+ORDER BY f.id ASC
+    LIMIT 1;
+
+
+SELECT f.*
+FROM files f
+         JOIN epub_manage.book_link_file blf ON f.file_id = blf.file_id
+WHERE blf.book_id = '1a7c3ccc61a0c00998d588971ee4e567'
+  AND f.id > (
+    SELECT f2.id
+    FROM files f2
+    WHERE f2.file_id = '65403824ba1ebed1576f950d4fb495c0'
+    ORDER BY f2.id DESC
+    LIMIT 1
+    )
+ORDER BY f.id ASC
+    LIMIT 1;

+ 4 - 4
epub_node/environment/index.js

@@ -7,20 +7,20 @@ function dbInfo() {
   //   password: "btm-2024-.",
   //   database: "epub_manage",
   // };
-  /*return {
+  return {
     host: "localhost",
     port: 3306,
     user: "root",
     password: "12345678",
     database: "epub_manage",
-  };*/
-  return {
+  };
+  /*return {
     host: "192.168.2.101",
     port: 6806,
     user: "root",
     password: "admin",
     database: "epub_manage",
-  };
+  };*/
 }
 
 

+ 29 - 9
epub_node/router/epub/index.js

@@ -5,7 +5,7 @@ import {
     author_insert,
     get_author_info,
     clear_all_data,
-    searchChapterForBookId,
+    getChaptersByBookId,
 } from "#db";
 import logger from "#logger";
 import path from "node:path";
@@ -19,7 +19,7 @@ import {saveImgs, calculateMD5} from "./image.js";
 import {htmlParser, saveMateInfo} from "./txt.js";
 import {saveAllCSS} from "./style.js";
 import {saveAllFount} from "./font.js";
-import {saveToc} from "./toc_old.js";
+import {saveToc, fetchAndBuildTree, fetchNextFile} from "./toc.js";
 
 const router = express.Router();
 
@@ -35,12 +35,30 @@ router.get("/", async function (req, res) {
 // 获取所有的章节数据
 router.get("/chapter_all/:book_id", async function (req, res) {
     const book_id = req.params.book_id; // 获取 fileId 参数
-    res.send("epub types" + book_id);
+    // 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('searchChapterForBookId')
-    const chapter_all = await searchChapterForBookId({book_id, level: '1'});
-    console.timeEnd('searchChapterForBookId')
-    console.log(393939, chapter_all.length, chapter_all[0]);
+    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();
@@ -64,17 +82,19 @@ router.get("/html/:fileId", async function (req, res) {
     }
     const uploadPath =
         "./base_files/" + fileRow.book_id + "/Text/" + fileId + ".html";
-    console.log(46, fileRow);
     
     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.sendFile(filePath);
+    res.send(newHtmlStr);
 });
 
 router.get("/img/:fileId", async function (req, res) {

+ 111 - 46
epub_node/router/epub/toc.js

@@ -1,57 +1,122 @@
-import { Worker } from 'worker_threads';
+import logger from "#logger";
+import {chapter_insert, searchChapterInfoForPath, getChaptersByBookId, getFileBymd5} from "#db";
 import cliProgress from 'cli-progress';
-
-const MAX_THREADS = 10;
+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);
-
-    let activeWorkers = 0;
-    let currentIndex = 0;
-    const promises = [];
-
-    function startNextWorker() {
-        if (currentIndex >= epub.toc.length) {
-            return;
+    
+    // 栈用于追踪父章节的 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();
+}
 
-        const elm = epub.toc[currentIndex];
-        currentIndex++;
-        activeWorkers++;
-
-        const promise = new Promise((resolve, reject) => {
-            const worker = new Worker(new URL('./worker.mjs', import.meta.url), {
-                workerData: { elm, book_id, author_id }
-            });
-
-            worker.on('message', (message) => {
-                progressBar.increment();
-                activeWorkers--;
-                resolve(message);
-                startNextWorker();
-            });
-
-            worker.on('error', (err) => {
-                activeWorkers--;
-                reject(err);
-                startNextWorker();
-            });
-
-            worker.on('exit', (code) => {
-                if (code !== 0) {
-                    reject(new Error(`Worker stopped with exit code ${code}`));
-                }
-            });
-        });
+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;
+}
 
-        promises.push(promise);
-    }
+export async function fetchAndBuildTree(book_id) {
+    // 从数据库中获取所有章节数据
+    const chapters = await getChaptersByBookId(book_id);
+    return buildTree(chapters)
+}
 
-    for (let i = 0; i < Math.min(MAX_THREADS, epub.toc.length); i++) {
-        startNextWorker();
+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')
     }
-
-    await Promise.all(promises);
-    progressBar.stop();
 }

+ 0 - 47
epub_node/router/epub/toc_old.js

@@ -1,47 +0,0 @@
-import logger from "#logger";
-import { chapter_insert, searchChapterInfoForPath } from "#db";
-import cliProgress from 'cli-progress';
-
-export async function saveToc(epub, uploadPath, book_id, author_id) {
-    // Initialize the progress bar
-    const progressBar = new cliProgress.SingleBar({}, cliProgress.Presets.shades_classic);
-    progressBar.start(epub.toc.length, 0);
-
-    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);
-            }
-            logger.info(elm);
-            const name = `${elm.title}`;
-            elm.title = '';
-            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`,
-            };
-            logger.info(params);
-            await chapter_insert(params);
-        } catch (e) {
-            logger.error(e);
-        } finally {
-            // Update the progress bar
-            progressBar.increment();
-        }
-    }
-
-    // Stop the progress bar
-    progressBar.stop();
-}

+ 42 - 64
epub_node/router/epub/txt.js

@@ -10,57 +10,56 @@ import {
     book_mate_insert,
 } from "#db";
 import {calculateMD5} from "./image.js";
-import cliProgress from 'cli-progress';
 
-const imageExtensions = [".png", ".jpg", ".jpeg", ".svg"];
+const imageExtensions = [".png", ".jpg", ".jpeg", ".svg", ".gif"];
 
-async function processFiles(elmData, file_md5, elmIndex) {
+async function processFiles(elmData, file_md5) {
     const rows = elmData.toString().split(/\n/);
-    const promises = rows.map(async (rowtext) => {
+    const results = [];
+    
+    for (const rowtext of rows) {
         if (
             rowtext.includes("<img ") &&
             imageExtensions.some((ext) => rowtext.includes(ext))
         ) {
-            // const match = rowtext.match(/.*(..\/Images\/(.*\.(jpg|png|jpeg|svg))).*/);
-            const match = rowtext.match(/src=("|')(.*\/(.*\.[a-zA-Z]+))("|')/)
+            const match = rowtext.match(/src=("|')(.*\/(.*\.[a-zA-Z]+))("|')/);
             if (match) {
                 const [, , imgPath, imageSrc] = match;
                 const imgRow = await searchFileByPath(imageSrc);
                 if (imgRow) {
-                    return (
+                    results.push(
                         rowtext.replace(imgPath, `/api/v1/epub/img/${imgRow.file_id}`) +
                         "\n"
                     );
+                    continue;
                 }
             }
         } else if (rowtext.includes(".css")) {
             const match = rowtext.match(/.*="(.*\/?(.*\.css))/);
-            if (rowtext) {
-                const [elmPath , elmName] = `${rowtext}`.match(/.*\/?(.*\.css)/);
-
+            const [elmPath, elmName] = `${rowtext}`.match(/.*\/(.*\.css)/);
+            if (match) {
                 const [, cssPath, cssSrc] = match;
-                // const imgRow = await searchFileByPath(elmName, file_md5);
-                const imgNameRow =  await searchFileByName(elmName, file_md5);
+                const imgNameRow = await searchFileByName(elmName, file_md5);
                 if (imgNameRow) {
-                    return (
-                      rowtext.replace(cssPath, `/api/v1/epub/css/${imgNameRow.file_id}`) +
-                      "\n"
+                    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) {                       
-                        return (
+                    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}`);
                     }
@@ -70,53 +69,49 @@ async function processFiles(elmData, file_md5, elmIndex) {
             }
         }
         
-        return rowtext + "\n";
-    });
+        results.push(rowtext + "\n");
+    }
     
-    const results = await Promise.allSettled(promises);
     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")
+        (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);
-
-    // Initialize the progress bar
-    const progressBar = new cliProgress.SingleBar({}, cliProgress.Presets.shades_classic);
-    progressBar.start(needSetImage.length, 0);
-
-    for (let elmIndex = 0; elmIndex < needSetImage.length; elmIndex++) {
-        const elm = needSetImage[elmIndex];
+    
+    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, elmIndex);
+        const htmlStr = await processFiles(elmData, file_md5);
         let file_path;
         let source_id;
+        
         if (htmlStr) {
-            // console.log('needSetImage', elmIndex);
-            // fs.writeFileSync(filePath, 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"}`
+                isCss ? styleBasePath : basePath,
+                `${htmlMd5}.${isCss ? "css" : "html"}`
             );
-
-            Object.keys(epub.manifest).forEach(m_key => {
+            
+            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,
@@ -127,40 +122,23 @@ export async function htmlParser(epub, zipEpubExtract, file_md5, author_id) {
                 source_id: source_id,
             };
             await files_insert(params);
-            await Promise.all([
-                files_insert_link_epub({
-                    file_id: htmlMd5,
-                    book_id: file_md5,
-                    author_id,
-                }),
-                fs.promises.writeFile(newFilePath, htmlStr),
-            ]);
+            await files_insert_link_epub({
+                file_id: htmlMd5,
+                book_id: file_md5,
+                author_id,
+            });
+            await fs.promises.writeFile(newFilePath, htmlStr);
         }
-        // Update the progress bar
-        progressBar.update(elmIndex + 1);
     }
-    // Update the progress bar
-    progressBar.update(needSetImage.length);
-    // Stop the progress bar
-    progressBar.stop();
 }
 
-
 // saveMateInfo
 export async function saveMateInfo(epub, zipEpubExtract, file_md5, author_id) {
-    // book_mate_insert
     const params = {
         book_name: epub.metadata.title,
         book_id: file_md5,
         book_md5: file_md5,
-        // language: "",
-        // date: "",
-        // creatorFileAs: "",
-        // UUID: "",
-        // ISBN: "",
         author_id: author_id,
-        // category_id: "",
-        // Introduction: "",
     };
     
     const res = await book_mate_insert(params);

+ 0 - 157
epub_node/router/epub/txt_old.js

@@ -1,157 +0,0 @@
-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"];
-
-async function processFiles(elmData, file_md5, elmIndex) {
-    const rows = elmData.toString().split(/\n/);
-    const promises = rows.map(async (rowtext) => {
-        if (
-            rowtext.includes("<img ") &&
-            imageExtensions.some((ext) => rowtext.includes(ext))
-        ) {
-            // const match = rowtext.match(/.*(..\/Images\/(.*\.(jpg|png|jpeg|svg))).*/);
-            const match = rowtext.match(/src=("|')(.*\/(.*\.[a-zA-Z]+))("|')/)
-            if (match) {
-                const [, , imgPath, imageSrc] = match;
-                const imgRow = await searchFileByPath(imageSrc);
-                if (imgRow) {
-                    return (
-                        rowtext.replace(imgPath, `/api/v1/epub/img/${imgRow.file_id}`) +
-                        "\n"
-                    );
-                }
-            }
-        } else if (rowtext.includes(".css")) {
-            const match = rowtext.match(/.*="(.*\/?(.*\.css))/);
-            if (rowtext) {
-                const [elmPath , elmName] = `${rowtext}`.match(/.*\/?(.*\.css)/);
-
-                const [, cssPath, cssSrc] = match;
-                // const imgRow = await searchFileByPath(elmName, file_md5);
-                const imgNameRow =  await searchFileByName(elmName, file_md5);
-                if (imgNameRow) {
-                    return (
-                      rowtext.replace(cssPath, `/api/v1/epub/css/${imgNameRow.file_id}`) +
-                      "\n"
-                    );
-                }
-            }
-        } 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) {                       
-                        return (
-                            rowtext.replace(cssPath, `/api/v1/epub/css/${imgRow.file_id}`) +
-                            "\n"
-                        );
-                    } else {
-                        console.warn(`Font file not found for path: ${cssSrc}`);
-                    }
-                } catch (error) {
-                    console.error("Error searching for font file:", error);
-                }
-            }
-        }
-        
-        return rowtext + "\n";
-    });
-    
-    const results = await Promise.allSettled(promises);
-    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);
-    
-    await Promise.allSettled(
-        needSetImage.map(async (elm, elmIndex) => {
-            const filePath = path.join(zipEpubExtract, elm);
-            const elmData = fs.readFileSync(filePath);
-            const htmlStr = await processFiles(elmData, file_md5, elmIndex);
-            let file_path;
-            let source_id;
-            if (htmlStr) {
-                console.log('needSetImage', elmIndex)
-                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"}`
-                );
-                
-                Object.keys(epub.manifest).forEach(m_key => {
-                    const mElm = epub.manifest[m_key];
-                    if (mElm.href.indexOf(elm) > -1 && !source_id) {
-                        source_id = mElm.order_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 Promise.allSettled([
-                    files_insert_link_epub({
-                        file_id: htmlMd5,
-                        book_id: file_md5,
-                        author_id,
-                    }),
-                    fs.promises.writeFile(newFilePath, htmlStr),
-                ]);
-            }
-        })
-    );
-}
-
-// saveMateInfo
-export async function saveMateInfo(epub, zipEpubExtract, file_md5, author_id) {
-    // book_mate_insert
-    const params = {
-        book_name: epub.metadata.title,
-        book_id: file_md5,
-        book_md5: file_md5,
-        // language: "",
-        // date: "",
-        // creatorFileAs: "",
-        // UUID: "",
-        // ISBN: "",
-        author_id: author_id,
-        // category_id: "",
-        // Introduction: "",
-    };
-    
-    const res = await book_mate_insert(params);
-}

+ 1 - 0
epub_node/setHtml.js

@@ -0,0 +1 @@
+cheerio

+ 146 - 0
epub_node/tree.js

@@ -0,0 +1,146 @@
+// const chapter = [
+//     {
+//         level: 1,
+//         id: 1,
+//         name: '第一卷',
+//     },
+//     {
+//         level: 2,
+//         id: 2,
+//         name: '第一章',
+//     },
+//     {
+//         level: 3,
+//         id: 12,
+//         name: '第一节',
+//     },
+//     {
+//         level: 2,
+//         id: 3,
+//         name: '第二章',
+//     },
+//     {
+//         level: 2,
+//         id: 4,
+//         name: '第三章',
+//     },
+//     {
+//         level: 1,
+//         id: 5,
+//         name: '第二卷',
+//     },
+//     {
+//         level: 2,
+//         id: 6,
+//         name: '第一章',
+//     },
+//     {
+//         level: 2,
+//         id: 7,
+//         name: '第二章',
+//     },
+//     {
+//         level: 2,
+//         id: 8,
+//         name: '第三章',
+//     },
+//     {
+//         level: 1,
+//         id: 9,
+//         name: '第三卷',
+//     },
+//     {
+//         level: 2,
+//         id: 10,
+//         name: '第一章',
+//     },
+//     {
+//         level: 2,
+//         id: 11,
+//         name: '第二章',
+//     },
+// ]
+//
+// function buildTree(chapters) {
+//     const tree = [];
+//     let currentVolume = null;
+//
+//     chapters.forEach(chapter => {
+//         if (chapter.level === 1) {
+//             // Create a new volume node
+//             currentVolume = {
+//                 ...chapter,
+//                 children: []
+//             };
+//             tree.push(currentVolume);
+//         } else if (chapter.level === 2 && currentVolume) {
+//             // Add chapter to the current volume's children
+//             currentVolume.children.push(chapter);
+//         }
+//     });
+//
+//     return tree;
+// }
+//
+// const chapterTree = buildTree(chapter);
+//
+// console.log(JSON.stringify(chapterTree, null, 2));
+
+
+
+
+
+
+
+
+
+
+
+// =================================================
+
+
+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;
+}
+
+// 示例数据
+const data = [
+    {"id":103282,"name":"封面","level":0,"order_id":"b5c723069afad903d9410a1b87ee430d","parent_id":null,"path":"./base_files/1a7c3ccc61a0c00998d588971ee4e567/Text/b5c723069afad903d9410a1b87ee430d.html"},
+    {"id":103283,"name":"主目录","level":0,"order_id":"d2ae72f833f4622b4dad008d962dca63","parent_id":null,"path":"./base_files/1a7c3ccc61a0c00998d588971ee4e567/Text/d2ae72f833f4622b4dad008d962dca63.html"},
+    {"id":103284,"name":"史记","level":0,"order_id":"7c95352994901b619b32372054b2ad9d","parent_id":null,"path":"./base_files/1a7c3ccc61a0c00998d588971ee4e567/Text/7c95352994901b619b32372054b2ad9d.html"},
+    {"id":103285,"name":"五帝本纪第一","level":1,"order_id":"8f528e29556ad5246cf6c972c381bbed","parent_id":103284,"path":"./base_files/1a7c3ccc61a0c00998d588971ee4e567/Text/8f528e29556ad5246cf6c972c381bbed.html"},
+    {"id":103286,"name":"夏本纪第二","level":1,"order_id":"601b466b63d71a0bf787528254581282","parent_id":103284,"path":"./base_files/1a7c3ccc61a0c00998d588971ee4e567/Text/601b466b63d71a0bf787528254581282.html"},
+    {"id":103287,"name":"殷本纪第三","level":1,"order_id":"798fc04674f31ceae6c4f4d5b3b9f6db","parent_id":103284,"path":"./base_files/1a7c3ccc61a0c00998d588971ee4e567/Text/798fc04674f31ceae6c4f4d5b3b9f6db.html"},
+    {"id":103288,"name":"周本纪第四","level":1,"order_id":"8fa943845104b4891a1bf1191c2e2414","parent_id":103284,"path":"./base_files/1a7c3ccc61a0c00998d588971ee4e567/Text/8fa943845104b4891a1bf1191c2e2414.html"},
+    {"id":103289,"name":"秦本纪第五","level":1,"order_id":"f0371fc303cd35ffe95c4201f7073a91","parent_id":103284,"path":"./base_files/1a7c3ccc61a0c00998d588971ee4e567/Text/f0371fc303cd35ffe95c4201f7073a91.html"},
+    {"id":103290,"name":"秦始皇本纪第六","level":1,"order_id":"9af7c7db6c4ce70d6c37b47dffbec21f","parent_id":103284,"path":"./base_files/1a7c3ccc61a0c00998d588971ee4e567/Text/9af7c7db6c4ce70d6c37b47dffbec21f.html"},
+    {"id":103291,"name":"项羽本纪第七","level":1,"order_id":"297ee4d17393155dc0e989a78136984a","parent_id":103284,"path":"./base_files/1a7c3ccc61a0c00998d588971ee4e567/Text/297ee4d17393155dc0e989a78136984a.html"},
+];
+
+// 构建树并输出
+const tree = buildTree(data);
+console.log(JSON.stringify(tree, null, 2));