yuque-dl 1.0.18 → 1.0.20
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-8d8db60d.js +1 -0
- package/dist/cjs/index.js +1 -1
- package/dist/es/cli.js +1 -1
- package/dist/es/index-7b2fb7ac.js +1 -0
- package/dist/es/index.js +1 -1
- package/package.json +3 -3
- package/types/ProgressBar.d.ts +1 -1
- package/dist/cjs/index-70976837.js +0 -1
- package/dist/es/index-e2c80a3e.js +0 -1
package/dist/cjs/cli.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var e=require("node:fs"),r=require("cac"),o=require("./index-
|
|
1
|
+
"use strict";var e=require("node:fs"),r=require("cac"),o=require("./index-8d8db60d.js");require("node:fs/promises"),require("axios"),require("pull-md-img"),require("ora"),require("cli-progress"),require("log4js"),require("rand-user-agent");const i=r.cac("yuque-dl"),{version:n}=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());i.command("<url>","语雀知识库url").option("-d, --distDir <dir>","下载的目录 eg: -d download",{default:"download"}).option("-i, --ignoreImg","忽略图片不下载",{default:!1}).option("-t, --token <token>",'语雀的cookie "_yuque_session"').action((async(e,r)=>{try{await o.main(e,r)}catch(e){o.logger.error(e.message||"unknown exception")}})),i.help(),i.version(n);try{i.parse()}catch(e){o.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,c=(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,c.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 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:""},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,a.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,a.push(i)))})),n+=this.genSummaryContent(a,"");try{await e.writeFile(`${o}/SUMMARY.md`,n)}catch(e){c.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=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(e){e.BOARD="board",e.TABLE="table",e.SHEET="sheet",e.DOC="doc"}(a||(a={}));const h=new Map([[a.BOARD,"画板类型"],[a.TABLE,"数据表类型"],[a.SHEET,"表格类型"],[a.DOC,"文档类型"]]);var p;function g(e){const t={"user-agent":d({browser:"chrome",device:"desktop"})};return e&&(t.cookie=`_yuque_session=${e}`),t}function f(e,r){const o=/decodeURIComponent\("(.+)"\)\);/m;return t.get(e,{headers:g(r)}).then((({data:e="",status:t})=>200===t?e:"")).then((e=>{const t=o.exec(e)??"";if(!t[1])return{};const r=JSON.parse(decodeURIComponent(t[1]));if(!r.book)return{};return{bookId:r.book.id,bookSlug:r.book.slug,tocList:r.book.toc||[],bookName:r.book.name||"",bookDesc:r.book.description||""}}))}async function w(i,s,n){const{bookId:c,itemUrl:l,savePath:u,saveFilePath:d,uuid:p,articleUrl:f,articleTitle:w,ignoreImg:m}=i;let $=`https://www.yuque.com/api/docs/${l}`;const b={book_id:String(c),merge_dynamic_data:String(!1),mode:"markdown"},y=new URLSearchParams(b).toString();$=`${$}?${y}`;let k=(await t.get($,{headers:g(n)}).then((({data:e,status:t})=>{if(200===t){const t=e,r=t?.data?.type?.toLocaleLowerCase();if([a.BOARD,a.SHEET,a.TABLE].includes(r)){const e=h.get(r);throw new Error(`download article Error: 暂不支持“${e}”的文档`)}if(!t?.data?.sourcecode)throw new Error(`download article Error: ${$}`);return t}throw new Error(`download article Error: ${$} http status ${t}`)}))).data.sourcecode;if(!m){s.pause(),console.log("");const e=o({text:`下载 "${w}" 的图片中...`});e.start();try{k=await r.run(k,{dist:u,imgDir:`./img/${p}`,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()}}k=k.replace(/<br(\s?)\/>/gm,"\n"),w&&(k=`# ${w}\n\x3c!--page header--\x3e\n\n${k}\n\n`),f&&(k+=`\x3c!--page footer--\x3e\n- 原文: <${f}>`);try{return await e.writeFile(d,k),!0}catch(e){throw new Error(`download article Error ${f}: ${e.message}`)}}function m(e){if(!e)return"";return e.replace(/[\\/:*?"<>|\n\r]/g,"_").replace(/\s/,"")}!function(e){e.TITLE="title",e.LINK="link",e.DOC="doc"}(p||(p={})),exports.downloadArticle=w,exports.getKnowledgeBaseInfo=f,exports.logger=c,exports.main=async function(t,r){const{bookId:o,tocList:i,bookName:s,bookDesc:n,bookSlug:a}=await f(t,r.token);if(!o)throw new Error("No found book id");if(!i||0===i.length)throw new Error("No found toc list");const d=`${r.distDir}/${s?m(s):o}`;await e.mkdir(d,{recursive:!0});const h=i.length,g=new l(d,h);if(await g.init(),g.curr===h)return g.bar&&g.bar.stop(),void c.info(`√ 已完成: ${process.cwd()}/${d}`);const $=new Map;g.isDownloadInterrupted&&g.progressInfo.forEach((e=>{$.set(e.toc.uuid,e)}));const b=t.replace(new RegExp(`(.*?/${a}).*`),"$1");let y=0,k=0,I=0,L=[],E=[];for(let t=0;t<h;t++){const s=i[t];if("string"!=typeof s.type)continue;if($.get(s.uuid))continue;const n=s.type.toLocaleLowerCase();if(n===p.TITLE||""!==s.child_uuid||n===p.LINK){let t=s,r=[],o=[];for(;t;)r.unshift(m(t.title)),o.unshift(t.uuid),t=$.get(t.parent_uuid)?$.get(t.parent_uuid).toc:void 0;const i={path:r.join("/"),pathTitleList:r,pathIdList:o,toc:s};n===p.LINK?(I+=1,E.push(i)):await e.mkdir(`${d}/${r.join("/")}`,{recursive:!0}),$.set(s.uuid,i),await g.updateProgress(i,n!==p.LINK)}else if(s.url){k+=1;let e={path:"",pathTitleList:[],pathIdList:[]};$.get(s.parent_uuid)&&(e=$.get(s.parent_uuid));const t=m(s.title),i=[...e.pathTitleList,`${t}.md`],n=[...e.pathIdList,s.uuid],a={path:i.join("/"),pathTitleList:i,pathIdList:n,toc:s};let c=!0;const l=`${b}/${s.url}`;try{await w({bookId:o,itemUrl:s.url,savePath:`${d}/${e.path}`,saveFilePath:`${d}/${a.path}`,uuid:s.uuid,articleUrl:l,articleTitle:s.title,ignoreImg:r.ignoreImg},g,r.token)}catch(e){c=!1,y+=1,L.push({articleUrl:l,errItem:a,errMsg:e.message,err:e})}$.set(s.uuid,a),await g.updateProgress(a,c)}}if(I>0){c.warn("该知识库存在以下外链文章");for(const e of E)c.warn(`———— ✕ ${e.path} ${e.toc.url}`)}if(y>0){c.error(`本次执行总数${k}篇,✕ 失败${y}篇`);for(const e of L)c.error(`${e.errItem.path} ———— ${e.articleUrl}`),c.error(`———— ✕ ${e.errMsg}`);c.error("o(╥﹏╥)o 由于网络波动或链接失效以上下载失败,可重新执行命令重试(不会影响已下载成功的数据)~~")}const v=new u({bookPath:d,bookName:s,bookDesc:n,uuidMap:$});await v.genFile();const S=process.cwd();c.info(`√ 生成目录 ${S}/${d}/SUMMARY.md`),g.curr===h&&c.info(`√ 已完成: ${S}/${d}`),process.exit(0)};
|
package/dist/cjs/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";require("node:fs/promises"),require("axios"),require("pull-md-img"),require("ora");var e=require("./index-
|
|
1
|
+
"use strict";require("node:fs/promises"),require("axios"),require("pull-md-img"),require("ora");var e=require("./index-8d8db60d.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-7b2fb7ac.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}).option("-t, --token <token>",'语雀的cookie "_yuque_session"').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 n from"cli-progress";import a from"log4js";import c from"rand-user-agent";var l,u=(a.configure({appenders:{cheese:{type:"console",layout:{type:"pattern",pattern:"%[%c [%p]:%] %m%n"}}},categories:{default:{appenders:["cheese"],level:"trace"}}}),a.getLogger("yuque-dl"));class h{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,u.info("根据上次数据继续断点下载")),this.bar=new n.SingleBar({format:"Download [{bar}] {percentage}% | {value}/{total}"},n.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 d{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 n=s;const a=[];i.forEach((t=>{const e=t.toc,r=e.parent_uuid,o=this.findTree(a,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,a.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,a.push(i)))})),n+=this.genSummaryContent(a,"");try{await t.writeFile(`${o}/SUMMARY.md`,n)}catch(t){u.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 p({browser:t="chrome",os:e="mac os",device:r="desktop"}){r=r.toLowerCase(),t=t.toLowerCase(),e=e.toLowerCase();let o=c(r,t,e);if("chrome"===t)for(;o.includes("Chrome-Lighthouse")||o.includes("Gener8")||o.includes("HeadlessChrome")||o.includes("SMTBot");)o=c(r,t,e);if("safari"===t)for(;o.includes("Applebot");)o=c(r,t,e);return o}!function(t){t.BOARD="board",t.TABLE="table",t.SHEET="sheet",t.DOC="doc"}(l||(l={}));const g=new Map([[l.BOARD,"画板类型"],[l.TABLE,"数据表类型"],[l.SHEET,"表格类型"],[l.DOC,"文档类型"]]);var f;function m(t){const e={"user-agent":p({browser:"chrome",device:"desktop"})};return t&&(e.cookie=`_yuque_session=${t}`),e}function w(t,e){const r=/decodeURIComponent\("(.+)"\)\);/m;return o.get(t,{headers:m(e)}).then((({data:t="",status:e})=>200===e?t:"")).then((t=>{const e=r.exec(t)??"";if(!e[1])return{};const o=JSON.parse(decodeURIComponent(e[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 $(t,r,n){const{bookId:a,itemUrl:c,savePath:u,saveFilePath:h,uuid:d,articleUrl:p,articleTitle:f,ignoreImg:w}=t;let $=`https://www.yuque.com/api/docs/${c}`;const b={book_id:String(a),merge_dynamic_data:String(!1),mode:"markdown"},y=new URLSearchParams(b).toString();$=`${$}?${y}`;let k=(await o.get($,{headers:m(n)}).then((({data:t,status:e})=>{if(200===e){const e=t,r=e?.data?.type?.toLocaleLowerCase();if([l.BOARD,l.SHEET,l.TABLE].includes(r)){const t=g.get(r);throw new Error(`download article Error: 暂不支持“${t}”的文档`)}if(!e?.data?.sourcecode)throw new Error(`download article Error: ${$}`);return e}throw new Error(`download article Error: ${$} http status ${e}`)}))).data.sourcecode;if(!w){r.pause(),console.log("");const t=s({text:`下载 "${f}" 的图片中...`});t.start();try{k=await i.run(k,{dist:u,imgDir:`./img/${d}`,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()}}k=k.replace(/<br(\s?)\/>/gm,"\n"),f&&(k=`# ${f}\n\x3c!--page header--\x3e\n\n${k}\n\n`),p&&(k+=`\x3c!--page footer--\x3e\n- 原文: <${p}>`);try{return await e(h,k),!0}catch(t){throw new Error(`download article Error ${p}: ${t.message}`)}}function b(t){if(!t)return"";return t.replace(/[\\/:*?"<>|\n\r]/g,"_").replace(/\s/,"")}async function y(t,e){const{bookId:o,tocList:i,bookName:s,bookDesc:n,bookSlug:a}=await w(t,e.token);if(!o)throw new Error("No found book id");if(!i||0===i.length)throw new Error("No found toc list");const c=`${e.distDir}/${s?b(s):o}`;await r(c,{recursive:!0});const l=i.length,p=new h(c,l);if(await p.init(),p.curr===l)return p.bar&&p.bar.stop(),void u.info(`√ 已完成: ${process.cwd()}/${c}`);const g=new Map;p.isDownloadInterrupted&&p.progressInfo.forEach((t=>{g.set(t.toc.uuid,t)}));const m=t.replace(new RegExp(`(.*?/${a}).*`),"$1");let y=0,k=0,I=0,L=[],E=[];for(let t=0;t<l;t++){const s=i[t];if("string"!=typeof s.type)continue;if(g.get(s.uuid))continue;const n=s.type.toLocaleLowerCase();if(n===f.TITLE||""!==s.child_uuid||n===f.LINK){let t=s,e=[],o=[];for(;t;)e.unshift(b(t.title)),o.unshift(t.uuid),t=g.get(t.parent_uuid)?g.get(t.parent_uuid).toc:void 0;const i={path:e.join("/"),pathTitleList:e,pathIdList:o,toc:s};n===f.LINK?(I+=1,E.push(i)):await r(`${c}/${e.join("/")}`,{recursive:!0}),g.set(s.uuid,i),await p.updateProgress(i,n!==f.LINK)}else if(s.url){k+=1;let t={path:"",pathTitleList:[],pathIdList:[]};g.get(s.parent_uuid)&&(t=g.get(s.parent_uuid));const r=b(s.title),i=[...t.pathTitleList,`${r}.md`],n=[...t.pathIdList,s.uuid],a={path:i.join("/"),pathTitleList:i,pathIdList:n,toc:s};let l=!0;const u=`${m}/${s.url}`;try{await $({bookId:o,itemUrl:s.url,savePath:`${c}/${t.path}`,saveFilePath:`${c}/${a.path}`,uuid:s.uuid,articleUrl:u,articleTitle:s.title,ignoreImg:e.ignoreImg},p,e.token)}catch(t){l=!1,y+=1,L.push({articleUrl:u,errItem:a,errMsg:t.message,err:t})}g.set(s.uuid,a),await p.updateProgress(a,l)}}if(I>0){u.warn("该知识库存在以下外链文章");for(const t of E)u.warn(`———— ✕ ${t.path} ${t.toc.url}`)}if(y>0){u.error(`本次执行总数${k}篇,✕ 失败${y}篇`);for(const t of L)u.error(`${t.errItem.path} ———— ${t.articleUrl}`),u.error(`———— ✕ ${t.errMsg}`);u.error("o(╥﹏╥)o 由于网络波动或链接失效以上下载失败,可重新执行命令重试(不会影响已下载成功的数据)~~")}const v=new d({bookPath:c,bookName:s,bookDesc:n,uuidMap:g});await v.genFile();const S=process.cwd();u.info(`√ 生成目录 ${S}/${c}/SUMMARY.md`),p.curr===l&&u.info(`√ 已完成: ${S}/${c}`),process.exit(0)}!function(t){t.TITLE="title",t.LINK="link",t.DOC="doc"}(f||(f={}));export{$ as d,w as g,u as l,y as m};
|
package/dist/es/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
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-
|
|
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-7b2fb7ac.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.20",
|
|
4
4
|
"description": "yuque 知识库下载",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -60,10 +60,10 @@
|
|
|
60
60
|
"cli-progress": "^3.12.0",
|
|
61
61
|
"log4js": "^6.9.1",
|
|
62
62
|
"ora": "^7.0.1",
|
|
63
|
-
"pull-md-img": "^0.0.
|
|
63
|
+
"pull-md-img": "^0.0.24",
|
|
64
64
|
"rand-user-agent": "1.0.109"
|
|
65
65
|
},
|
|
66
66
|
"engines": {
|
|
67
|
-
"node": "
|
|
67
|
+
"node": ">=16.14.0"
|
|
68
68
|
}
|
|
69
69
|
}
|
package/types/ProgressBar.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ export interface IProgressItem {
|
|
|
6
6
|
pathIdList: string[];
|
|
7
7
|
pathTitleList: string[];
|
|
8
8
|
}
|
|
9
|
-
export type IProgress = IProgressItem[];
|
|
9
|
+
export declare type IProgress = IProgressItem[];
|
|
10
10
|
export default class ProgressBar {
|
|
11
11
|
bookPath: string;
|
|
12
12
|
progressFilePath: string;
|
|
@@ -1 +0,0 @@
|
|
|
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 t={"user-agent":u({browser:"chrome",device:"desktop"})};return e&&(t.cookie=`_yuque_session=${e}`),t}function d(e,r){const o=/decodeURIComponent\(\"(.+)\"\)\);/m;return t.get(e,{headers:h(r)}).then((({data:e="",status:t})=>200===t?e:"")).then((e=>{const t=e.match(o)||"";if(!t[1])return{};const r=JSON.parse(decodeURIComponent(t[1]));if(!r.book)return{};return{bookId:r.book.id,bookSlug:r.book.slug,tocList:r.book.toc||[],bookName:r.book.name||"",bookDesc:r.book.description||""}}))}async function p(i,s,n){const{bookId:a,itemUrl:l,savePath:c,saveFilePath:u,uuid:d,articleUrl:p,articleTitle:g,ignoreImg:f}=i;let w=`https://www.yuque.com/api/docs/${l}`;const m={book_id:String(a),merge_dynamic_data:String(!1),mode:"markdown"},$=new URLSearchParams(m).toString();w=`${w}?${$}`;let y=(await t.get(w,{headers:h(n)}).then((({data:e,status:t})=>{if(200===t){const t=e;if(!t||!t.data||!t.data.sourcecode)throw new Error(`download article Error: ${w}`);return t}throw new Error(`download article Error: ${w} http status ${t}`)})).catch((e=>{throw new Error(`download article Error: ${w} ${e.message}`)}))).data.sourcecode;if(!f){s.pause(),console.log("");const e=o({text:`下载 "${g}" 的图片中...`});e.start();try{y=await r.run(y,{dist:c,imgDir:`img/${d}`,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()}}y=y.replace(/<br(\s?)\/>/gm,"\n"),g&&(y=`# ${g}\n\x3c!--page header--\x3e\n\n${y}\n\n`),p&&(y+=`\x3c!--page footer--\x3e\n- 原文: <${p}>`);try{return await e.writeFile(u,y),!0}catch(e){throw new Error(`download article Error ${p}: ${e.message}`)}}function g(e){if(!e)return"";return e.replace(/[\\\/:\*\?"<>\|\n\r]/g,"_").replace(/\s/,"")}exports.downloadArticle=p,exports.getKnowledgeBaseInfo=d,exports.logger=a,exports.main=async function(t,r){const{bookId:o,tocList:i,bookName:s,bookDesc:n,bookSlug:u}=await d(t,r.token);if(!o)throw new Error("No found book id");if(!i||0===i.length)throw new Error("No found toc list");const h=`${r.distDir}/${s?g(s):o}`;await e.mkdir(h,{recursive:!0});const f=i.length,w=new l(h,f);if(await w.init(),w.curr===f)return w.bar&&w.bar.stop(),void a.info(`√ 已完成: ${process.cwd()}/${h}`);const m=new Map;w.isDownloadInterrupted&&w.progressInfo.forEach((e=>{m.set(e.toc.uuid,e)}));const $=t.replace(new RegExp(`(.*?/${u}).*`),"$1");let y=0,b=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(g(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(`${h}/${r.join("/")}`,{recursive:!0}),m.set(s.uuid,i),await w.updateProgress(i,"link"!==n)}else if(s.url){b+=1;let e={path:"",pathTitleList:[],pathIdList:[]};m.get(s.parent_uuid)&&(e=m.get(s.parent_uuid));const t=g(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;const c=`${$}/${s.url}`;try{await p({bookId:o,itemUrl:s.url,savePath:`${h}/${e.path}`,saveFilePath:`${h}/${a.path}`,uuid:s.uuid,articleUrl:c,articleTitle:s.title,ignoreImg:r.ignoreImg},w,r.token)}catch(e){l=!1,y+=1,I.push({articleUrl:c,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(y>0){a.error(`本次执行总数${b}篇,✕ 失败${y}篇`);for(let e=0;e<I.length;e++){const t=I[e];a.error(`${t.errItem.path} ———— ${t.articleUrl}`),a.error(`———— ✕ ${t.errMsg}`)}a.error("o(╥﹏╥)o 由于网络波动或链接失效以上下载失败,可重新执行命令重试(不会影响已下载成功的数据)~~")}const L=new c({bookPath:h,bookName:s,bookDesc:n,uuidMap:m});await L.genFile();const P=process.cwd();a.info(`√ 生成目录 ${P}/${h}/SUMMARY.md`),w.curr===f&&a.info(`√ 已完成: ${P}/${h}`),process.exit(0)};
|
|
@@ -1 +0,0 @@
|
|
|
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 n from"cli-progress";import a from"log4js";import l from"rand-user-agent";var c=(a.configure({appenders:{cheese:{type:"console",layout:{type:"pattern",pattern:"%[%c [%p]:%] %m%n"}}},categories:{default:{appenders:["cheese"],level:"trace"}}}),a.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 n.SingleBar({format:"Download [{bar}] {percentage}% | {value}/{total}"},n.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 n=s;const a=[];i.forEach((t=>{const e=t.toc,r=e.parent_uuid,o=this.findTree(a,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,a.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,a.push(i)))})),n+=this.genSummaryContent(a,"");try{await t.writeFile(`${o}/SUMMARY.md`,n)}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={"user-agent":d({browser:"chrome",device:"desktop"})};return t&&(e.cookie=`_yuque_session=${t}`),e}function g(t,e){const r=/decodeURIComponent\(\"(.+)\"\)\);/m;return o.get(t,{headers:p(e)}).then((({data:t="",status:e})=>200===e?t:"")).then((t=>{const e=t.match(r)||"";if(!e[1])return{};const o=JSON.parse(decodeURIComponent(e[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 f(t,r,n){const{bookId:a,itemUrl:l,savePath:c,saveFilePath:u,uuid:h,articleUrl:d,articleTitle:g,ignoreImg:f}=t;let m=`https://www.yuque.com/api/docs/${l}`;const w={book_id:String(a),merge_dynamic_data:String(!1),mode:"markdown"},$=new URLSearchParams(w).toString();m=`${m}?${$}`;let y=(await o.get(m,{headers:p(n)}).then((({data:t,status:e})=>{if(200===e){const e=t;if(!e||!e.data||!e.data.sourcecode)throw new Error(`download article Error: ${m}`);return e}throw new Error(`download article Error: ${m} http status ${e}`)})).catch((t=>{throw new Error(`download article Error: ${m} ${t.message}`)}))).data.sourcecode;if(!f){r.pause(),console.log("");const t=s({text:`下载 "${g}" 的图片中...`});t.start();try{y=await i.run(y,{dist:c,imgDir:`img/${h}`,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()}}y=y.replace(/<br(\s?)\/>/gm,"\n"),g&&(y=`# ${g}\n\x3c!--page header--\x3e\n\n${y}\n\n`),d&&(y+=`\x3c!--page footer--\x3e\n- 原文: <${d}>`);try{return await e(u,y),!0}catch(t){throw new Error(`download article Error ${d}: ${t.message}`)}}function m(t){if(!t)return"";return t.replace(/[\\\/:\*\?"<>\|\n\r]/g,"_").replace(/\s/,"")}async function w(t,e){const{bookId:o,tocList:i,bookName:s,bookDesc:n,bookSlug:a}=await g(t,e.token);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?m(s):o}`;await r(l,{recursive:!0});const d=i.length,p=new u(l,d);if(await p.init(),p.curr===d)return p.bar&&p.bar.stop(),void c.info(`√ 已完成: ${process.cwd()}/${l}`);const w=new Map;p.isDownloadInterrupted&&p.progressInfo.forEach((t=>{w.set(t.toc.uuid,t)}));const $=t.replace(new RegExp(`(.*?/${a}).*`),"$1");let y=0,b=0,k=0,I=[],L=[];for(let t=0;t<d;t++){const s=i[t];if("string"!=typeof s.type)continue;if(w.get(s.uuid))continue;const n=s.type.toLocaleLowerCase();if("title"===n||""!==s.child_uuid||"link"===n){let t=s,e=[],o=[];for(;t;)e.unshift(m(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"===n?(k+=1,L.push(i)):await r(`${l}/${e.join("/")}`,{recursive:!0}),w.set(s.uuid,i),await p.updateProgress(i,"link"!==n)}else if(s.url){b+=1;let t={path:"",pathTitleList:[],pathIdList:[]};w.get(s.parent_uuid)&&(t=w.get(s.parent_uuid));const r=m(s.title),i=[...t.pathTitleList,`${r}.md`],n=[...t.pathIdList,s.uuid],a={path:i.join("/"),pathTitleList:i,pathIdList:n,toc:s};let c=!0;const u=`${$}/${s.url}`;try{await f({bookId:o,itemUrl:s.url,savePath:`${l}/${t.path}`,saveFilePath:`${l}/${a.path}`,uuid:s.uuid,articleUrl:u,articleTitle:s.title,ignoreImg:e.ignoreImg},p,e.token)}catch(t){c=!1,y+=1,I.push({articleUrl:u,errItem:a,errMsg:t.message,err:t})}w.set(s.uuid,a),await p.updateProgress(a,c)}}if(k>0){c.warn("该知识库存在以下外链文章");for(let t=0;t<L.length;t++)c.warn(`———— ✕ ${L[t].path} ${L[t].toc.url}`)}if(y>0){c.error(`本次执行总数${b}篇,✕ 失败${y}篇`);for(let t=0;t<I.length;t++){const e=I[t];c.error(`${e.errItem.path} ———— ${e.articleUrl}`),c.error(`———— ✕ ${e.errMsg}`)}c.error("o(╥﹏╥)o 由于网络波动或链接失效以上下载失败,可重新执行命令重试(不会影响已下载成功的数据)~~")}const v=new h({bookPath:l,bookName:s,bookDesc:n,uuidMap:w});await v.genFile();const P=process.cwd();c.info(`√ 生成目录 ${P}/${l}/SUMMARY.md`),p.curr===d&&c.info(`√ 已完成: ${P}/${l}`),process.exit(0)}export{f as d,g,c as l,w as m};
|