yuque-dl 1.0.14 → 1.0.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/cli.js +1 -1
- package/dist/cjs/index-d6956d87.js +1 -0
- package/dist/cjs/index.js +1 -1
- package/dist/es/cli.js +1 -1
- package/dist/es/index-36090493.js +1 -0
- package/dist/es/index.js +1 -1
- package/package.json +4 -2
- package/types/ProgressBar.d.ts +5 -2
- package/types/index.d.ts +2 -1
- package/dist/cjs/index-3cb44b7c.js +0 -1
- package/dist/es/index-65916ed3.js +0 -1
package/dist/cjs/cli.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var e=require("node:fs"),r=require("cac"),
|
|
1
|
+
"use strict";var e=require("node:fs"),r=require("cac"),i=require("./index-d6956d87.js");require("node:fs/promises"),require("axios"),require("pull-md-img"),require("ora"),require("cli-progress"),require("log4js"),require("rand-user-agent");const n=r.cac("yuque-dl"),{version:o}=JSON.parse(e.readFileSync(new URL("../../package.json","undefined"==typeof document?require("url").pathToFileURL(__filename).href:document.currentScript&&document.currentScript.src||new URL("cli.js",document.baseURI).href)).toString());n.command("<url>","语雀知识库url").option("-d, --distDir <dir>","下载的目录 eg: -d download",{default:"download"}).option("-i, --ignoreImg","忽略图片不下载",{default:!1}).action((async(e,r)=>{try{await i.main(e,r)}catch(e){i.logger.error(e.message||"unknown exception")}})),n.help(),n.version(o);try{n.parse()}catch(e){i.logger.error(e.message||"unknown exception"),process.exit(1)}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";var e=require("node:fs/promises"),t=require("axios"),r=require("pull-md-img"),o=require("ora"),i=require("cli-progress"),s=require("log4js"),n=require("rand-user-agent");var a=(s.configure({appenders:{cheese:{type:"console",layout:{type:"pattern",pattern:"%[%c [%p]:%] %m%n"}}},categories:{default:{appenders:["cheese"],level:"trace"}}}),s.getLogger("yuque-dl"));class l{constructor(e,t){this.bookPath="",this.progressFilePath="",this.progressInfo=[],this.curr=0,this.total=0,this.isDownloadInterrupted=!1,this.bar=null,this.completePromise=null,this.bookPath=e,this.progressFilePath=`${e}/progress.json`,this.total=t}async init(){this.progressInfo=await this.getProgress(),this.curr=this.progressInfo.length,this.curr!==this.total&&(this.curr>0&&this.curr!==this.total&&(this.isDownloadInterrupted=!0,a.info("根据上次数据继续断点下载")),this.bar=new i.SingleBar({format:"Download [{bar}] {percentage}% | {value}/{total}"},i.Presets.legacy),this.bar.start(this.total,this.curr))}async getProgress(){let t=[];try{const r=await e.readFile(this.progressFilePath,{encoding:"utf8"});t=JSON.parse(r)}catch(r){r&&"ENOENT"===r.code&&await e.writeFile(this.progressFilePath,JSON.stringify(t),{encoding:"utf8"})}return t}async updateProgress(t,r){this.curr=this.curr+1,r&&(this.progressInfo.push(t),await e.writeFile(this.progressFilePath,JSON.stringify(this.progressInfo),{encoding:"utf8"})),this.bar&&(this.bar.update(this.curr),this.curr>=this.total&&(this.bar.stop(),console.log("")))}pause(){this.bar&&this.bar.stop()}continue(){this.clearLine(2),this.bar?.start(this.total,this.curr)}clearLine(e){if(!(e<=0)){process.stderr.cursorTo(0);for(let t=0;t<e;t++)process.stderr.moveCursor(0,-1),process.stderr.clearLine(1)}}}class c{constructor(e){this.summaryInfo=e}async genFile(){const{bookName:t,bookDesc:r,bookPath:o,uuidMap:i}=this.summaryInfo;let s=`# ${t}\n\n`;r&&(s+=`> ${r}\n\n`);let n=s;const l=[];i.forEach((e=>{const t=e.toc,r=t.parent_uuid,o=this.findTree(l,r),i={text:t.title.replace(/[\\\/:\*\?"<>\|\n\r]/g,"_").replace(/\s/,""),id:t.uuid,level:1,type:""},s=t.type.toLocaleLowerCase();"title"===s||""!==t.child_uuid?(i.type="title","boolean"!=typeof o?(Array.isArray(o.children)||(o.children=[]),i.level=o.level+1,o.children.push(i)):(i.level=1,l.push(i))):(i.type="link",i.link="link"===s?e.toc.url:e.path,"boolean"!=typeof o?(Array.isArray(o.children)||(o.children=[]),i.level=o.level+1,o.children.push(i)):(i.level=1,l.push(i)))})),n+=this.genSummaryContent(l,"");try{await e.writeFile(`${o}/SUMMARY.md`,n)}catch(e){a.error("Generate Summary Error")}}genSummaryContent(e,t){for(let r=0;r<e.length;r++){const o=e[r];if("title"===o.type)t+=`\n${"".padStart(o.level+1,"#")} ${o.text}\n\n`;else if("link"===o.type){const e=o.link?o.link.replace(/\s/g,"%20"):o.link;t+=`- [${o.text}](${e})\n`}Array.isArray(o.children)&&(t+=this.genSummaryContent(o.children,""))}return t}findIdItem(e,t){if(e.id===t)return e;if(e.children){const r=this.findTree(e.children,t);if(r)return r}return!1}findTree(e,t){if(!t)return!1;for(let r=0;r<e.length;r++){const o=this.findIdItem(e[r],t);if(o)return o}return!1}}function u({browser:e="chrome",os:t="mac os",device:r="desktop"}){r=r.toLowerCase(),e=e.toLowerCase(),t=t.toLowerCase();let o=n(r,e,t);if("chrome"===e)for(;o.includes("Chrome-Lighthouse")||o.includes("Gener8")||o.includes("HeadlessChrome")||o.includes("SMTBot");)o=n(r,e,t);if("safari"===e)for(;o.includes("Applebot");)o=n(r,e,t);return o}function h(e){const r=/decodeURIComponent\(\"(.+)\"\)\);/m;return t.get(e,{headers:{"user-agent":u({browser:"chrome",device:"desktop"})}}).then((({data:e="",status:t})=>200===t?e:"")).then((e=>{const t=e.match(r)||"";if(!t[1])return{};const o=JSON.parse(decodeURIComponent(t[1]));if(!o.book)return{};return{bookId:o.book.id,bookSlug:o.book.slug,tocList:o.book.toc||[],bookName:o.book.name||"",bookDesc:o.book.description||""}}))}async function d(i,s){const{bookId:n,itemUrl:a,savePath:l,saveFilePath:c,uuid:h,articleUrl:d,articleTitle:p,ignoreImg:g}=i;let f=`https://www.yuque.com/api/docs/${a}`;const w={book_id:String(n),merge_dynamic_data:String(!1),mode:"markdown"},m=new URLSearchParams(w).toString();f=`${f}?${m}`;let $=(await t.get(f,{headers:{"user-agent":u({browser:"chrome",device:"desktop"})}}).then((({data:e,status:t})=>{if(200===t){const t=e;if(!t||!t.data||!t.data.sourcecode)throw new Error(`download article Error: ${f}`);return t}throw new Error(`download article Error: ${f} http status ${t}`)})).catch((e=>{throw new Error(`download article Error: ${f} ${e.message}`)}))).data.sourcecode;if(!g){s.pause(),console.log("");const e=o({text:`下载 "${p}" 的图片中...`});e.start();try{$=await r.run($,{dist:l,imgDir:`img/${h}`,isIgnoreConsole:!0})}catch(e){let t=`download article image Error: ${e.message}`;throw e.error&&e.url&&(t=`download article image Error ${e.url}: ${e.error?.message}`),new Error(t)}finally{e.stop(),s.continue()}}$=$.replace(/<br(\s?)\/>/gm,"\n"),p&&($=`# ${p}\n\x3c!--page header--\x3e\n\n${$}\n\n`),d&&($+=`\x3c!--page footer--\x3e\n- 原文: <${d}>`);try{return await e.writeFile(c,$),!0}catch(e){throw new Error(`download article Error ${d}: ${e.message}`)}}function p(e){if(!e)return"";return e.replace(/[\\\/:\*\?"<>\|\n\r]/g,"_").replace(/\s/,"")}exports.downloadArticle=d,exports.getKnowledgeBaseInfo=h,exports.logger=a,exports.main=async function(t,r){const{bookId:o,tocList:i,bookName:s,bookDesc:n,bookSlug:u}=await h(t);if(!o)throw new Error("No found book id");if(!i||0===i.length)throw new Error("No found toc list");const g=`${r.distDir}/${s?p(s):o}`;await e.mkdir(g,{recursive:!0});const f=i.length,w=new l(g,f);if(await w.init(),w.curr===f)return w.bar&&w.bar.stop(),void a.info(`√ 已完成: ${process.cwd()}/${g}`);const m=new Map;w.isDownloadInterrupted&&w.progressInfo.forEach((e=>{m.set(e.toc.uuid,e)}));const $=t.replace(new RegExp(`(.*?/${u}).*`),"$1");let b=0,y=0,k=0,I=[],v=[];for(let t=0;t<f;t++){const s=i[t];if("string"!=typeof s.type)continue;if(m.get(s.uuid))continue;const n=s.type.toLocaleLowerCase();if("title"===n||""!==s.child_uuid||"link"===n){let t=s,r=[],o=[];for(;t;)r.unshift(p(t.title)),o.unshift(t.uuid),t=m.get(t.parent_uuid)?m.get(t.parent_uuid).toc:void 0;const i={path:r.join("/"),pathTitleList:r,pathIdList:o,toc:s};"link"===n?(k+=1,v.push(i)):await e.mkdir(`${g}/${r.join("/")}`,{recursive:!0}),m.set(s.uuid,i),await w.updateProgress(i,"link"!==n)}else if(s.url){y+=1;let e={path:"",pathTitleList:[],pathIdList:[]};m.get(s.parent_uuid)&&(e=m.get(s.parent_uuid));const t=p(s.title),i=[...e.pathTitleList,`${t}.md`],n=[...e.pathIdList,s.uuid],a={path:i.join("/"),pathTitleList:i,pathIdList:n,toc:s};let l=!0;try{await d({bookId:o,itemUrl:s.url,savePath:`${g}/${e.path}`,saveFilePath:`${g}/${a.path}`,uuid:s.uuid,articleUrl:`${$}/${s.url}`,articleTitle:s.title,ignoreImg:r.ignoreImg},w)}catch(e){l=!1,b+=1,I.push({errItem:a,errMsg:e.message,err:e})}m.set(s.uuid,a),await w.updateProgress(a,l)}}if(k>0){a.warn("该知识库存在以下外链文章");for(let e=0;e<v.length;e++)a.warn(`———— ✕ ${v[e].path} ${v[e].toc.url}`)}if(b>0){a.error(`本次执行总数${y}篇,✕ 失败${b}篇`);for(let e=0;e<I.length;e++)a.error(`${I[e].errItem.path}`),a.error(`———— ✕ ${I[e].errMsg}`);a.error("o(╥﹏╥)o 由于网络波动或链接失效以上下载失败,可重新执行命令重试(不会影响已下载成功的数据)~~")}const L=new c({bookPath:g,bookName:s,bookDesc:n,uuidMap:m});await L.genFile();const P=process.cwd();a.info(`√ 生成目录 ${P}/${g}/SUMMARY.md`),w.curr!==f||a.info(`√ 已完成: ${P}/${g}`)};
|
package/dist/cjs/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";require("node:fs/promises"),require("axios"),require("pull-md-img");var e=require("./index-
|
|
1
|
+
"use strict";require("node:fs/promises"),require("axios"),require("pull-md-img"),require("ora");var e=require("./index-d6956d87.js");require("cli-progress"),require("log4js"),require("rand-user-agent"),exports.downloadArticle=e.downloadArticle,exports.getKnowledgeBaseInfo=e.getKnowledgeBaseInfo,exports.main=e.main;
|
package/dist/es/cli.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{readFileSync as o}from"node:fs";import{cac as r}from"cac";import{m as e,l as i}from"./index-
|
|
1
|
+
import{readFileSync as o}from"node:fs";import{cac as r}from"cac";import{m as e,l as i}from"./index-36090493.js";import"node:fs/promises";import"axios";import"pull-md-img";import"ora";import"cli-progress";import"log4js";import"rand-user-agent";const t=r("yuque-dl"),{version:n}=JSON.parse(o(new URL("../../package.json",import.meta.url)).toString());t.command("<url>","语雀知识库url").option("-d, --distDir <dir>","下载的目录 eg: -d download",{default:"download"}).option("-i, --ignoreImg","忽略图片不下载",{default:!1}).action((async(o,r)=>{try{await e(o,r)}catch(o){i.error(o.message||"unknown exception")}})),t.help(),t.version(n);try{t.parse()}catch(o){i.error(o.message||"unknown exception"),process.exit(1)}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import t,{writeFile as e,mkdir as r}from"node:fs/promises";import o from"axios";import i from"pull-md-img";import s from"ora";import a from"cli-progress";import n from"log4js";import l from"rand-user-agent";var c=(n.configure({appenders:{cheese:{type:"console",layout:{type:"pattern",pattern:"%[%c [%p]:%] %m%n"}}},categories:{default:{appenders:["cheese"],level:"trace"}}}),n.getLogger("yuque-dl"));class u{constructor(t,e){this.bookPath="",this.progressFilePath="",this.progressInfo=[],this.curr=0,this.total=0,this.isDownloadInterrupted=!1,this.bar=null,this.completePromise=null,this.bookPath=t,this.progressFilePath=`${t}/progress.json`,this.total=e}async init(){this.progressInfo=await this.getProgress(),this.curr=this.progressInfo.length,this.curr!==this.total&&(this.curr>0&&this.curr!==this.total&&(this.isDownloadInterrupted=!0,c.info("根据上次数据继续断点下载")),this.bar=new a.SingleBar({format:"Download [{bar}] {percentage}% | {value}/{total}"},a.Presets.legacy),this.bar.start(this.total,this.curr))}async getProgress(){let e=[];try{const r=await t.readFile(this.progressFilePath,{encoding:"utf8"});e=JSON.parse(r)}catch(r){r&&"ENOENT"===r.code&&await t.writeFile(this.progressFilePath,JSON.stringify(e),{encoding:"utf8"})}return e}async updateProgress(e,r){this.curr=this.curr+1,r&&(this.progressInfo.push(e),await t.writeFile(this.progressFilePath,JSON.stringify(this.progressInfo),{encoding:"utf8"})),this.bar&&(this.bar.update(this.curr),this.curr>=this.total&&(this.bar.stop(),console.log("")))}pause(){this.bar&&this.bar.stop()}continue(){this.clearLine(2),this.bar?.start(this.total,this.curr)}clearLine(t){if(!(t<=0)){process.stderr.cursorTo(0);for(let e=0;e<t;e++)process.stderr.moveCursor(0,-1),process.stderr.clearLine(1)}}}class h{constructor(t){this.summaryInfo=t}async genFile(){const{bookName:e,bookDesc:r,bookPath:o,uuidMap:i}=this.summaryInfo;let s=`# ${e}\n\n`;r&&(s+=`> ${r}\n\n`);let a=s;const n=[];i.forEach((t=>{const e=t.toc,r=e.parent_uuid,o=this.findTree(n,r),i={text:e.title.replace(/[\\\/:\*\?"<>\|\n\r]/g,"_").replace(/\s/,""),id:e.uuid,level:1,type:""},s=e.type.toLocaleLowerCase();"title"===s||""!==e.child_uuid?(i.type="title","boolean"!=typeof o?(Array.isArray(o.children)||(o.children=[]),i.level=o.level+1,o.children.push(i)):(i.level=1,n.push(i))):(i.type="link",i.link="link"===s?t.toc.url:t.path,"boolean"!=typeof o?(Array.isArray(o.children)||(o.children=[]),i.level=o.level+1,o.children.push(i)):(i.level=1,n.push(i)))})),a+=this.genSummaryContent(n,"");try{await t.writeFile(`${o}/SUMMARY.md`,a)}catch(t){c.error("Generate Summary Error")}}genSummaryContent(t,e){for(let r=0;r<t.length;r++){const o=t[r];if("title"===o.type)e+=`\n${"".padStart(o.level+1,"#")} ${o.text}\n\n`;else if("link"===o.type){const t=o.link?o.link.replace(/\s/g,"%20"):o.link;e+=`- [${o.text}](${t})\n`}Array.isArray(o.children)&&(e+=this.genSummaryContent(o.children,""))}return e}findIdItem(t,e){if(t.id===e)return t;if(t.children){const r=this.findTree(t.children,e);if(r)return r}return!1}findTree(t,e){if(!e)return!1;for(let r=0;r<t.length;r++){const o=this.findIdItem(t[r],e);if(o)return o}return!1}}function d({browser:t="chrome",os:e="mac os",device:r="desktop"}){r=r.toLowerCase(),t=t.toLowerCase(),e=e.toLowerCase();let o=l(r,t,e);if("chrome"===t)for(;o.includes("Chrome-Lighthouse")||o.includes("Gener8")||o.includes("HeadlessChrome")||o.includes("SMTBot");)o=l(r,t,e);if("safari"===t)for(;o.includes("Applebot");)o=l(r,t,e);return o}function p(t){const e=/decodeURIComponent\(\"(.+)\"\)\);/m;return o.get(t,{headers:{"user-agent":d({browser:"chrome",device:"desktop"})}}).then((({data:t="",status:e})=>200===e?t:"")).then((t=>{const r=t.match(e)||"";if(!r[1])return{};const o=JSON.parse(decodeURIComponent(r[1]));if(!o.book)return{};return{bookId:o.book.id,bookSlug:o.book.slug,tocList:o.book.toc||[],bookName:o.book.name||"",bookDesc:o.book.description||""}}))}async function g(t,r){const{bookId:a,itemUrl:n,savePath:l,saveFilePath:c,uuid:u,articleUrl:h,articleTitle:p,ignoreImg:g}=t;let f=`https://www.yuque.com/api/docs/${n}`;const m={book_id:String(a),merge_dynamic_data:String(!1),mode:"markdown"},w=new URLSearchParams(m).toString();f=`${f}?${w}`;let $=(await o.get(f,{headers:{"user-agent":d({browser:"chrome",device:"desktop"})}}).then((({data:t,status:e})=>{if(200===e){const e=t;if(!e||!e.data||!e.data.sourcecode)throw new Error(`download article Error: ${f}`);return e}throw new Error(`download article Error: ${f} http status ${e}`)})).catch((t=>{throw new Error(`download article Error: ${f} ${t.message}`)}))).data.sourcecode;if(!g){r.pause(),console.log("");const t=s({text:`下载 "${p}" 的图片中...`});t.start();try{$=await i.run($,{dist:l,imgDir:`img/${u}`,isIgnoreConsole:!0})}catch(t){let e=`download article image Error: ${t.message}`;throw t.error&&t.url&&(e=`download article image Error ${t.url}: ${t.error?.message}`),new Error(e)}finally{t.stop(),r.continue()}}$=$.replace(/<br(\s?)\/>/gm,"\n"),p&&($=`# ${p}\n\x3c!--page header--\x3e\n\n${$}\n\n`),h&&($+=`\x3c!--page footer--\x3e\n- 原文: <${h}>`);try{return await e(c,$),!0}catch(t){throw new Error(`download article Error ${h}: ${t.message}`)}}function f(t){if(!t)return"";return t.replace(/[\\\/:\*\?"<>\|\n\r]/g,"_").replace(/\s/,"")}async function m(t,e){const{bookId:o,tocList:i,bookName:s,bookDesc:a,bookSlug:n}=await p(t);if(!o)throw new Error("No found book id");if(!i||0===i.length)throw new Error("No found toc list");const l=`${e.distDir}/${s?f(s):o}`;await r(l,{recursive:!0});const d=i.length,m=new u(l,d);if(await m.init(),m.curr===d)return m.bar&&m.bar.stop(),void c.info(`√ 已完成: ${process.cwd()}/${l}`);const w=new Map;m.isDownloadInterrupted&&m.progressInfo.forEach((t=>{w.set(t.toc.uuid,t)}));const $=t.replace(new RegExp(`(.*?/${n}).*`),"$1");let b=0,y=0,k=0,I=[],v=[];for(let t=0;t<d;t++){const s=i[t];if("string"!=typeof s.type)continue;if(w.get(s.uuid))continue;const a=s.type.toLocaleLowerCase();if("title"===a||""!==s.child_uuid||"link"===a){let t=s,e=[],o=[];for(;t;)e.unshift(f(t.title)),o.unshift(t.uuid),t=w.get(t.parent_uuid)?w.get(t.parent_uuid).toc:void 0;const i={path:e.join("/"),pathTitleList:e,pathIdList:o,toc:s};"link"===a?(k+=1,v.push(i)):await r(`${l}/${e.join("/")}`,{recursive:!0}),w.set(s.uuid,i),await m.updateProgress(i,"link"!==a)}else if(s.url){y+=1;let t={path:"",pathTitleList:[],pathIdList:[]};w.get(s.parent_uuid)&&(t=w.get(s.parent_uuid));const r=f(s.title),i=[...t.pathTitleList,`${r}.md`],a=[...t.pathIdList,s.uuid],n={path:i.join("/"),pathTitleList:i,pathIdList:a,toc:s};let c=!0;try{await g({bookId:o,itemUrl:s.url,savePath:`${l}/${t.path}`,saveFilePath:`${l}/${n.path}`,uuid:s.uuid,articleUrl:`${$}/${s.url}`,articleTitle:s.title,ignoreImg:e.ignoreImg},m)}catch(t){c=!1,b+=1,I.push({errItem:n,errMsg:t.message,err:t})}w.set(s.uuid,n),await m.updateProgress(n,c)}}if(k>0){c.warn("该知识库存在以下外链文章");for(let t=0;t<v.length;t++)c.warn(`———— ✕ ${v[t].path} ${v[t].toc.url}`)}if(b>0){c.error(`本次执行总数${y}篇,✕ 失败${b}篇`);for(let t=0;t<I.length;t++)c.error(`${I[t].errItem.path}`),c.error(`———— ✕ ${I[t].errMsg}`);c.error("o(╥﹏╥)o 由于网络波动或链接失效以上下载失败,可重新执行命令重试(不会影响已下载成功的数据)~~")}const L=new h({bookPath:l,bookName:s,bookDesc:a,uuidMap:w});await L.genFile();const P=process.cwd();c.info(`√ 生成目录 ${P}/${l}/SUMMARY.md`),m.curr!==d||c.info(`√ 已完成: ${P}/${l}`)}export{g as d,p as g,c as l,m};
|
package/dist/es/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import"node:fs/promises";import"axios";import"pull-md-img";export{d as downloadArticle,g as getKnowledgeBaseInfo,m as main}from"./index-
|
|
1
|
+
import"node:fs/promises";import"axios";import"pull-md-img";import"ora";export{d as downloadArticle,g as getKnowledgeBaseInfo,m as main}from"./index-36090493.js";import"cli-progress";import"log4js";import"rand-user-agent";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "yuque-dl",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.15",
|
|
4
4
|
"description": "yuque 知识库下载",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"@rollup/plugin-terser": "^0.4.3",
|
|
36
36
|
"@rollup/plugin-typescript": "^11.1.2",
|
|
37
|
+
"@types/cli-progress": "^3.11.0",
|
|
37
38
|
"@types/node": "^20.5.0",
|
|
38
39
|
"@types/progress": "^2.0.5",
|
|
39
40
|
"@typescript-eslint/eslint-plugin": "^6.3.0",
|
|
@@ -56,8 +57,9 @@
|
|
|
56
57
|
"dependencies": {
|
|
57
58
|
"axios": "^1.4.0",
|
|
58
59
|
"cac": "^6.7.14",
|
|
60
|
+
"cli-progress": "^3.12.0",
|
|
59
61
|
"log4js": "^6.9.1",
|
|
60
|
-
"
|
|
62
|
+
"ora": "^7.0.1",
|
|
61
63
|
"pull-md-img": "^0.0.22",
|
|
62
64
|
"rand-user-agent": "1.0.109"
|
|
63
65
|
},
|
package/types/ProgressBar.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import cliProgress from 'cli-progress';
|
|
2
2
|
import type { KnowledgeBase } from './types/KnowledgeBaseResponse';
|
|
3
3
|
export interface IProgressItem {
|
|
4
4
|
path: string;
|
|
@@ -14,10 +14,13 @@ export default class ProgressBar {
|
|
|
14
14
|
curr: number;
|
|
15
15
|
total: number;
|
|
16
16
|
isDownloadInterrupted: boolean;
|
|
17
|
-
bar:
|
|
17
|
+
bar: cliProgress.SingleBar | null;
|
|
18
18
|
completePromise: Promise<void> | null;
|
|
19
19
|
constructor(bookPath: string, total: number);
|
|
20
20
|
init(): Promise<void>;
|
|
21
21
|
getProgress(): Promise<IProgress>;
|
|
22
22
|
updateProgress(progressItem: IProgressItem, isSuccess: boolean): Promise<void>;
|
|
23
|
+
pause(): void;
|
|
24
|
+
continue(): void;
|
|
25
|
+
clearLine(line: number): void;
|
|
23
26
|
}
|
package/types/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import ProgressBar from './ProgressBar';
|
|
1
2
|
import type { KnowledgeBase } from './types/KnowledgeBaseResponse';
|
|
2
3
|
import type { IOptions } from './cli';
|
|
3
4
|
interface IKnowledgeBaseInfo {
|
|
@@ -20,6 +21,6 @@ interface IDownloadArticleParams {
|
|
|
20
21
|
/** 获取知识库数据信息 */
|
|
21
22
|
declare function getKnowledgeBaseInfo(url: string): Promise<IKnowledgeBaseInfo>;
|
|
22
23
|
/** 下载单篇文章 */
|
|
23
|
-
declare function downloadArticle(params: IDownloadArticleParams): Promise<boolean>;
|
|
24
|
+
declare function downloadArticle(params: IDownloadArticleParams, progressBar: ProgressBar): Promise<boolean>;
|
|
24
25
|
declare function main(url: string, options: IOptions): Promise<void>;
|
|
25
26
|
export { getKnowledgeBaseInfo, downloadArticle, main };
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"use strict";var e=require("node:fs/promises"),t=require("axios"),r=require("pull-md-img"),o=require("progress"),i=require("log4js"),s=require("rand-user-agent");var n=(i.configure({appenders:{cheese:{type:"console",layout:{type:"pattern",pattern:"%[%c [%p]:%] %m%n"}}},categories:{default:{appenders:["cheese"],level:"trace"}}}),i.getLogger("yuque-dl"));class a{constructor(e,t){this.bookPath="",this.progressFilePath="",this.progressInfo=[],this.curr=0,this.total=0,this.isDownloadInterrupted=!1,this.bar=null,this.completePromise=null,this.bookPath=e,this.progressFilePath=`${e}/progress.json`,this.total=t}async init(){let e;this.progressInfo=await this.getProgress(),this.curr=this.progressInfo.length,this.completePromise=new Promise((t=>{e=t})),this.curr>0&&this.curr!==this.total&&(this.isDownloadInterrupted=!0,n.info("断点下载")),this.bar=new o("downloading [:bar] :rate/bps :percent :etas",{width:80,total:this.total,curr:0,callback(){e()}}),this.curr>0&&this.bar.tick(this.curr)}async getProgress(){let t=[];try{const r=await e.readFile(this.progressFilePath,{encoding:"utf8"});t=JSON.parse(r)}catch(r){r&&"ENOENT"===r.code&&await e.writeFile(this.progressFilePath,JSON.stringify(t),{encoding:"utf8"})}return t}async updateProgress(t,r){r&&(this.progressInfo.push(t),await e.writeFile(this.progressFilePath,JSON.stringify(this.progressInfo),{encoding:"utf8"})),this.curr=this.curr+1,this.bar&&this.bar?.tick()}}class l{constructor(e){this.summaryInfo=e}async genFile(){const{bookName:t,bookDesc:r,bookPath:o,uuidMap:i}=this.summaryInfo;let s=`# ${t}\n\n`;r&&(s+=`> ${r}\n\n`);let a=s;const l=[];i.forEach((e=>{const t=e.toc,r=t.parent_uuid,o=this.findTree(l,r),i={text:t.title.replace(/[\\\/:\*\?"<>\|\n\r]/g,"_").replace(/\s/,""),id:t.uuid,level:1,type:""};"title"===t.type.toLocaleLowerCase()||""!==t.child_uuid?(i.type="title","boolean"!=typeof o?(Array.isArray(o.children)||(o.children=[]),i.level=o.level+1,o.children.push(i)):(i.level=1,l.push(i))):(i.type="link",i.link=e.path,"boolean"!=typeof o?(Array.isArray(o.children)||(o.children=[]),i.level=o.level+1,o.children.push(i)):(i.level=1,l.push(i)))})),a+=this.genSummaryContent(l,"");try{await e.writeFile(`${o}/SUMMARY.md`,a)}catch(e){n.error("Generate Summary Error")}}genSummaryContent(e,t){for(let r=0;r<e.length;r++){const o=e[r];if("title"===o.type)t+=`\n${"".padStart(o.level+1,"#")} ${o.text}\n\n`;else if("link"===o.type){const e=o.link?o.link.replace(/\s/g,"%20"):o.link;t+=`- [${o.text}](${e})\n`}Array.isArray(o.children)&&(t+=this.genSummaryContent(o.children,""))}return t}findIdItem(e,t){if(e.id===t)return e;if(e.children){const r=this.findTree(e.children,t);if(r)return r}return!1}findTree(e,t){if(!t)return!1;for(let r=0;r<e.length;r++){const o=this.findIdItem(e[r],t);if(o)return o}return!1}}function c({browser:e="chrome",os:t="mac os",device:r="desktop"}){r=r.toLowerCase(),e=e.toLowerCase(),t=t.toLowerCase();let o=s(r,e,t);if("chrome"===e)for(;o.includes("Chrome-Lighthouse")||o.includes("Gener8")||o.includes("HeadlessChrome")||o.includes("SMTBot");)o=s(r,e,t);if("safari"===e)for(;o.includes("Applebot");)o=s(r,e,t);return o}function u(e){const r=/decodeURIComponent\(\"(.+)\"\)\);/m;return t.get(e,{headers:{"user-agent":c({browser:"chrome",device:"desktop"})}}).then((({data:e="",status:t})=>200===t?e:"")).then((e=>{const t=e.match(r)||"";if(!t[1])return{};const o=JSON.parse(decodeURIComponent(t[1]));if(!o.book)return{};return{bookId:o.book.id,bookSlug:o.book.slug,tocList:o.book.toc||[],bookName:o.book.name||"",bookDesc:o.book.description||""}}))}async function d(o){const{bookId:i,itemUrl:s,savePath:n,saveFilePath:a,uuid:l,articleUrl:u,articleTitle:d,ignoreImg:h}=o;let p=`https://www.yuque.com/api/docs/${s}`;const g={book_id:String(i),merge_dynamic_data:String(!1),mode:"markdown"},f=new URLSearchParams(g).toString();p=`${p}?${f}`;let m=(await t.get(p,{headers:{"user-agent":c({browser:"chrome",device:"desktop"})}}).then((({data:e,status:t})=>{if(200===t){const t=e;if(!t||!t.data||!t.data.sourcecode)throw new Error(`download article Error: ${p}`);return t}throw new Error(`download article Error: ${p} http status ${t}`)})).catch((e=>{throw new Error(`download article Error: ${p} ${e.message}`)}))).data.sourcecode;if(!h)try{m=await r.run(m,{dist:n,imgDir:`img/${l}`,isIgnoreConsole:!0})}catch(e){let t=`download article image Error: ${e.message}`;throw e.error&&e.url&&(t=`download article image Error ${e.url}: ${e.error?.message}`),new Error(t)}m=m.replace(/<br(\s?)\/>/gm,"\n"),d&&(m=`# ${d}\n\x3c!--page header--\x3e\n\n${m}\n\n`),u&&(m+=`\x3c!--page footer--\x3e\n- 原文: <${u}>`);try{return await e.writeFile(a,m),!0}catch(e){throw new Error(`download article Error ${u}: ${e.message}`)}}function h(e){if(!e)return"";return e.replace(/[\\\/:\*\?"<>\|\n\r]/g,"_").replace(/\s/,"")}exports.downloadArticle=d,exports.getKnowledgeBaseInfo=u,exports.logger=n,exports.main=async function(t,r){const{bookId:o,tocList:i,bookName:s,bookDesc:c,bookSlug:p}=await u(t);if(!o)throw new Error("No found book id");if(!i||0===i.length)throw new Error("No found toc list");const g=`${r.distDir}/${s?h(s):o}`;await e.mkdir(g,{recursive:!0});const f=i.length,m=new a(g,f);if(await m.init(),m.curr===f)return void n.info(`√ 已完成: ${process.cwd()}/${g}`);const w=new Map;m.isDownloadInterrupted&&m.progressInfo.forEach((e=>{w.set(e.toc.uuid,e)}));const $=t.replace(new RegExp(`(.*?/${p}).*`),"$1");let y=0,b=0,k=[];for(let t=0;t<f;t++){const s=i[t];if("string"!=typeof s.type)continue;if(w.get(s.uuid))continue;if("title"===s.type.toLocaleLowerCase()||""!==s.child_uuid){let t=s,r=[],o=[];for(;t;)r.unshift(h(t.title)),o.unshift(t.uuid),t=w.get(t.parent_uuid)?w.get(t.parent_uuid).toc:void 0;const i={path:r.join("/"),pathTitleList:r,pathIdList:o,toc:s};await e.mkdir(`${g}/${r.join("/")}`,{recursive:!0}),w.set(s.uuid,i),m.updateProgress(i,!0)}else if(s.url){b+=1;let e={path:"",pathTitleList:[],pathIdList:[]};w.get(s.parent_uuid)&&(e=w.get(s.parent_uuid));const t=h(s.title),i=[...e.pathTitleList,`${t}.md`],n=[...e.pathIdList,s.uuid],a={path:i.join("/"),pathTitleList:i,pathIdList:n,toc:s};let l=!0;try{await d({bookId:o,itemUrl:s.url,savePath:`${g}/${e.path}`,saveFilePath:`${g}/${a.path}`,uuid:s.uuid,articleUrl:`${$}/${s.url}`,articleTitle:s.title,ignoreImg:r.ignoreImg})}catch(e){l=!1,y+=1,k.push(a),m.bar?.interrupt(`✕ ${e.message}`)}w.set(s.uuid,a),m.updateProgress(a,l)}}if(await(m.completePromise?.finally((()=>console.log("")))),y>0){n.info(`本次执行总数${b}篇,✕ 失败${y}篇`);for(let e=0;e<k.length;e++)n.error(`✕ ${k[e].path}`);n.info("o(╥﹏╥)o 由于网络波动以上下载失败,请重新执行命令")}const I=new l({bookPath:g,bookName:s,bookDesc:c,uuidMap:w});await I.genFile(),n.info("√ 生成目录 SUMMARY.md"),m.curr!==f||n.info(`√ 已完成: ${process.cwd()}/${g}`)};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import e,{writeFile as t,mkdir as r}from"node:fs/promises";import o from"axios";import i from"pull-md-img";import s from"progress";import n from"log4js";import a from"rand-user-agent";var l=(n.configure({appenders:{cheese:{type:"console",layout:{type:"pattern",pattern:"%[%c [%p]:%] %m%n"}}},categories:{default:{appenders:["cheese"],level:"trace"}}}),n.getLogger("yuque-dl"));class c{constructor(e,t){this.bookPath="",this.progressFilePath="",this.progressInfo=[],this.curr=0,this.total=0,this.isDownloadInterrupted=!1,this.bar=null,this.completePromise=null,this.bookPath=e,this.progressFilePath=`${e}/progress.json`,this.total=t}async init(){let e;this.progressInfo=await this.getProgress(),this.curr=this.progressInfo.length,this.completePromise=new Promise((t=>{e=t})),this.curr>0&&this.curr!==this.total&&(this.isDownloadInterrupted=!0,l.info("断点下载")),this.bar=new s("downloading [:bar] :rate/bps :percent :etas",{width:80,total:this.total,curr:0,callback(){e()}}),this.curr>0&&this.bar.tick(this.curr)}async getProgress(){let t=[];try{const r=await e.readFile(this.progressFilePath,{encoding:"utf8"});t=JSON.parse(r)}catch(r){r&&"ENOENT"===r.code&&await e.writeFile(this.progressFilePath,JSON.stringify(t),{encoding:"utf8"})}return t}async updateProgress(t,r){r&&(this.progressInfo.push(t),await e.writeFile(this.progressFilePath,JSON.stringify(this.progressInfo),{encoding:"utf8"})),this.curr=this.curr+1,this.bar&&this.bar?.tick()}}class u{constructor(e){this.summaryInfo=e}async genFile(){const{bookName:t,bookDesc:r,bookPath:o,uuidMap:i}=this.summaryInfo;let s=`# ${t}\n\n`;r&&(s+=`> ${r}\n\n`);let n=s;const a=[];i.forEach((e=>{const t=e.toc,r=t.parent_uuid,o=this.findTree(a,r),i={text:t.title.replace(/[\\\/:\*\?"<>\|\n\r]/g,"_").replace(/\s/,""),id:t.uuid,level:1,type:""};"title"===t.type.toLocaleLowerCase()||""!==t.child_uuid?(i.type="title","boolean"!=typeof o?(Array.isArray(o.children)||(o.children=[]),i.level=o.level+1,o.children.push(i)):(i.level=1,a.push(i))):(i.type="link",i.link=e.path,"boolean"!=typeof o?(Array.isArray(o.children)||(o.children=[]),i.level=o.level+1,o.children.push(i)):(i.level=1,a.push(i)))})),n+=this.genSummaryContent(a,"");try{await e.writeFile(`${o}/SUMMARY.md`,n)}catch(e){l.error("Generate Summary Error")}}genSummaryContent(e,t){for(let r=0;r<e.length;r++){const o=e[r];if("title"===o.type)t+=`\n${"".padStart(o.level+1,"#")} ${o.text}\n\n`;else if("link"===o.type){const e=o.link?o.link.replace(/\s/g,"%20"):o.link;t+=`- [${o.text}](${e})\n`}Array.isArray(o.children)&&(t+=this.genSummaryContent(o.children,""))}return t}findIdItem(e,t){if(e.id===t)return e;if(e.children){const r=this.findTree(e.children,t);if(r)return r}return!1}findTree(e,t){if(!t)return!1;for(let r=0;r<e.length;r++){const o=this.findIdItem(e[r],t);if(o)return o}return!1}}function d({browser:e="chrome",os:t="mac os",device:r="desktop"}){r=r.toLowerCase(),e=e.toLowerCase(),t=t.toLowerCase();let o=a(r,e,t);if("chrome"===e)for(;o.includes("Chrome-Lighthouse")||o.includes("Gener8")||o.includes("HeadlessChrome")||o.includes("SMTBot");)o=a(r,e,t);if("safari"===e)for(;o.includes("Applebot");)o=a(r,e,t);return o}function h(e){const t=/decodeURIComponent\(\"(.+)\"\)\);/m;return o.get(e,{headers:{"user-agent":d({browser:"chrome",device:"desktop"})}}).then((({data:e="",status:t})=>200===t?e:"")).then((e=>{const r=e.match(t)||"";if(!r[1])return{};const o=JSON.parse(decodeURIComponent(r[1]));if(!o.book)return{};return{bookId:o.book.id,bookSlug:o.book.slug,tocList:o.book.toc||[],bookName:o.book.name||"",bookDesc:o.book.description||""}}))}async function p(e){const{bookId:r,itemUrl:s,savePath:n,saveFilePath:a,uuid:l,articleUrl:c,articleTitle:u,ignoreImg:h}=e;let p=`https://www.yuque.com/api/docs/${s}`;const g={book_id:String(r),merge_dynamic_data:String(!1),mode:"markdown"},f=new URLSearchParams(g).toString();p=`${p}?${f}`;let m=(await o.get(p,{headers:{"user-agent":d({browser:"chrome",device:"desktop"})}}).then((({data:e,status:t})=>{if(200===t){const t=e;if(!t||!t.data||!t.data.sourcecode)throw new Error(`download article Error: ${p}`);return t}throw new Error(`download article Error: ${p} http status ${t}`)})).catch((e=>{throw new Error(`download article Error: ${p} ${e.message}`)}))).data.sourcecode;if(!h)try{m=await i.run(m,{dist:n,imgDir:`img/${l}`,isIgnoreConsole:!0})}catch(e){let t=`download article image Error: ${e.message}`;throw e.error&&e.url&&(t=`download article image Error ${e.url}: ${e.error?.message}`),new Error(t)}m=m.replace(/<br(\s?)\/>/gm,"\n"),u&&(m=`# ${u}\n\x3c!--page header--\x3e\n\n${m}\n\n`),c&&(m+=`\x3c!--page footer--\x3e\n- 原文: <${c}>`);try{return await t(a,m),!0}catch(e){throw new Error(`download article Error ${c}: ${e.message}`)}}function g(e){if(!e)return"";return e.replace(/[\\\/:\*\?"<>\|\n\r]/g,"_").replace(/\s/,"")}async function f(e,t){const{bookId:o,tocList:i,bookName:s,bookDesc:n,bookSlug:a}=await h(e);if(!o)throw new Error("No found book id");if(!i||0===i.length)throw new Error("No found toc list");const d=`${t.distDir}/${s?g(s):o}`;await r(d,{recursive:!0});const f=i.length,m=new c(d,f);if(await m.init(),m.curr===f)return void l.info(`√ 已完成: ${process.cwd()}/${d}`);const w=new Map;m.isDownloadInterrupted&&m.progressInfo.forEach((e=>{w.set(e.toc.uuid,e)}));const $=e.replace(new RegExp(`(.*?/${a}).*`),"$1");let y=0,b=0,k=[];for(let e=0;e<f;e++){const s=i[e];if("string"!=typeof s.type)continue;if(w.get(s.uuid))continue;if("title"===s.type.toLocaleLowerCase()||""!==s.child_uuid){let e=s,t=[],o=[];for(;e;)t.unshift(g(e.title)),o.unshift(e.uuid),e=w.get(e.parent_uuid)?w.get(e.parent_uuid).toc:void 0;const i={path:t.join("/"),pathTitleList:t,pathIdList:o,toc:s};await r(`${d}/${t.join("/")}`,{recursive:!0}),w.set(s.uuid,i),m.updateProgress(i,!0)}else if(s.url){b+=1;let e={path:"",pathTitleList:[],pathIdList:[]};w.get(s.parent_uuid)&&(e=w.get(s.parent_uuid));const r=g(s.title),i=[...e.pathTitleList,`${r}.md`],n=[...e.pathIdList,s.uuid],a={path:i.join("/"),pathTitleList:i,pathIdList:n,toc:s};let l=!0;try{await p({bookId:o,itemUrl:s.url,savePath:`${d}/${e.path}`,saveFilePath:`${d}/${a.path}`,uuid:s.uuid,articleUrl:`${$}/${s.url}`,articleTitle:s.title,ignoreImg:t.ignoreImg})}catch(e){l=!1,y+=1,k.push(a),m.bar?.interrupt(`✕ ${e.message}`)}w.set(s.uuid,a),m.updateProgress(a,l)}}if(await(m.completePromise?.finally((()=>console.log("")))),y>0){l.info(`本次执行总数${b}篇,✕ 失败${y}篇`);for(let e=0;e<k.length;e++)l.error(`✕ ${k[e].path}`);l.info("o(╥﹏╥)o 由于网络波动以上下载失败,请重新执行命令")}const I=new u({bookPath:d,bookName:s,bookDesc:n,uuidMap:w});await I.genFile(),l.info("√ 生成目录 SUMMARY.md"),m.curr!==f||l.info(`√ 已完成: ${process.cwd()}/${d}`)}export{p as d,h as g,l,f as m};
|