yuque-dl 1.0.70 → 1.0.71

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/bin/index.js CHANGED
File without changes
package/dist/es/cli.js CHANGED
@@ -1 +1 @@
1
- import{_ as o,l as e,m as i}from"./index-kkT1fPB8.js";import{readFileSync as t}from"node:fs";import{cac as n}from"cac";import r from"semver";import"rand-user-agent";import{mkdir as s,writeFile as d,access as c,readdir as l,stat as m}from"node:fs/promises";import"cli-progress";import{createServer as p}from"vitepress";import{resolve as a,sep as u,join as f}from"node:path";import"log4js";import"node:process";import"axios";import"ora";import"pull-md-img";import"markdown-toc";import"pako";import"node:crypto";import"node:stream";import"node:util";import"mdast-util-from-markdown";import"mdast-util-to-markdown";let y;function v(e){return o(this,void 0,void 0,(function*(){const i=a(e);if(!(yield h(i)))throw new Error("server root not found");yield function(e){return o(this,void 0,void 0,(function*(){const i=e.split(u).filter(Boolean).at(-1)||"yuque-dl",t=f(e,"/.vitepress");yield s(t,{recursive:!0});const n=f(t,"config.ts"),r=yield function(e){return o(this,void 0,void 0,(function*(){const o=[],i=[".vitepress","img","index.md","progress.json","attachments"];let t=yield l(e);t=t.filter((o=>!i.includes(o)));for(const i of t){const t=f(e,i);if((yield m(t)).isDirectory()){const n=yield h(f(t,"index.md")),r={text:i,collapsed:!0,items:yield k(e,i)};n&&(r.link=`/${i}/`),o.push(r)}else o.push({text:i.slice(0,i.lastIndexOf(".")),link:`/${i}`})}return o}))}(e),c=`\n export default {\n title: "${i}",\n themeConfig: {\n search: {\n provider: 'local'\n },\n sidebar: ${JSON.stringify(r)}\n },\n vite: {\n optimizeDeps: {\n include: []\n }\n },\n markdown: {\n html: false\n }\n }\n `;yield d(n,c)}))}(i);const t=()=>o(this,void 0,void 0,(function*(){const i=yield p(e,{},(()=>o(this,void 0,void 0,(function*(){return y||(y=(()=>o(this,void 0,void 0,(function*(){yield i.close(),yield t()})))().finally((()=>{y=void 0}))),y}))));yield i.listen(),i.printUrls()}));yield t().catch((o=>{console.error(o),process.exit(1)}))}))}function h(e){return o(this,void 0,void 0,(function*(){try{return yield c(e),!0}catch(o){if("ENOENT"===o.code)return!1;throw o}}))}function k(e,...i){return o(this,void 0,void 0,(function*(){const o=yield l(f(e,...i)),t=[];for(const n of o){const o=f(e,...i,n);if((yield m(o)).isDirectory()){const r=yield k(f(e),...i,n);if(r.length>0){const e={text:n,items:r,collapsed:false};(yield h(f(o,"index.md")))&&(e.link="/"+[...i,n].map(decodeURIComponent).join("/")+"/"),t.push(e)}}else{if("index.md"===n||/^-.*\.(md|MD)$/.test(n)||!n.endsWith(".md"))continue;const o=n.replace(/\.md$/,""),e={text:o,link:"/"+[...i.map(decodeURIComponent),`${encodeURI(o)}.html`].join("/")};t.push(e)}}return t}))}const x=n("yuque-dl"),{version:g,engines:w}=JSON.parse(t(new URL("../../package.json",import.meta.url)).toString());!function(){const o=w.node;r.satisfies(process.version,o)||(e.error(`✕ nodejs 版本需 ${o}, 当前版本为 ${process.version}`),process.exit(1))}(),x.command("<url>","语雀知识库url").option("-d, --dist-dir <dir>","下载的目录 eg: -d download",{default:"download"}).option("-i, --ignore-img","忽略图片不下载",{default:!1}).option("-k, --key <key>",'语雀的cookie key, 默认是 "_yuque_session", 在某些企业版本中 key 不一样').option("-t, --token <token>","语雀的cookie key 对应的值").option("--toc","是否输出文档toc目录",{default:!1}).action(((t,n)=>o(void 0,void 0,void 0,(function*(){try{yield i(t,n),process.exit(0)}catch(o){e.error(o.message||"unknown exception"),process.exit(1)}})))),x.command("server <serverPath>","启动web服务").action((i=>o(void 0,void 0,void 0,(function*(){try{yield v(i)}catch(o){e.error(o.message||"unknown exception"),process.exit(1)}})))),x.help(),x.version(g);try{x.parse()}catch(o){e.error(o.message||"unknown exception"),process.exit(1)}
1
+ import{_ as o,l as e,m as i}from"./index-CeluBAOZ.js";import{readFileSync as t}from"node:fs";import{cac as n}from"cac";import r from"semver";import"rand-user-agent";import{mkdir as s,writeFile as d,access as c,readdir as l,stat as m}from"node:fs/promises";import"cli-progress";import{createServer as p}from"vitepress";import{resolve as a,sep as u,join as f}from"node:path";import"log4js";import"node:process";import"axios";import"ora";import"pull-md-img";import"markdown-toc";import"pako";import"node:crypto";import"node:stream";import"node:util";import"mdast-util-from-markdown";import"mdast-util-to-markdown";let y;function v(e){return o(this,void 0,void 0,(function*(){const i=a(e);if(!(yield h(i)))throw new Error("server root not found");yield function(e){return o(this,void 0,void 0,(function*(){const i=e.split(u).filter(Boolean).at(-1)||"yuque-dl",t=f(e,"/.vitepress");yield s(t,{recursive:!0});const n=f(t,"config.ts"),r=yield function(e){return o(this,void 0,void 0,(function*(){const o=[],i=[".vitepress","img","index.md","progress.json","attachments"];let t=yield l(e);t=t.filter((o=>!i.includes(o)));for(const i of t){const t=f(e,i);if((yield m(t)).isDirectory()){const n=yield h(f(t,"index.md")),r={text:i,collapsed:!0,items:yield k(e,i)};n&&(r.link=`/${i}/`),o.push(r)}else o.push({text:i.slice(0,i.lastIndexOf(".")),link:`/${i}`})}return o}))}(e),c=`\n export default {\n title: "${i}",\n themeConfig: {\n search: {\n provider: 'local'\n },\n sidebar: ${JSON.stringify(r)}\n },\n vite: {\n optimizeDeps: {\n include: []\n }\n },\n markdown: {\n html: false\n }\n }\n `;yield d(n,c)}))}(i);const t=()=>o(this,void 0,void 0,(function*(){const i=yield p(e,{},(()=>o(this,void 0,void 0,(function*(){return y||(y=(()=>o(this,void 0,void 0,(function*(){yield i.close(),yield t()})))().finally((()=>{y=void 0}))),y}))));yield i.listen(),i.printUrls()}));yield t().catch((o=>{console.error(o),process.exit(1)}))}))}function h(e){return o(this,void 0,void 0,(function*(){try{return yield c(e),!0}catch(o){if("ENOENT"===o.code)return!1;throw o}}))}function k(e,...i){return o(this,void 0,void 0,(function*(){const o=yield l(f(e,...i)),t=[];for(const n of o){const o=f(e,...i,n);if((yield m(o)).isDirectory()){const r=yield k(f(e),...i,n);if(r.length>0){const e={text:n,items:r,collapsed:false};(yield h(f(o,"index.md")))&&(e.link="/"+[...i,n].map(decodeURIComponent).join("/")+"/"),t.push(e)}}else{if("index.md"===n||/^-.*\.(md|MD)$/.test(n)||!n.endsWith(".md"))continue;const o=n.replace(/\.md$/,""),e={text:o,link:"/"+[...i.map(decodeURIComponent),`${encodeURI(o)}.html`].join("/")};t.push(e)}}return t}))}const x=n("yuque-dl"),{version:g,engines:w}=JSON.parse(t(new URL("../../package.json",import.meta.url)).toString());!function(){const o=w.node;r.satisfies(process.version,o)||(e.error(`✕ nodejs 版本需 ${o}, 当前版本为 ${process.version}`),process.exit(1))}(),x.command("<url>","语雀知识库url").option("-d, --dist-dir <dir>","下载的目录 eg: -d download",{default:"download"}).option("-i, --ignore-img","忽略图片不下载",{default:!1}).option("-k, --key <key>",'语雀的cookie key, 默认是 "_yuque_session", 在某些企业版本中 key 不一样').option("-t, --token <token>","语雀的cookie key 对应的值").option("--toc","是否输出文档toc目录",{default:!1}).action(((t,n)=>o(void 0,void 0,void 0,(function*(){try{yield i(t,n),process.exit(0)}catch(o){e.error(o.message||"unknown exception"),process.exit(1)}})))),x.command("server <serverPath>","启动web服务").action((i=>o(void 0,void 0,void 0,(function*(){try{yield v(i)}catch(o){e.error(o.message||"unknown exception"),process.exit(1)}})))),x.help(),x.version(g);try{x.parse()}catch(o){e.error(o.message||"unknown exception"),process.exit(1)}
@@ -1 +1 @@
1
- import e,{writeFile as t,mkdir as o}from"node:fs/promises";import r from"node:path";import n from"rand-user-agent";import i from"log4js";import s from"cli-progress";import{env as a,stdout as l}from"node:process";import c from"axios";import u from"ora";import d from"pull-md-img";import h from"markdown-toc";import f from"pako";import p from"node:crypto";import{createWriteStream as m,mkdirSync as v}from"node:fs";import*as g from"node:stream";import{promisify as y}from"node:util";import{fromMarkdown as k}from"mdast-util-from-markdown";import{toMarkdown as $}from"mdast-util-to-markdown";function w(e,t,o,r){return new(o||(o=Promise))((function(n,i){function s(e){try{l(r.next(e))}catch(e){i(e)}}function a(e){try{l(r.throw(e))}catch(e){i(e)}}function l(e){var t;e.done?n(e.value):(t=e.value,t instanceof o?t:new o((function(e){e(t)}))).then(s,a)}l((r=r.apply(e,t||[])).next())}))}var I,b;"function"==typeof SuppressedError&&SuppressedError,function(e){e.TITLE="title",e.LINK="link",e.DOC="doc"}(I||(I={})),function(e){e.BOARD="board",e.TABLE="table",e.SHEET="sheet",e.DOC="doc"}(b||(b={}));const E=new Map([[b.BOARD,"画板类型"],[b.TABLE,"数据表类型"],[b.SHEET,"表格类型"],[b.DOC,"文档类型"]]),N="_yuque_session",P="https://www.yuque.com",L="UXO91eVnUveQn8suOJaYMvBcWs9KptS8N5HoP8ezSeU4vqApZpy1CkPaTpkpQEx2W2mlhxL8zwS8UePwBgksUM0CTtAODbTTTDFD",D=(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 S{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}init(){return w(this,void 0,void 0,(function*(){this.progressInfo=yield this.getProgress(),this.curr=this.progressInfo.length,this.curr!==this.total&&(this.curr>0&&this.curr!==this.total&&(this.isDownloadInterrupted=!0,D.info("根据上次数据继续断点下载")),this.bar=new s.SingleBar({format:"Download [{bar}] {percentage}% | {value}/{total}"},s.Presets.legacy),this.bar.start(this.total,this.curr))}))}getProgress(){return w(this,void 0,void 0,(function*(){let t=[];try{const o=yield e.readFile(this.progressFilePath,{encoding:"utf8"});t=JSON.parse(o)}catch(o){o&&"ENOENT"===o.code&&(yield e.writeFile(this.progressFilePath,JSON.stringify(t),{encoding:"utf8"}))}return t}))}updateProgress(t,o){return w(this,void 0,void 0,(function*(){this.curr!==this.total&&(this.curr=this.curr+1,o&&(this.progressInfo.push(t),yield 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(){var e;this.clearLine(2),null===(e=this.bar)||void 0===e||e.start(this.total,this.curr)}clearLine(e){var t;if(!(e<=0)&&"function"==typeof(null===(t=null===process||void 0===process?void 0:process.stderr)||void 0===t?void 0:t.cursorTo)){process.stderr.cursorTo(0);for(let t=0;t<e;t++)process.stderr.moveCursor(0,-1),process.stderr.clearLine(1)}}}function T({browser:e="chrome",os:t="mac os",device:o="desktop"}){o=o.toLowerCase(),e=e.toLowerCase(),t=t.toLowerCase();let r=n(o,e,t);if("chrome"===e)for(;r.includes("Chrome-Lighthouse")||r.includes("Gener8")||r.includes("HeadlessChrome")||r.includes("SMTBot");)r=n(o,e,t);if("safari"===e)for(;r.includes("Applebot");)r=n(o,e,t);return r}function U(e){return e.toString().padStart(2,"0")}function O(e){const t=new Date(e);if(isNaN(t.getTime()))return"";const o=t.getFullYear(),r=t.getMonth()+1,n=t.getDate(),i=t.getHours(),s=t.getMinutes(),a=t.getSeconds();return`${o}-${U(r)}-${U(n)} ${U(i)}:${U(s)}:${U(a)}`}class x{constructor(e){this.summaryInfo=e}genFile(){return w(this,void 0,void 0,(function*(){const{bookName:t,bookDesc:o,bookPath:r,uuidMap:n}=this.summaryInfo;let i=`# ${t}\n\n`;o&&(i+=`> ${o}\n\n`);let s=i;const a=[];n.forEach((e=>{const t=e.toc,o=t.parent_uuid,r=this.findTree(a,o),n={text:t.title.replace(/[\\/:*?"<>|\n\r]/g,"_").replace(/\s/,""),id:t.uuid,level:1,type:"link"},i=t.type.toLocaleLowerCase();i===I.TITLE||""!==t.child_uuid?(n.type="title","boolean"!=typeof r?(Array.isArray(r.children)||(r.children=[]),n.level=r.level+1,r.children.push(n)):(n.level=1,a.push(n)),i===I.DOC&&(n.link=e.path)):(n.type="link",n.link=i===I.LINK?e.toc.url:e.path,"boolean"!=typeof r?(Array.isArray(r.children)||(r.children=[]),n.level=r.level+1,r.children.push(n)):(n.level=1,a.push(n)))}));s+=this.genSummaryContent(a,"");try{yield e.writeFile(`${r}/index.md`,s)}catch(e){D.error("Generate Summary Error")}return s}))}genSummaryContent(e,t){for(const o of e){if(o.type===I.TITLE)if(o.link){const e=o.link?o.link.replace(/\s/g,"%20"):o.link;t+=`\n${"".padStart(o.level+1,"#")} [${o.text}](${e})\n\n`}else t+=`\n${"".padStart(o.level+1,"#")} ${o.text}\n\n`;else if(o.type===I.LINK){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 o=this.findTree(e.children,t);if(o)return o}return!1}findTree(e,t){if(!t)return!1;for(const o of e){const e=this.findIdItem(o,t);if(e)return e}return!1}}function C(e){const{key:t=N,token:o}=e,r={"user-agent":T({browser:"chrome",device:"desktop"})};return o&&(r.cookie=`${t}=${o};`),r}function j(e){const t={headers:C(e),beforeRedirect:t=>{t.headers=Object.assign(Object.assign({},(null==t?void 0:t.headers)||{}),C(e))}};return"test"===a.NODE_ENV&&(t.proxy=!1),t}const R=(e,t=!0)=>{const{articleUrl:o,bookId:r,token:n,key:i,host:s=P}=e;let a=`${s}/api/docs/${o}`;const l={book_id:String(r),merge_dynamic_data:String(!1)};t&&(l.mode="markdown");const u=new URLSearchParams(l).toString();return a=`${a}?${u}`,c.get(a,j({token:n,key:i})).then((({data:e,status:t})=>({apiUrl:a,httpStatus:t,response:e})))};function _(e){if(!e)return"";return e.replace(/[\\/:*?"<>|\n\r]/g,"_").replace(/\s/g,"").replace(/[\ud800-\udbff][\udc00-\udfff]/g,"")}const F=e=>{if(!e)return"";const t=f.inflate(e,{to:"string"}),o=JSON.parse(t);let r="";return o.forEach((e=>{const t=`## ${e.name}\n`,o=function(e){var t,o,r;let n=Object.keys(e);n=n.filter((t=>Object.keys(e[t]).some((o=>{var r,n;return null===(n=null===(r=null==e?void 0:e[t])||void 0===r?void 0:r[o])||void 0===n?void 0:n.v}))));let i=[];n.forEach((t=>{const o=e[t];o&&(i=i.concat(Object.keys(o)))}));const s=Math.max(...n.map((e=>Number(e)))),a=Math.max(...i.map((e=>Number(e))));if(s<0||a<0)return"";let l="";const c="ABCDEFGHIJKLMNOPQRSTUVWXYZ";let u=Array(a+1).fill(" ").map(((e,t)=>{const o=t%c.length;return c[o]})).join(" | ");u=`| |${u}|`;let d=Array(a+2).fill("---").join(" |");d=`|${d}|`,l=`${u}\n${d}\n`;for(let n=0;n<s+1;n++){const i=[];for(let s=0;s<a+1;s++){const a=(null===(o=null===(t=null==e?void 0:e[n])||void 0===t?void 0:t[s])||void 0===o?void 0:o.v)||null;a&&"string"==typeof a?i.push(a):a&&"object"==typeof a?"image"===(null==a?void 0:a.class)&&(null==a?void 0:a.src)?i.push(`![${null==a?void 0:a.name}'](${null==a?void 0:a.src})`):"checkbox"===(null==a?void 0:a.class)?i.push((null==a?void 0:a.value)?"[x] ":"[ ] "):"link"===(null==a?void 0:a.class)?i.push(`[${null==a?void 0:a.text}](${null==a?void 0:a.url})`):"select"===(null==a?void 0:a.class)&&i.push(null===(r=null==a?void 0:a.value)||void 0===r?void 0:r.join(",")):i.push(null)}l=`${l}${`| ${n+1} | ${i.join(" | ")}|`}\n`}return l}(e.data);r=`${r}\n${t}\n${o}`})),r};function A(e,t=[]){return function(e,t){try{const{host:o,pathname:r}=new URL(e);return!t.includes(o)&&Boolean(r)}catch(e){return!1}}(e,t)?`https://www.yuque.com/api/filetransfer/images?url=${encodeURIComponent(e)}&sign=${function(e){const t=p.createHash("sha256");return t.update(`${L}${e}`),t.digest("hex")}(e)}`:e}const M=y(g.finished);function B(e){return w(this,void 0,void 0,(function*(){const{fileUrl:t,savePath:o,token:r,key:n,fileName:i}=e;return c.get(t,Object.assign(Object.assign({},j({token:r,key:n})),{responseType:"stream"})).then((e=>w(this,void 0,void 0,(function*(){var r,n,s;if(null===(n=null===(r=e.request)||void 0===r?void 0:r.path)||void 0===n?void 0:n.startsWith("/login"))throw new Error(`"${i}" need token`);if(200===e.status){const r=m(o);return null===(s=e.data)||void 0===s||s.pipe(r),M(r).then((()=>({fileUrl:t,savePath:o})))}throw new Error(`response status ${e.status}`)}))))}))}const J=/\[(.*?)\]\((.*?)\)/g,q=/\[(.*?)\]\((.*?\.yuque\.com\/attachments.*?)\)/;function H(e){return w(this,void 0,void 0,(function*(){const{mdData:t,savePath:o,attachmentsDir:n,articleTitle:i,token:s,key:c}=e,d=(t.match(J)||[]).filter((e=>q.test(e)));if(0===d.length)return{mdData:t};const h=u({text:`下载 "${i}" 的附件中...`,stream:l});"test"!==a.NODE_ENV&&h.start();const f=r.resolve(o,n),p=d.map((e=>function(e,t){const[,o,n]=q.exec(e)||[];if(!n)return!1;const i=o||n.split("/").at(-1);if(!i)return!1;const s=r.join(t,i);return{fileName:i,url:n,rawMd:e,currentFilePath:s}}(e,f))).filter((e=>!1!==e));v(f,{recursive:!0});const m=p.map((e=>B({fileUrl:e.url,savePath:e.currentFilePath,token:s,key:c,fileName:e.fileName}))),g=yield Promise.all(m).finally((function(){h&&h.stop()}));let y=t;return g.forEach((e=>{const t=p.find((t=>t.url===e.fileUrl));if(t){const e=`[附件: ${t.fileName}](${n}/${t.fileName})`;y=y.replace(t.rawMd,e)}})),{mdData:y}}))}function K(e){const t=[];return V(e,(function(e,o){"link"===e.type&&t.push({node:e,keyChain:o})})),t}function V(e,t,o=[]){t(e,o),Array.isArray(e.children)&&(o.push("children"),e.children.forEach(((e,r)=>{V(e,t,[...o,String(r)])})))}const W=/name="audio" value="data:(.*?audioId.*?)".*?><\/card>/gm;function G(e){return w(this,void 0,void 0,(function*(){const{mdData:t,htmlData:o,savePath:n,attachmentsDir:i,articleTitle:s,token:c,key:d}=e,h=function(e){return k(e)}(t),f=K(h).filter((e=>/_lake_card.*?videoId/.test(e.node.url))),p=function(e){const t=e.match(W)||[];try{return t.map((e=>e.replace(W,"$1"))).map((e=>JSON.parse(decodeURIComponent(e))))}catch(e){return[]}}(o);if(0===f.length&&0===p.length)return{mdData:t};const m=u({text:`下载 "${s}" 的音视频中...`,stream:l});"test"!==a.NODE_ENV&&m.start();const g=r.resolve(n,i);v(g,{recursive:!0});let y=t;try{if(f.length>0){const t=yield function(e,t,o){return w(this,void 0,void 0,(function*(){const{key:n,token:i}=t,s=e.map((e=>w(this,void 0,void 0,(function*(){var t,s;const a=function(e){var t;try{const o=null!==(t=new URL(e).searchParams.get("_lake_card"))&&void 0!==t?t:"",r=decodeURIComponent(o),n=JSON.parse(r);return{name:(null==n?void 0:n.name)||"",videoId:(null==n?void 0:n.videoId)||""}}catch(e){return!1}}(e.node.url);if(!a)return!1;const l=yield Q({videoId:a.videoId,key:n,token:i});if(!l)return!1;const c=null!==(s=null!==(t=a.name)&&void 0!==t?t:a.videoId.split("/").at(-1))&&void 0!==s?s:a.videoId;return{videoInfo:Object.assign(Object.assign({},a),l),astNode:e,fileName:c,currentFilePath:r.join(o,c)}}))));return(yield Promise.all(s)).filter(Y)}))}(f,e,g),o=t.map((e=>B({fileUrl:e.videoInfo.video,savePath:e.currentFilePath,token:c,key:d,fileName:e.videoInfo.name})));(yield Promise.all(o)).forEach((e=>{const o=t.find((t=>t.videoInfo.video===e.fileUrl));o&&(o.astNode.node.url=`${i}${r.sep}${o.fileName}`,o.astNode.node.children=[{type:"text",value:`音视频附件: ${o.videoInfo.name}`}])})),y=function(e){return $(e)}(h)}if(p.length>0){const t=yield function(e,t,o){return w(this,void 0,void 0,(function*(){const{key:n,token:i}=t,s=e.map((e=>w(this,void 0,void 0,(function*(){var t;const s=yield Q({videoId:e.audioId,key:n,token:i});if(!s)return!1;const a=null!==(t=null==e?void 0:e.fileName)&&void 0!==t?t:e.id;return{audioInfo:Object.assign(Object.assign({},e),s),fileName:a,currentFilePath:r.join(o,a)}}))));return(yield Promise.all(s)).filter(Y)}))}(p,e,g),o=t.map((e=>B({fileUrl:e.audioInfo.audio,savePath:e.currentFilePath,token:c,key:d,fileName:e.audioInfo.fileName}))),n=yield Promise.all(o);let s="\n\n> [yuque-dl warn]: 由于语雀markdown接口限制, 无法准确定位音频文件在文档中所在位置, 所以统一所有音频放到一起\n";n.forEach((e=>{const o=t.find((t=>t.audioInfo.audio===e.fileUrl));o&&(s+=`> - [音视频附件: ${o.audioInfo.fileName}](${i}${r.sep}${o.fileName})\n`)})),y+=s}}finally{m&&m.stop()}return{mdData:y}}))}function Q(e){let t="https://www.yuque.com/api/video";const{videoId:o,token:r,key:n}=e,i=new URLSearchParams;return i.set("video_id",o),t=`${t}?${i.toString()}`,c.get(t,j({token:r,key:n})).then((({data:e,status:t})=>{const o=e.data;return 200===t&&"success"===o.status&&o.info})).catch((()=>!1))}function Y(e){return!!e}function z(e){return w(this,void 0,void 0,(function*(){var o,r,n,i,s,c,h,f,p,m;const{articleInfo:v,progressBar:g,options:y}=e,{token:k,key:$}=y,{bookId:w,itemUrl:I,savePath:N,saveFilePath:P,uuid:L,articleUrl:D,articleTitle:S,ignoreImg:T,host:U,imageServiceDomains:x}=v,C={articleUrl:I,bookId:w,token:k,host:U,key:$},{httpStatus:j,apiUrl:M,response:B}=yield R(C),J=null===(r=null===(o=null==B?void 0:B.data)||void 0===o?void 0:o.type)||void 0===r?void 0:r.toLocaleLowerCase();let q="";if(J===b.SHEET){const{response:e}=yield R(C,!1);try{const t=null===(n=null==e?void 0:e.data)||void 0===n?void 0:n.content,o=t?JSON.parse(t):{},r=null==o?void 0:o.sheet;q=r?F(r):""}catch(e){const t=E.get(J);throw new Error(`download article Error: “${t}”解析错误 ${e}`)}}else{if([b.BOARD,b.TABLE].includes(J)){const e=E.get(J);throw new Error(`download article Error: 暂不支持“${e}”的文档`)}if("string"!=typeof(null===(i=null==B?void 0:B.data)||void 0===i?void 0:i.sourcecode))throw new Error(`download article Error: ${M}, http status ${j}`);q=B.data.sourcecode,q=function(e){const t=/!\[(.*?)\]\((http.*?latex.*?)\)/gm,o=e.match(t);let r=e;const n=e;try{null==o||o.forEach((e=>{var o,n;t.lastIndex=0;const i=null!==(n=null===(o=t.exec(e))||void 0===o?void 0:o[2])&&void 0!==n?n:"",{pathname:s,search:a}=new URL(i);if(!s.endsWith(".svg")&&a){const t=decodeURIComponent(a);r=r.replace(e,t.slice(1))}}))}catch(e){return n}return r}(q)}const K=function(e){if(!e)return[];const t=/!\[(.*?)\]\((.*?)\)/gm;let o=Array.from(e.match(t)||[]);return o=o.map((e=>(e=e.replace(t,"$2"),/^http.*/g.test(e)?e:""))).filter((e=>Boolean(e))),o}(q),V=null!==(h=null===(c=null===(s=(yield R(C,!1)).response)||void 0===s?void 0:s.data)||void 0===c?void 0:c.content)&&void 0!==h?h:"";K.length&&!T&&(q=function(e,t,o){if(!o)return t;const r=/<card.*?name="image".*?value="data:(.*?)">(.*?)<\/card>/gm,n=[];let i,s=!0;for(;s||Boolean(i);)if(s=!1,i=r.exec(o),null==i?void 0:i[1])try{const e=decodeURIComponent(i[1]),t=JSON.parse(e);n.push((null==t?void 0:t.src)||"")}catch(e){n.push("")}const a=new Map;return e.forEach((e=>{const{origin:o,pathname:r}=new URL(e),i=`${o}${r}`,s=n.find(((e,t)=>{const o=new RegExp(`${i}.*?`).test(e);return o&&n.splice(t,1),o}));if(s){const o=new RegExp(e,"g"),r=a.get(e)||0;let n=0;t=t.replace(o,(e=>{let t=e;return n===r&&(t=s),n+=1,t})),a.set(e,r+1)}})),t}(K,q,V));const W={toc:y.toc,articleTitle:S,articleUrl:D,articleUpdateTime:O(null!==(p=null===(f=null==B?void 0:B.data)||void 0===f?void 0:f.content_updated_at)&&void 0!==p?p:"")},Q=[];try{g.pause(),console.log("");q=(yield H({mdData:q,savePath:N,attachmentsDir:`./attachments/${_(L)}`,articleTitle:S,token:k,key:$})).mdData}catch(e){Q.push(`附件下载失败: ${e.message||"unknown error"}`)}finally{g.continue()}try{g.pause(),console.log("");q=(yield G({mdData:q,htmlData:V,savePath:N,attachmentsDir:`./attachments/${_(L)}`,articleTitle:S,token:k,key:$})).mdData}catch(e){Q.push(`音视频下载失败: ${e.message||"unknown error"}`)}finally{g.continue()}if(K.length&&!T){let e;g.pause(),console.log(""),"test"!==a.NODE_ENV&&(e=u({text:`下载 "${S}" 的图片中...`,stream:l}),e.start());let o=[],r=q;try{const e=yield d.run(q,{dist:N,imgDir:`./img/${L}`,isIgnoreConsole:!0,errorStillReturn:!0,referer:D||"",transform:e=>A(e=e.replace("x-oss-process=image%2Fwatermark%2C",""),x)});o=e.errorInfo,r=e.data}catch(e){o=[e]}q=r;const n=()=>{e&&e.stop(),g.continue()};if(o.length>0){const e=o[0];let r="图片下载失败(失败的以远程链接保存): ";throw r=e.url?`${r}${null===(m=e.error)||void 0===m?void 0:m.message} ${e.url.slice(0,20)}...`:`${r}${e.message}`,yield t(P,X(q,W)),n(),new Error(`${r}`)}n()}try{if(yield t(P,X(q,W)),Q.length>0)throw new Error(Q[0]);return!0}catch(e){throw new Error(`${e.message}`)}}))}function X(e,t){const{articleTitle:o,articleUrl:r,toc:n}=t;let i=e;i=i.replace(/<a.*?>(\s*?)<\/a>/gm,"");const s=o?`# ${o}\n\n`:"";let a=n?h(i).content:"";a&&(a=`${a}\n\n---\n\n`);let l="\n\n";return t.articleUpdateTime&&(l+=`> 更新: ${t.articleUpdateTime} \n`),r&&(l+=`> 原文: <${r}>`),i=`${s}${a}${i}${l}`,i}function Z(e,t){return w(this,void 0,void 0,(function*(){if(!function(e){if("function"==typeof URL.canParse)return URL.canParse(e);try{return new URL(e),!0}catch(e){return!1}}(e))throw new Error("Please enter a valid URL");const{bookId:n,tocList:i,bookName:s,bookDesc:a,bookSlug:l,host:u,imageServiceDomains:d}=yield((e,t)=>{const o=/decodeURIComponent\("(.+)"\)\);/m;return c.get(e,j(t)).then((({data:e="",status:t})=>200===t?e:"")).then((e=>{var t,r;const n=null!==(t=o.exec(e))&&void 0!==t?t:"";if(!n[1])return{};const i=JSON.parse(decodeURIComponent(n[1]));return i.book?{bookId:i.book.id,bookSlug:i.book.slug,tocList:i.book.toc||[],bookName:i.book.name||"",bookDesc:i.book.description||"",host:(null===(r=i.space)||void 0===r?void 0:r.host)||P,imageServiceDomains:i.imageServiceDomains||[]}:{}})).catch((e=>{var t;const o=null!==(t=null==e?void 0:e.message)&&void 0!==t?t:"";if(!o)throw new Error("unknown error");if(["getaddrinfo ENOTFOUND","read ECONNRESET","Client network socket disconnected before secure TLS connection was established"].some((e=>o.startsWith(e))))throw new Error("请检查网络(是否正常联网/是否开启了代理软件)");throw new Error(o)}))})(e,{token:t.token,key:t.key});if(!n)throw new Error("No found book id");if(!i||0===i.length)throw new Error("No found toc list");const h=r.resolve(t.distDir,s?_(s):String(n));yield o(h,{recursive:!0});const f=i.length,p=new S(h,f);if(yield p.init(),p.curr===f)return p.bar&&p.bar.stop(),void D.info(`√ 已完成: ${h}`);const m=new Map;p.isDownloadInterrupted&&p.progressInfo.forEach((e=>{m.set(e.toc.uuid,e)}));const v=e.replace(new RegExp(`(.*?/${l}).*`),"$1");yield function(e){return w(this,void 0,void 0,(function*(){const{articleUrlPrefix:t,total:n,uuidMap:i,tocList:s,bookPath:a,bookId:l,progressBar:c,host:u,options:d,imageServiceDomains:h=[]}=e;let f=0,p=0,m=0;const v=[],g=[];for(let e=0;e<n;e++){const t=s[e];if("string"!=typeof t.type)continue;if(i.get(t.uuid))continue;const r=t.type.toLocaleLowerCase();if(r===I.TITLE||""!==t.child_uuid||r===I.LINK){let e=t;const n=[],s=[];for(;e;)n.unshift(_(e.title)),s.unshift(e.uuid),e=i.get(e.parent_uuid)?i.get(e.parent_uuid).toc:void 0;const l={path:n.map(_).join("/"),pathTitleList:n,pathIdList:s,toc:t};r===I.LINK?(m+=1,g.push(l)):yield o(`${a}/${n.map(_).join("/")}`,{recursive:!0}),i.set(t.uuid,l),r===b.DOC?yield y(t):yield c.updateProgress(l,r!==I.LINK)}else t.url&&(yield y(t))}function y(e){return w(this,void 0,void 0,(function*(){p+=1;let o={path:"",pathTitleList:[],pathIdList:[]};const n=e.type.toLocaleLowerCase();i.get(e.parent_uuid)&&(o=i.get(e.parent_uuid));const s=_(e.title),m=[...o.pathTitleList,s],g=[...o.pathIdList,e.uuid];let y=[...o.pathTitleList,`${s}.md`].map(_).join("/"),k=o.pathTitleList.map(_).join("/");n===b.DOC&&e.child_uuid&&(y=[...o.pathTitleList,s,"index.md"].map(_).join("/"),k=m.map(_).join("/"));const $={path:y,savePath:k,pathTitleList:m,pathIdList:g,toc:e};let w=!0;const I=`${t}/${e.url}`;try{const t={bookId:l,itemUrl:e.url,savePath:r.resolve(a,$.savePath),saveFilePath:r.resolve(a,$.path),uuid:e.uuid,articleUrl:I,articleTitle:e.title,ignoreImg:d.ignoreImg,host:u,imageServiceDomains:h};yield z({articleInfo:t,progressBar:c,options:d})}catch(e){w=!1,f+=1,v.push({articleUrl:I,errItem:$,errMsg:e.message,err:e})}i.set(e.uuid,$),yield c.updateProgress($,w)}))}if(m>0){D.warn("该知识库存在以下外链文章");for(const e of g)D.warn(`———— ✕ ${e.path} ${e.toc.url}`)}if(f>0){D.error(`本次执行总数${p}篇,✕ 失败${f}篇`);for(const e of v)D.error(`《${e.errItem.path}》: ${e.articleUrl}`),e.errMsg.split("\n").forEach((e=>{D.error(`———— ✕ ${e}`)}));D.error("o(╥﹏╥)o 由于网络波动或链接失效以上下载失败,可重新执行命令重试(PS:不会影响已下载成功的数据)")}}))}({articleUrlPrefix:v,total:f,uuidMap:m,tocList:i,bookPath:h,bookId:n,progressBar:p,host:u,options:t,imageServiceDomains:d});const g=new x({bookPath:h,bookName:s,bookDesc:a,uuidMap:m});yield g.genFile(),D.info(`√ 生成目录 ${r.resolve(h,"index.md")}`),p.curr===f&&D.info(`√ 已完成: ${h}`)}))}export{w as _,D as l,Z as m};
1
+ import e,{writeFile as t,mkdir as o}from"node:fs/promises";import r from"node:path";import n from"rand-user-agent";import i from"log4js";import s from"cli-progress";import{env as a,stdout as l}from"node:process";import c from"axios";import u from"ora";import d from"pull-md-img";import h from"markdown-toc";import f from"pako";import p from"node:crypto";import{createWriteStream as m,mkdirSync as v}from"node:fs";import*as g from"node:stream";import{promisify as y}from"node:util";import{fromMarkdown as k}from"mdast-util-from-markdown";import{toMarkdown as $}from"mdast-util-to-markdown";function w(e,t,o,r){return new(o||(o=Promise))((function(n,i){function s(e){try{l(r.next(e))}catch(e){i(e)}}function a(e){try{l(r.throw(e))}catch(e){i(e)}}function l(e){var t;e.done?n(e.value):(t=e.value,t instanceof o?t:new o((function(e){e(t)}))).then(s,a)}l((r=r.apply(e,t||[])).next())}))}var I,b;"function"==typeof SuppressedError&&SuppressedError,function(e){e.TITLE="title",e.LINK="link",e.DOC="doc"}(I||(I={})),function(e){e.BOARD="board",e.TABLE="table",e.SHEET="sheet",e.DOC="doc"}(b||(b={}));const E=new Map([[b.BOARD,"画板类型"],[b.TABLE,"数据表类型"],[b.SHEET,"表格类型"],[b.DOC,"文档类型"]]),N="_yuque_session",P="https://www.yuque.com",L="UXO91eVnUveQn8suOJaYMvBcWs9KptS8N5HoP8ezSeU4vqApZpy1CkPaTpkpQEx2W2mlhxL8zwS8UePwBgksUM0CTtAODbTTTDFD",D=(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 S{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}init(){return w(this,void 0,void 0,(function*(){this.progressInfo=yield this.getProgress(),this.curr=this.progressInfo.length,this.curr!==this.total&&(this.curr>0&&this.curr!==this.total&&(this.isDownloadInterrupted=!0,D.info("根据上次数据继续断点下载")),this.bar=new s.SingleBar({format:"Download [{bar}] {percentage}% | {value}/{total}"},s.Presets.legacy),this.bar.start(this.total,this.curr))}))}getProgress(){return w(this,void 0,void 0,(function*(){let t=[];try{const o=yield e.readFile(this.progressFilePath,{encoding:"utf8"});t=JSON.parse(o)}catch(o){o&&"ENOENT"===o.code&&(yield e.writeFile(this.progressFilePath,JSON.stringify(t),{encoding:"utf8"}))}return t}))}updateProgress(t,o){return w(this,void 0,void 0,(function*(){this.curr!==this.total&&(this.curr=this.curr+1,o&&(this.progressInfo.push(t),yield 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(){var e;this.clearLine(2),null===(e=this.bar)||void 0===e||e.start(this.total,this.curr)}clearLine(e){var t;if(!(e<=0)&&"function"==typeof(null===(t=null===process||void 0===process?void 0:process.stderr)||void 0===t?void 0:t.cursorTo)){process.stderr.cursorTo(0);for(let t=0;t<e;t++)process.stderr.moveCursor(0,-1),process.stderr.clearLine(1)}}}function T({browser:e="chrome",os:t="mac os",device:o="desktop"}){o=o.toLowerCase(),e=e.toLowerCase(),t=t.toLowerCase();let r=n(o,e,t);if("chrome"===e)for(;r.includes("Chrome-Lighthouse")||r.includes("Gener8")||r.includes("HeadlessChrome")||r.includes("SMTBot");)r=n(o,e,t);if("safari"===e)for(;r.includes("Applebot");)r=n(o,e,t);return r}function U(e){return e.toString().padStart(2,"0")}function O(e){const t=new Date(e);if(isNaN(t.getTime()))return"";const o=t.getFullYear(),r=t.getMonth()+1,n=t.getDate(),i=t.getHours(),s=t.getMinutes(),a=t.getSeconds();return`${o}-${U(r)}-${U(n)} ${U(i)}:${U(s)}:${U(a)}`}class x{constructor(e){this.summaryInfo=e}genFile(){return w(this,void 0,void 0,(function*(){const{bookName:t,bookDesc:o,bookPath:r,uuidMap:n}=this.summaryInfo;let i=`# ${t}\n\n`;o&&(i+=`> ${o}\n\n`);let s=i;const a=[];n.forEach((e=>{const t=e.toc,o=t.parent_uuid,r=this.findTree(a,o),n={text:t.title.replace(/[\\/:*?"<>|\n\r]/g,"_").replace(/\s/,""),id:t.uuid,level:1,type:"link"},i=t.type.toLocaleLowerCase();i===I.TITLE||""!==t.child_uuid?(n.type="title","boolean"!=typeof r?(Array.isArray(r.children)||(r.children=[]),n.level=r.level+1,r.children.push(n)):(n.level=1,a.push(n)),i===I.DOC&&(n.link=e.path)):(n.type="link",n.link=i===I.LINK?e.toc.url:e.path,"boolean"!=typeof r?(Array.isArray(r.children)||(r.children=[]),n.level=r.level+1,r.children.push(n)):(n.level=1,a.push(n)))}));s+=this.genSummaryContent(a,"");try{yield e.writeFile(`${r}/index.md`,s)}catch(e){D.error("Generate Summary Error")}return s}))}genSummaryContent(e,t){for(const o of e){if(o.type===I.TITLE)if(o.link){const e=o.link?o.link.replace(/\s/g,"%20"):o.link;t+=`\n${"".padStart(o.level+1,"#")} [${o.text}](${e})\n\n`}else t+=`\n${"".padStart(o.level+1,"#")} ${o.text}\n\n`;else if(o.type===I.LINK){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 o=this.findTree(e.children,t);if(o)return o}return!1}findTree(e,t){if(!t)return!1;for(const o of e){const e=this.findIdItem(o,t);if(e)return e}return!1}}function C(e){const{key:t=N,token:o}=e,r={"user-agent":T({browser:"chrome",device:"desktop"})};return o&&(r.cookie=`${t}=${o};`),r}function j(e){const t={headers:C(e),beforeRedirect:t=>{t.headers=Object.assign(Object.assign({},(null==t?void 0:t.headers)||{}),C(e))}};return"test"===a.NODE_ENV&&(t.proxy=!1),t}const R=(e,t=!0)=>{const{articleUrl:o,bookId:r,token:n,key:i,host:s=P}=e;let a=`${s}/api/docs/${o}`;const l={book_id:String(r),merge_dynamic_data:String(!1)};t&&(l.mode="markdown");const u=new URLSearchParams(l).toString();return a=`${a}?${u}`,c.get(a,j({token:n,key:i})).then((({data:e,status:t})=>({apiUrl:a,httpStatus:t,response:e})))};function _(e){if(!e)return"";return e.replace(/[\\/:*?"<>|\n\r]/g,"_").replace(/\s/g,"").replace(/[\ud800-\udbff][\udc00-\udfff]/g,"")}const F=e=>{if(!e)return"";const t=f.inflate(e,{to:"string"}),o=JSON.parse(t);let r="";return o.forEach((e=>{const t=`## ${e.name}\n`,o=function(e){var t,o,r;let n=Object.keys(e);n=n.filter((t=>Object.keys(e[t]).some((o=>{var r,n;return null===(n=null===(r=null==e?void 0:e[t])||void 0===r?void 0:r[o])||void 0===n?void 0:n.v}))));let i=[];n.forEach((t=>{const o=e[t];o&&(i=i.concat(Object.keys(o)))}));const s=Math.max(...n.map((e=>Number(e)))),a=Math.max(...i.map((e=>Number(e))));if(s<0||a<0)return"";let l="";const c="ABCDEFGHIJKLMNOPQRSTUVWXYZ";let u=Array(a+1).fill(" ").map(((e,t)=>{const o=t%c.length;return c[o]})).join(" | ");u=`| |${u}|`;let d=Array(a+2).fill("---").join(" |");d=`|${d}|`,l=`${u}\n${d}\n`;for(let n=0;n<s+1;n++){const i=[];for(let s=0;s<a+1;s++){const a=(null===(o=null===(t=null==e?void 0:e[n])||void 0===t?void 0:t[s])||void 0===o?void 0:o.v)||null;a&&"string"==typeof a?i.push(a):a&&"object"==typeof a?"image"===(null==a?void 0:a.class)&&(null==a?void 0:a.src)?i.push(`![${null==a?void 0:a.name}'](${null==a?void 0:a.src})`):"checkbox"===(null==a?void 0:a.class)?i.push((null==a?void 0:a.value)?"[x] ":"[ ] "):"link"===(null==a?void 0:a.class)?i.push(`[${null==a?void 0:a.text}](${null==a?void 0:a.url})`):"select"===(null==a?void 0:a.class)&&i.push(null===(r=null==a?void 0:a.value)||void 0===r?void 0:r.join(",")):i.push(null)}l=`${l}${`| ${n+1} | ${i.join(" | ")}|`}\n`}return l}(e.data);r=`${r}\n${t}\n${o}`})),r};function A(e,t=[]){return function(e,t){try{const{host:o,pathname:r}=new URL(e);return!t.includes(o)&&Boolean(r)}catch(e){return!1}}(e,t)?`https://www.yuque.com/api/filetransfer/images?url=${encodeURIComponent(e)}&sign=${function(e){const t=p.createHash("sha256");return t.update(`${L}${e}`),t.digest("hex")}(e)}`:e}const M=y(g.finished);function B(e){return w(this,void 0,void 0,(function*(){const{fileUrl:t,savePath:o,token:r,key:n,fileName:i}=e;return c.get(t,Object.assign(Object.assign({},j({token:r,key:n})),{responseType:"stream"})).then((e=>w(this,void 0,void 0,(function*(){var r,n,s;if(null===(n=null===(r=e.request)||void 0===r?void 0:r.path)||void 0===n?void 0:n.startsWith("/login"))throw new Error(`"${i}" need token`);if(200===e.status){const r=m(o);return null===(s=e.data)||void 0===s||s.pipe(r),M(r).then((()=>({fileUrl:t,savePath:o})))}throw new Error(`response status ${e.status}`)}))))}))}const J=/\[(.*?)\]\((.*?)\)/g,q=/\[(.*?)\]\((.*?\.yuque\.com\/attachments.*?)\)/;function H(e){return w(this,void 0,void 0,(function*(){const{mdData:t,savePath:o,attachmentsDir:n,articleTitle:i,token:s,key:c}=e,d=(t.match(J)||[]).filter((e=>q.test(e)));if(0===d.length)return{mdData:t};const h=u({text:`下载 "${i}" 的附件中...`,stream:l});"test"!==a.NODE_ENV&&h.start();const f=r.resolve(o,n),p=d.map((e=>function(e,t){const[,o,n]=q.exec(e)||[];if(!n)return!1;const i=o||n.split("/").at(-1);if(!i)return!1;const s=r.join(t,i);return{fileName:i,url:n,rawMd:e,currentFilePath:s}}(e,f))).filter((e=>!1!==e));v(f,{recursive:!0});const m=p.map((e=>B({fileUrl:e.url,savePath:e.currentFilePath,token:s,key:c,fileName:e.fileName}))),g=yield Promise.all(m).finally((function(){h&&h.stop()}));let y=t;return g.forEach((e=>{const t=p.find((t=>t.url===e.fileUrl));if(t){const e=`[附件: ${t.fileName}](${n}/${t.fileName})`;y=y.replace(t.rawMd,e)}})),{mdData:y}}))}function K(e){const t=[];return V(e,(function(e,o){"link"===e.type&&t.push({node:e,keyChain:o})})),t}function V(e,t,o=[]){t(e,o),Array.isArray(e.children)&&(o.push("children"),e.children.forEach(((e,r)=>{V(e,t,[...o,String(r)])})))}const W=/name="audio" value="data:(.*?audioId.*?)".*?><\/card>/gm;function G(e){return w(this,void 0,void 0,(function*(){const{mdData:t,htmlData:o,savePath:n,attachmentsDir:i,articleTitle:s,token:c,key:d}=e,h=function(e){return k(e)}(t),f=K(h).filter((e=>/_lake_card.*?videoId/.test(e.node.url))),p=function(e){const t=e.match(W)||[];try{return t.map((e=>e.replace(W,"$1"))).map((e=>JSON.parse(decodeURIComponent(e))))}catch(e){return[]}}(o);if(0===f.length&&0===p.length)return{mdData:t};const m=u({text:`下载 "${s}" 的音视频中...`,stream:l});"test"!==a.NODE_ENV&&m.start();const g=r.resolve(n,i);v(g,{recursive:!0});let y=t;try{if(f.length>0){const t=yield function(e,t,o){return w(this,void 0,void 0,(function*(){const{key:n,token:i}=t,s=e.map((e=>w(this,void 0,void 0,(function*(){var t,s;const a=function(e){var t;try{const o=null!==(t=new URL(e).searchParams.get("_lake_card"))&&void 0!==t?t:"",r=decodeURIComponent(o),n=JSON.parse(r);return{name:(null==n?void 0:n.name)||"",videoId:(null==n?void 0:n.videoId)||""}}catch(e){return!1}}(e.node.url);if(!a)return!1;const l=yield Q({videoId:a.videoId,key:n,token:i});if(!l)return!1;const c=null!==(s=null!==(t=a.name)&&void 0!==t?t:a.videoId.split("/").at(-1))&&void 0!==s?s:a.videoId;return{videoInfo:Object.assign(Object.assign({},a),l),astNode:e,fileName:c,currentFilePath:r.join(o,c)}}))));return(yield Promise.all(s)).filter(Y)}))}(f,e,g),o=t.map((e=>B({fileUrl:e.videoInfo.video,savePath:e.currentFilePath,token:c,key:d,fileName:e.videoInfo.name})));(yield Promise.all(o)).forEach((e=>{const o=t.find((t=>t.videoInfo.video===e.fileUrl));o&&(o.astNode.node.url=`${i}${r.sep}${o.fileName}`,o.astNode.node.children=[{type:"text",value:`音视频附件: ${o.videoInfo.name}`}])})),y=function(e){return $(e)}(h)}if(p.length>0){const t=yield function(e,t,o){return w(this,void 0,void 0,(function*(){const{key:n,token:i}=t,s=e.map((e=>w(this,void 0,void 0,(function*(){var t;const s=yield Q({videoId:e.audioId,key:n,token:i});if(!s)return!1;const a=null!==(t=null==e?void 0:e.fileName)&&void 0!==t?t:e.id;return{audioInfo:Object.assign(Object.assign({},e),s),fileName:a,currentFilePath:r.join(o,a)}}))));return(yield Promise.all(s)).filter(Y)}))}(p,e,g),o=t.map((e=>B({fileUrl:e.audioInfo.audio,savePath:e.currentFilePath,token:c,key:d,fileName:e.audioInfo.fileName}))),n=yield Promise.all(o);let s="\n\n> [yuque-dl warn]: 由于语雀markdown接口限制, 无法准确定位音频文件在文档中所在位置, 所以统一所有音频放到一起\n";n.forEach((e=>{const o=t.find((t=>t.audioInfo.audio===e.fileUrl));o&&(s+=`> - [音视频附件: ${o.audioInfo.fileName}](${i}${r.sep}${o.fileName})\n`)})),y+=s}}finally{m&&m.stop()}return{mdData:y}}))}function Q(e){let t="https://www.yuque.com/api/video";const{videoId:o,token:r,key:n}=e,i=new URLSearchParams;return i.set("video_id",o),t=`${t}?${i.toString()}`,c.get(t,j({token:r,key:n})).then((({data:e,status:t})=>{const o=e.data;return 200===t&&"success"===o.status&&o.info})).catch((()=>!1))}function Y(e){return!!e}function z(e){return w(this,void 0,void 0,(function*(){var o,r,n,i,s,c,h,f,p,m;const{articleInfo:v,progressBar:g,options:y}=e,{token:k,key:$}=y,{bookId:w,itemUrl:I,savePath:N,saveFilePath:P,uuid:L,articleUrl:D,articleTitle:S,ignoreImg:T,host:U,imageServiceDomains:x}=v,C={articleUrl:I,bookId:w,token:k,host:U,key:$},{httpStatus:j,apiUrl:M,response:B}=yield R(C),J=null===(r=null===(o=null==B?void 0:B.data)||void 0===o?void 0:o.type)||void 0===r?void 0:r.toLocaleLowerCase();let q="";if(J===b.SHEET){const{response:e}=yield R(C,!1);try{const t=null===(n=null==e?void 0:e.data)||void 0===n?void 0:n.content,o=t?JSON.parse(t):{},r=null==o?void 0:o.sheet;q=r?F(r):""}catch(e){const t=E.get(J);throw new Error(`download article Error: “${t}”解析错误 ${e}`)}}else{if([b.BOARD,b.TABLE].includes(J)){const e=E.get(J);throw new Error(`download article Error: 暂不支持“${e}”的文档`)}if("string"!=typeof(null===(i=null==B?void 0:B.data)||void 0===i?void 0:i.sourcecode))throw new Error(`download article Error: ${M}, http status ${j}`);q=B.data.sourcecode,q=function(e){const t=/!\[(.*?)\]\((http.*?latex.*?)\)/gm,o=e.match(t);let r=e;const n=e;try{null==o||o.forEach((e=>{var o,n;t.lastIndex=0;const i=null!==(n=null===(o=t.exec(e))||void 0===o?void 0:o[2])&&void 0!==n?n:"",{pathname:s,search:a}=new URL(i);if(!s.endsWith(".svg")&&a){const t=decodeURIComponent(a);r=r.replace(e,t.slice(1))}}))}catch(e){return n}return r}(q)}const K=function(e){if(!e)return[];const t=/!\[(.*?)\]\((.*?)\)/gm;let o=Array.from(e.match(t)||[]);return o=o.map((e=>(e=e.replace(t,"$2"),/^http.*/g.test(e)?e:""))).filter((e=>Boolean(e))),o}(q),V=null!==(h=null===(c=null===(s=(yield R(C,!1)).response)||void 0===s?void 0:s.data)||void 0===c?void 0:c.content)&&void 0!==h?h:"";K.length&&!T&&(q=function(e,t,o){if(!o)return t;const r=/<card.*?name="image".*?value="data:(.*?)">(.*?)<\/card>/gm,n=[];let i,s=!0;for(;s||Boolean(i);)if(s=!1,i=r.exec(o),null==i?void 0:i[1])try{const e=decodeURIComponent(i[1]),t=JSON.parse(e);n.push((null==t?void 0:t.src)||"")}catch(e){n.push("")}const a=new Map;return e.forEach((e=>{const{origin:o,pathname:r}=new URL(e),i=`${o}${r}`,s=n.find(((e,t)=>{const o=new RegExp(`${i}.*?`).test(e);return o&&n.splice(t,1),o}));if(s){const o=new RegExp(e,"g"),r=a.get(e)||0;let n=0;t=t.replace(o,(e=>{let t=e;return n===r&&(t=s),n+=1,t})),a.set(e,r+1)}})),t}(K,q,V));const W={toc:y.toc,articleTitle:S,articleUrl:D,articleUpdateTime:O(null!==(p=null===(f=null==B?void 0:B.data)||void 0===f?void 0:f.content_updated_at)&&void 0!==p?p:"")},Q=[];try{g.pause(),console.log("");q=(yield H({mdData:q,savePath:N,attachmentsDir:`./attachments/${_(L)}`,articleTitle:S,token:k,key:$})).mdData}catch(e){Q.push(`附件下载失败: ${e.message||"unknown error"}`)}finally{g.continue()}try{g.pause(),console.log("");q=(yield G({mdData:q,htmlData:V,savePath:N,attachmentsDir:`./attachments/${_(L)}`,articleTitle:S,token:k,key:$})).mdData}catch(e){Q.push(`音视频下载失败: ${e.message||"unknown error"}`)}finally{g.continue()}if(K.length&&!T){let e;g.pause(),console.log(""),"test"!==a.NODE_ENV&&(e=u({text:`下载 "${S}" 的图片中...`,stream:l}),e.start());let o=[],r=q;try{const e=yield d.run(q,{dist:N,imgDir:`./img/${L}`,isIgnoreConsole:!0,errorStillReturn:!0,referer:D||"",transform:e=>A(e=e.replace("x-oss-process=image%2Fwatermark%2C",""),x),timeout:18e4});o=e.errorInfo,r=e.data}catch(e){o=[e]}q=r;const n=()=>{e&&e.stop(),g.continue()};if(o.length>0){const e=o[0];let r="图片下载失败(失败的以远程链接保存): ";throw r=e.url?`${r}${null===(m=e.error)||void 0===m?void 0:m.message} ${e.url.slice(0,20)}...`:`${r}${e.message}`,yield t(P,X(q,W)),n(),new Error(`${r}`)}n()}try{if(yield t(P,X(q,W)),Q.length>0)throw new Error(Q[0]);return!0}catch(e){throw new Error(`${e.message}`)}}))}function X(e,t){const{articleTitle:o,articleUrl:r,toc:n}=t;let i=e;i=i.replace(/<a.*?>(\s*?)<\/a>/gm,"");const s=o?`# ${o}\n\n`:"";let a=n?h(i).content:"";a&&(a=`${a}\n\n---\n\n`);let l="\n\n";return t.articleUpdateTime&&(l+=`> 更新: ${t.articleUpdateTime} \n`),r&&(l+=`> 原文: <${r}>`),i=`${s}${a}${i}${l}`,i}function Z(e,t){return w(this,void 0,void 0,(function*(){if(!function(e){if("function"==typeof URL.canParse)return URL.canParse(e);try{return new URL(e),!0}catch(e){return!1}}(e))throw new Error("Please enter a valid URL");const{bookId:n,tocList:i,bookName:s,bookDesc:a,bookSlug:l,host:u,imageServiceDomains:d}=yield((e,t)=>{const o=/decodeURIComponent\("(.+)"\)\);/m;return c.get(e,j(t)).then((({data:e="",status:t})=>200===t?e:"")).then((e=>{var t,r;const n=null!==(t=o.exec(e))&&void 0!==t?t:"";if(!n[1])return{};const i=JSON.parse(decodeURIComponent(n[1]));return i.book?{bookId:i.book.id,bookSlug:i.book.slug,tocList:i.book.toc||[],bookName:i.book.name||"",bookDesc:i.book.description||"",host:(null===(r=i.space)||void 0===r?void 0:r.host)||P,imageServiceDomains:i.imageServiceDomains||[]}:{}})).catch((e=>{var t;const o=null!==(t=null==e?void 0:e.message)&&void 0!==t?t:"";if(!o)throw new Error("unknown error");if(["getaddrinfo ENOTFOUND","read ECONNRESET","Client network socket disconnected before secure TLS connection was established"].some((e=>o.startsWith(e))))throw new Error("请检查网络(是否正常联网/是否开启了代理软件)");throw new Error(o)}))})(e,{token:t.token,key:t.key});if(!n)throw new Error("No found book id");if(!i||0===i.length)throw new Error("No found toc list");const h=r.resolve(t.distDir,s?_(s):String(n));yield o(h,{recursive:!0});const f=i.length,p=new S(h,f);if(yield p.init(),p.curr===f)return p.bar&&p.bar.stop(),void D.info(`√ 已完成: ${h}`);const m=new Map;p.isDownloadInterrupted&&p.progressInfo.forEach((e=>{m.set(e.toc.uuid,e)}));const v=e.replace(new RegExp(`(.*?/${l}).*`),"$1");yield function(e){return w(this,void 0,void 0,(function*(){const{articleUrlPrefix:t,total:n,uuidMap:i,tocList:s,bookPath:a,bookId:l,progressBar:c,host:u,options:d,imageServiceDomains:h=[]}=e;let f=0,p=0,m=0;const v=[],g=[];for(let e=0;e<n;e++){const t=s[e];if("string"!=typeof t.type)continue;if(i.get(t.uuid))continue;const r=t.type.toLocaleLowerCase();if(r===I.TITLE||""!==t.child_uuid||r===I.LINK){let e=t;const n=[],s=[];for(;e;)n.unshift(_(e.title)),s.unshift(e.uuid),e=i.get(e.parent_uuid)?i.get(e.parent_uuid).toc:void 0;const l={path:n.map(_).join("/"),pathTitleList:n,pathIdList:s,toc:t};r===I.LINK?(m+=1,g.push(l)):yield o(`${a}/${n.map(_).join("/")}`,{recursive:!0}),i.set(t.uuid,l),r===b.DOC?yield y(t):yield c.updateProgress(l,r!==I.LINK)}else t.url&&(yield y(t))}function y(e){return w(this,void 0,void 0,(function*(){p+=1;let o={path:"",pathTitleList:[],pathIdList:[]};const n=e.type.toLocaleLowerCase();i.get(e.parent_uuid)&&(o=i.get(e.parent_uuid));const s=_(e.title),m=[...o.pathTitleList,s],g=[...o.pathIdList,e.uuid];let y=[...o.pathTitleList,`${s}.md`].map(_).join("/"),k=o.pathTitleList.map(_).join("/");n===b.DOC&&e.child_uuid&&(y=[...o.pathTitleList,s,"index.md"].map(_).join("/"),k=m.map(_).join("/"));const $={path:y,savePath:k,pathTitleList:m,pathIdList:g,toc:e};let w=!0;const I=`${t}/${e.url}`;try{const t={bookId:l,itemUrl:e.url,savePath:r.resolve(a,$.savePath),saveFilePath:r.resolve(a,$.path),uuid:e.uuid,articleUrl:I,articleTitle:e.title,ignoreImg:d.ignoreImg,host:u,imageServiceDomains:h};yield z({articleInfo:t,progressBar:c,options:d})}catch(e){w=!1,f+=1,v.push({articleUrl:I,errItem:$,errMsg:e.message,err:e})}i.set(e.uuid,$),yield c.updateProgress($,w)}))}if(m>0){D.warn("该知识库存在以下外链文章");for(const e of g)D.warn(`———— ✕ ${e.path} ${e.toc.url}`)}if(f>0){D.error(`本次执行总数${p}篇,✕ 失败${f}篇`);for(const e of v)D.error(`《${e.errItem.path}》: ${e.articleUrl}`),e.errMsg.split("\n").forEach((e=>{D.error(`———— ✕ ${e}`)}));D.error("o(╥﹏╥)o 由于网络波动或链接失效以上下载失败,可重新执行命令重试(PS:不会影响已下载成功的数据)")}}))}({articleUrlPrefix:v,total:f,uuidMap:m,tocList:i,bookPath:h,bookId:n,progressBar:p,host:u,options:t,imageServiceDomains:d});const g=new x({bookPath:h,bookName:s,bookDesc:a,uuidMap:m});yield g.genFile(),D.info(`√ 生成目录 ${r.resolve(h,"index.md")}`),p.curr===f&&D.info(`√ 已完成: ${h}`)}))}export{w as _,D as l,Z as m};
package/dist/es/index.js CHANGED
@@ -1 +1 @@
1
- export{m as main}from"./index-kkT1fPB8.js";import"node:fs/promises";import"node:path";import"rand-user-agent";import"log4js";import"cli-progress";import"node:process";import"axios";import"ora";import"pull-md-img";import"markdown-toc";import"pako";import"node:crypto";import"node:fs";import"node:stream";import"node:util";import"mdast-util-from-markdown";import"mdast-util-to-markdown";
1
+ export{m as main}from"./index-CeluBAOZ.js";import"node:fs/promises";import"node:path";import"rand-user-agent";import"log4js";import"cli-progress";import"node:process";import"axios";import"ora";import"pull-md-img";import"markdown-toc";import"pako";import"node:crypto";import"node:fs";import"node:stream";import"node:util";import"mdast-util-from-markdown";import"mdast-util-to-markdown";
package/package.json CHANGED
@@ -1,27 +1,11 @@
1
1
  {
2
2
  "name": "yuque-dl",
3
- "version": "1.0.70",
3
+ "version": "1.0.71",
4
4
  "description": "yuque 知识库下载",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "yuque-dl": "bin/index.js"
8
8
  },
9
- "scripts": {
10
- "dev": "pnpm run build:bundle -w",
11
- "build": "run-s build:**",
12
- "build:bundle": "rollup -c rollup.config.ts --configPlugin typescript",
13
- "build:types": "tsc --emitDeclarationOnly --outDir types -p tsconfig.base.json",
14
- "clean": "rm -rf dist types",
15
- "np": "np",
16
- "eslintLog": "eslint . > eslint.log",
17
- "eslintFix": "eslint --fix .",
18
- "changelog:gen": "conventional-changelog -p angular -i CHANGELOG.md -s -r 2",
19
- "changelog:commit": "git add CHANGELOG.md && git commit CHANGELOG.md -m 'chore: update changelog' && git push",
20
- "release": "run-s clean build np changelog:gen changelog:commit",
21
- "test": "vitest --run",
22
- "test:ui": "vitest --ui --coverage.enabled=true",
23
- "test:coverage": "vitest run --coverage"
24
- },
25
9
  "repository": {
26
10
  "type": "git",
27
11
  "url": "git+https://github.com/gxr404/yuque-dl.git"
@@ -72,12 +56,28 @@
72
56
  "mdast-util-to-markdown": "^2.1.0",
73
57
  "ora": "^7.0.1",
74
58
  "pako": "1.0.11",
75
- "pull-md-img": "^0.0.60",
59
+ "pull-md-img": "^0.0.65",
76
60
  "rand-user-agent": "1.0.109",
77
61
  "semver": "^7.6.0",
78
62
  "vitepress": "^1.2.2"
79
63
  },
80
64
  "engines": {
81
65
  "node": ">=18.4.0"
66
+ },
67
+ "scripts": {
68
+ "dev": "pnpm run build:bundle -w",
69
+ "build": "run-s build:**",
70
+ "build:bundle": "rollup -c rollup.config.ts --configPlugin typescript",
71
+ "build:types": "tsc --emitDeclarationOnly --outDir types -p tsconfig.base.json",
72
+ "clean": "rm -rf dist types",
73
+ "np": "np",
74
+ "eslintLog": "eslint . > eslint.log",
75
+ "eslintFix": "eslint --fix .",
76
+ "changelog:gen": "conventional-changelog -p angular -i CHANGELOG.md -s -r 2",
77
+ "changelog:commit": "git add CHANGELOG.md && git commit CHANGELOG.md -m 'chore: update changelog' && git push",
78
+ "release": "run-s clean build np changelog:gen changelog:commit",
79
+ "test": "vitest --run",
80
+ "test:ui": "vitest --ui --coverage.enabled=true",
81
+ "test:coverage": "vitest run --coverage"
82
82
  }
83
- }
83
+ }