接下来,封装简单的课程下载:
ProductLoader, 传入一个product的信息,他就会自动给你下载课程原有的元数据, 对是 元数据, 还不能在浏览器里面看。
class ProductLoader {
constructor(proudct) {
/**
* 产品
*/
this.product = proudct;
/**
* 专栏id
*/
this.cid = null;
/**
* 全部文章
*/
this.articles = [];
/**
* 全部文章内容
*/
this.articlesContents = [];
/**
* 章节信息
*/
this.chapters = null;
}
getActiles(pid) {
// 获取全部文章
return request(API_URL.articles, {
cid: `${pid}`, // 产品id
order: "earliest",
prev: 0,
sample: false,
size: 200
}, {
"referrer": `https://time.geekbang.org/column/article/0?cid=${pid}`
});
}
async getArticlesContents(articles = []) {
const articlesContents = [];
const len = articles.length;
for (let i = 0; i < len; i++) {
const p = articles[i];
try {
const resArticle = await request(API_URL.article, {
id: p.id,
include_neighbors: false,
is_freelyread: true
});
await delay().run();
console.log("get acticle success:", resArticle.data.id);
articlesContents.push(resArticle.data);
console.log("articlesContents", articlesContents.length);
// break;
} catch (err) {
return articlesContents;
// await delay().run();
// console.log(`downdload article failed: artile id ${p.id}`);
// continue;
}
}
return articlesContents;
}
getChapters(cid) {
return request(API_URL.chapters, {
cid
});
}
async getJSONData() {
try {
const resAticles = await this.getActiles(this.product.id);
if (resAticles.code !== 0) {
return console.log("获取全部文章失败");
}
const cid = resAticles.data.list[0].column_id;
const articles = resAticles.data.list;
this.cid = cid;
// 全部文章
this.articles = articles;
const resChapters = await this.getChapters(cid);
if (resChapters.code !== 0) {
return console.log("fetch chapters failed");
}
// 章节信息
this.chapters = resChapters.data;
// 文章内容
this.articlesContents = await this.getArticlesContents(articles);
} catch (err) {
console.log("download product failed, product id", this.product.id);
throw err
}
}
async zipDownload() {
try {
await this.getJSONData();
const zip = new JSZip();
zip.file("product.json", JSON.stringify(this.product));
zip.file("articles.json", JSON.stringify(this.articles));
zip.file("chapters.json", JSON.stringify(this.chapters));
const folder = zip.folder("articles");
this.articlesContents.forEach(a => {
folder.file(`${a.id}.json`, JSON.stringify(a));
})
const blob = await zip.generateAsync({ type: "blob" });
downloadBlob(this.product.id + ".zip", blob);
} catch (err) {
console.log("zipDownload error,", err);
}
}
}
最后调整一下入口代码, 运行一下,就可以 某个课程的元数据了。
; (async function init() {
try {
// 获取已购买的产品
const resProducts = await request(API_URL.product, {
"desc": true,
"expire": 1,
"last_learn": 0,
"learn_status": 0,
"prev": 0,
"size": 50,
"sort": 1,
"type": "",
"with_learn_count": 1
});
if (resProducts.code != 0 || resProducts.data.list.length < 0) {
return console.log("穷光蛋,没有购买任何产品");
}
console.log("准备检查和注入JSZip");
if (!checkScript({
names: "jszip.min.js",
objectName: "JSzip",
})) {
await injectScript("https://cdn.bootcdn.net/ajax/libs/jszip/3.5.0/jszip.min.js");
console.log("注入JSZip成功");
}
injectControlPanel(resProducts.data.products);
download("product.json", JSON.stringify(resProducts.data))
} catch (err) {
console.log("脚本执行异常", err);
}
})();
到此为止, 我们回顾一下,zip包的数据接口,因为这和接下来的预览网站相关。
[productId]
product.json // 课程信息
chapters.json // 章节信息
artilces.json // 文章摘要信息
artilces
// 文章沐浴露
[article.id].json // 具体的文章
comments // 代码并未下载,自行添加
[article.id].json
搭建预览网站
这里分两步走搭建课程列表,文章详情。
先看一下文件接口
images // 图片目录,之后下载图片和MP4有用
[productId]
[img-1]
products // 产品列表
[productId]
artiles
[artileId].json
acticles.json
chapters.json
product.json
index.html // 列表页
article.html // 文章页
product.json // 产品列表
搭建预览网站 - 课程列表
代码非常的简单,预览
<ul>
</ul>
<script>
function getJSONData(url) {
return fetch(url).then(res => res.json())
};
const plEl = document.getElementById("product-list");
(async () => {
try {
const res = await getJSONData("./product.json");
res.products.forEach(p=>{
const pEl = document.createElement("div");
pEl.innerHTML = `
<li> <a href="http://www.likecs.com/article.html?id=${p.id}">
${p.title}
</a>
</li>
`
plEl.appendChild(pEl);
});
} catch (err) {
console.log("读取产品列表失败", err);
}
})();
</script>