diff --git a/docs/programming.md b/docs/programming.md
index f6744470ca..8f79869164 100644
--- a/docs/programming.md
+++ b/docs/programming.md
@@ -4,6 +4,14 @@ pageClass: routes
# 编程
+## AlgoCasts
+
+### 视频更新
+
+
+
+> AlgoCasts 需要付费订阅, RSS 仅做更新提醒, 不含付费内容.
+
## Dockone
### 周报
diff --git a/lib/router.js b/lib/router.js
index 9365717103..5e4017e01d 100644
--- a/lib/router.js
+++ b/lib/router.js
@@ -1345,6 +1345,9 @@ router.get('/steamgifts/discussions/:category?', require('./routes/steamgifts/di
// Steamgifts
router.get('/zzz', require('./routes/zzz/index'));
+// AlgoCasts
+router.get('/algocasts', require('./routes/algocasts/all'));
+
// aqicn
router.get('/aqicn/:city', require('./routes/aqicn/index'));
diff --git a/lib/routes/algocasts/all.js b/lib/routes/algocasts/all.js
new file mode 100644
index 0000000000..cb3fd192d0
--- /dev/null
+++ b/lib/routes/algocasts/all.js
@@ -0,0 +1,98 @@
+const axios = require('@/utils/axios');
+const cheerio = require('cheerio');
+
+const getCacheParsed = async (ctx, key) => {
+ const value = await ctx.cache.get(key);
+ if (value) {
+ return JSON.parse(value);
+ }
+};
+
+const generateResponse = async (ctx, items) => ({
+ // 源标题
+ title: 'AlgoCasts',
+ // 源链接
+ link: `https://www.algocasts.com`,
+ // 源说明
+ description: `AlgoCasts 旨在用心做好每一个算法讲解视频。每个视频包含两个部分:题目的剖析讲解以及编码,力求在讲解清楚到位的基础上,尽可能地保持视频精简短小,让大家可以在碎片时间里进行学习,并收获这些算法题背后的思想与乐趣。`,
+ item: await Promise.all(
+ items.map(async (item) => ({
+ // 文章标题
+ title: `${item.title} | AlgoCasts 视频更新`,
+ // 文章正文
+ description: item.description || '',
+ // 文章链接
+ link: item.link,
+ }))
+ ),
+});
+
+const makeFull = async (ctx, infos) => {
+ const limit = 10; // 仅获取最近十条信息(避免无意义请求)
+ infos = infos.slice(0, limit);
+
+ return await Promise.all(
+ infos.map(async (item) => {
+ const cacheKey = `episode-${item.episode}`;
+
+ // 尝试从缓存查找相关数据
+ let info = await getCacheParsed(ctx, cacheKey);
+ // console.log(info);
+ if (info) {
+ // 找到 -> 直接返回
+ return info;
+ } else {
+ // 未找到 -> 返回
+ info = item;
+ }
+ // 抓取详细信息
+ const response = await axios({
+ method: 'get',
+ url: info.link,
+ });
+ const $ = cheerio.load(response.data);
+
+ const badges = [];
+ $('.badge').each((index, badge) => {
+ badges.push($(badge).text());
+ });
+
+ info.description = `
${info.title}
${badges.join(' | ')}
${$('#my-content p').html()}
`;
+ ctx.cache.set(cacheKey, info);
+
+ // console.log(info);
+
+ return info;
+ })
+ );
+};
+
+module.exports = async (ctx) => {
+ const response = await axios({
+ method: 'get',
+ url: 'http://algocasts.io/episodes',
+ });
+
+ const $ = cheerio.load(response.data);
+ const infos = [];
+
+ $('tr')
+ .slice(1)
+ .each((i, e) => {
+ const id = parseInt(
+ $(e)
+ .find('th')
+ .text()
+ );
+ const titleLabel = $(e).find('td a');
+ const title = `${id}. ${titleLabel.text()}`;
+ const episode = titleLabel
+ .attr('href')
+ .trim()
+ .split('/')[2];
+ const link = `http://algocasts.io/episodes/${episode}`;
+
+ infos.push({ id, title, episode, link });
+ });
+ ctx.state.data = await generateResponse(ctx, await makeFull(ctx, infos));
+};