rsshub 1.0.0-master.ff3a9ce → 1.0.0-master.ff3ea5a
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/lib/config.ts +38 -0
- package/lib/errors/index.tsx +3 -2
- package/lib/middleware/debug.ts +6 -4
- package/lib/middleware/header.ts +4 -2
- package/lib/registry.test.ts +24 -1
- package/lib/registry.ts +40 -11
- package/lib/router.js +1 -1
- package/lib/routes/121/namespace.ts +9 -0
- package/lib/routes/121/templates/description.art +17 -0
- package/lib/routes/121/weather-live.ts +104 -0
- package/lib/routes/141jav/index.ts +3 -0
- package/lib/routes/141ppv/index.ts +3 -0
- package/lib/routes/163/news/rank.ts +1 -1
- package/lib/routes/18comic/album.ts +59 -64
- package/lib/routes/18comic/index.ts +1 -0
- package/lib/routes/18comic/search.ts +53 -4
- package/lib/routes/18comic/utils.ts +70 -1
- package/lib/routes/2048/index.ts +1 -0
- package/lib/routes/30secondsofcode/utils.ts +0 -1
- package/lib/routes/6park/index.ts +1 -1
- package/lib/routes/7mmtv/index.ts +1 -0
- package/lib/routes/91porn/index.ts +1 -0
- package/lib/routes/95mm/tab.ts +1 -0
- package/lib/routes/agefans/detail.ts +1 -1
- package/lib/routes/aliyun/database-month.ts +1 -1
- package/lib/routes/anthropic/research.ts +112 -0
- package/lib/routes/apnews/topics.ts +2 -1
- package/lib/routes/apnews/utils.ts +1 -1
- package/lib/routes/apple/design.ts +54 -0
- package/lib/routes/apple/namespace.ts +1 -1
- package/lib/routes/aqara/post.ts +1 -1
- package/lib/routes/augmentcode/blog.ts +161 -0
- package/lib/routes/augmentcode/namespace.ts +9 -0
- package/lib/routes/augmentcode/templates/description.art +17 -0
- package/lib/routes/azul/namespace.ts +9 -0
- package/lib/routes/azul/packages.ts +105 -0
- package/lib/routes/banshujiang/index.ts +763 -0
- package/lib/routes/banshujiang/namespace.ts +9 -0
- package/lib/routes/banshujiang/templates/description.art +17 -0
- package/lib/routes/bfl/announcements.ts +1 -1
- package/lib/routes/bilibili/cache.ts +78 -0
- package/lib/routes/bilibili/danmaku.ts +1 -1
- package/lib/routes/bilibili/dynamic.ts +8 -2
- package/lib/routes/bilibili/video.ts +31 -73
- package/lib/routes/biquge/index.ts +1 -1
- package/lib/routes/bsky/keyword.ts +15 -10
- package/lib/routes/capitalmind/insights.ts +40 -0
- package/lib/routes/capitalmind/namespace.ts +8 -0
- package/lib/routes/capitalmind/podcasts.ts +41 -0
- package/lib/routes/capitalmind/utils.ts +122 -0
- package/lib/routes/cartoonmad/comic.ts +1 -1
- package/lib/routes/chikubi/index.ts +1 -0
- package/lib/routes/chikubi/tag.ts +1 -0
- package/lib/routes/chocolatey/namespace.ts +9 -0
- package/lib/routes/chocolatey/packages.ts +122 -0
- package/lib/routes/cmde/index.ts +1 -1
- package/lib/routes/cockroachlabs/blog.ts +113 -0
- package/lib/routes/cockroachlabs/namespace.ts +7 -0
- package/lib/routes/cool18/index.ts +4 -1
- package/lib/routes/coomer/index.ts +12 -6
- package/lib/routes/coomer/namespace.ts +1 -1
- package/lib/routes/copymanga/comic.ts +7 -9
- package/lib/routes/css-tricks/articles.ts +48 -0
- package/lib/routes/css-tricks/collections.ts +62 -0
- package/lib/routes/css-tricks/namespace.ts +8 -0
- package/lib/routes/css-tricks/popular.ts +39 -0
- package/lib/routes/css-tricks/utils.ts +103 -0
- package/lib/routes/cursor/changelog.ts +26 -17
- package/lib/routes/cursor/namespace.ts +1 -1
- package/lib/routes/cw/utils.ts +6 -13
- package/lib/routes/dealstreetasia/home.ts +1 -1
- package/lib/routes/dedao/index.ts +3 -3
- package/lib/routes/deepl/blog.ts +422 -0
- package/lib/routes/deepl/namespace.ts +9 -0
- package/lib/routes/deepl/templates/description.art +21 -0
- package/lib/routes/deeplearning/the-batch.ts +1 -1
- package/lib/routes/dev.to/guides.ts +100 -0
- package/lib/routes/dev.to/namespace.ts +8 -0
- package/lib/routes/dev.to/top.ts +105 -0
- package/lib/routes/dgut/jwb.ts +73 -0
- package/lib/routes/dgut/namespace.ts +9 -0
- package/lib/routes/digitalpolicyalert/activity-tracker.ts +135 -0
- package/lib/routes/digitalpolicyalert/namespace.ts +9 -0
- package/lib/routes/dnaindia/common.ts +26 -8
- package/lib/routes/douban/other/list.ts +3 -3
- package/lib/routes/e-hentai/index.ts +4 -1
- package/lib/routes/ehentai/tag.ts +1 -0
- package/lib/routes/englishhome/index.ts +55 -0
- package/lib/routes/englishhome/namespace.ts +6 -0
- package/lib/routes/epicgames/index.ts +2 -2
- package/lib/routes/espn/news.ts +1 -1
- package/lib/routes/expats/czech-news.ts +272 -0
- package/lib/routes/expats/namespace.ts +9 -0
- package/lib/routes/fanbox/index.ts +1 -0
- package/lib/routes/fanbox/utils.ts +1 -1
- package/lib/routes/fansly/tag.ts +1 -0
- package/lib/routes/fantia/search.ts +1 -1
- package/lib/routes/fantube/creator.ts +69 -0
- package/lib/routes/fantube/namespace.ts +7 -0
- package/lib/routes/fantube/templates/post.art +17 -0
- package/lib/routes/fantube/types.ts +100 -0
- package/lib/routes/fantube/utils.ts +268 -0
- package/lib/routes/follow/profile.ts +1 -1
- package/lib/routes/followin/news.ts +1 -1
- package/lib/routes/foresightnews/util.ts +1 -1
- package/lib/routes/freexcomic/book.ts +4 -1
- package/lib/routes/gamer/hot.ts +5 -5
- package/lib/routes/gaoyu/blog.ts +145 -0
- package/lib/routes/gaoyu/namespace.ts +9 -0
- package/lib/routes/gaoyu/templates/description.art +7 -0
- package/lib/routes/geocaching/blogs.ts +2 -2
- package/lib/routes/github/namespace.ts +1 -1
- package/lib/routes/github/private-feed.ts +229 -0
- package/lib/routes/github/star.ts +1 -1
- package/lib/routes/google/extension.ts +9 -10
- package/lib/routes/gov/beijing/bphc/index.ts +1 -1
- package/lib/routes/gov/ccdi/index.ts +1 -1
- package/lib/routes/gov/chongqing/sydwgkzp.ts +22 -6
- package/lib/routes/gov/customs/list.ts +1 -1
- package/lib/routes/gov/general/general.ts +1 -1
- package/lib/routes/gov/hangzhou/zwfw.ts +1 -1
- package/lib/routes/gov/pbc/goutongjiaoliu.ts +1 -1
- package/lib/routes/gov/shenzhen/szlh/index.ts +77 -0
- package/lib/routes/gov/shenzhen/szlh/namespace.ts +8 -0
- package/lib/routes/grist/utils.ts +5 -4
- package/lib/routes/hanime1/search.ts +4 -2
- package/lib/routes/hellogithub/report.ts +1 -1
- package/lib/routes/hit/hitgs.ts +240 -54
- package/lib/routes/hit/templates/description.art +7 -0
- package/lib/routes/hlju/namespace.ts +8 -0
- package/lib/routes/hlju/news.ts +141 -0
- package/lib/routes/hostmonit/cloudflareyes.ts +1 -1
- package/lib/routes/hotukdeals/index.ts +1 -1
- package/lib/routes/hyperdash/namespace.ts +7 -0
- package/lib/routes/hyperdash/templates/description.art +34 -0
- package/lib/routes/hyperdash/top-traders.ts +84 -0
- package/lib/routes/hyperdash/utils.ts +49 -0
- package/lib/routes/hypergryph/arknights/announce.ts +1 -1
- package/lib/routes/ielts/index.ts +1 -1
- package/lib/routes/infoq/topic.ts +16 -13
- package/lib/routes/instagram/web-api/index.ts +17 -13
- package/lib/routes/instagram/web-api/utils.ts +46 -63
- package/lib/routes/jamesclear/book-summaries.ts +40 -0
- package/lib/routes/jamesclear/great-speeches.ts +40 -0
- package/lib/routes/jamesclear/namespace.ts +8 -0
- package/lib/routes/jamesclear/quotes.ts +40 -0
- package/lib/routes/jamesclear/three-two-one.ts +41 -0
- package/lib/routes/jamesclear/utils.ts +22 -0
- package/lib/routes/japanpost/utils.ts +2 -2
- package/lib/routes/javbus/index.ts +3 -0
- package/lib/routes/javdb/tags.ts +1 -0
- package/lib/routes/javlibrary/star.ts +1 -0
- package/lib/routes/javtiful/actress.ts +3 -0
- package/lib/routes/javtiful/channel.ts +3 -0
- package/lib/routes/javtrailers/casts.ts +3 -0
- package/lib/routes/jetbrains/comments.ts +99 -0
- package/lib/routes/jetbrains/namespace.ts +8 -0
- package/lib/routes/jimmyspa/books.ts +1 -1
- package/lib/routes/jimmyspa/news.ts +1 -1
- package/lib/routes/jpxgmn/tab.ts +3 -0
- package/lib/routes/kakuyomu/works.ts +3 -0
- package/lib/routes/kaopu/news.ts +1 -1
- package/lib/routes/kemono/const.ts +1 -1
- package/lib/routes/kemono/index.ts +12 -11
- package/lib/routes/kemono/namespace.ts +1 -1
- package/lib/routes/kiro/blog.ts +131 -0
- package/lib/routes/kiro/changelog.ts +128 -0
- package/lib/routes/kiro/namespace.ts +9 -0
- package/lib/routes/konachan/post.ts +3 -0
- package/lib/routes/kovidgoyal/kitty/changelog.ts +83 -0
- package/lib/routes/kovidgoyal/namespace.ts +7 -0
- package/lib/routes/kunchengblog/essay.ts +1 -1
- package/lib/routes/landiannews/category.ts +1 -1
- package/lib/routes/landiannews/index.ts +1 -1
- package/lib/routes/landiannews/tag.ts +1 -1
- package/lib/routes/line/utils.ts +1 -1
- package/lib/routes/lineageos/changes.ts +79 -0
- package/lib/routes/lineageos/namespace.ts +9 -0
- package/lib/routes/linkedin/cn/renderer.ts +1 -1
- package/lib/routes/liquipedia/cs-matches.ts +42 -14
- package/lib/routes/literotica/new.ts +1 -0
- package/lib/routes/lofter/tag.ts +27 -3
- package/lib/routes/makerworld/contest.ts +51 -0
- package/lib/routes/makerworld/namespace.ts +7 -0
- package/lib/routes/makerworld/trending.ts +45 -0
- package/lib/routes/makerworld/user-upload.ts +54 -0
- package/lib/routes/makerworld/utils.ts +18 -0
- package/lib/routes/manhuagui/comic.ts +1 -1
- package/lib/routes/manus/blog.ts +77 -0
- package/lib/routes/manus/namespace.ts +7 -0
- package/lib/routes/mastodon/tag.ts +48 -0
- package/lib/routes/mathpix/blog.ts +155 -0
- package/lib/routes/mathpix/namespace.ts +9 -0
- package/lib/routes/mathpix/templates/description.art +21 -0
- package/lib/routes/mckinsey/cn/category-map.ts +9 -15
- package/lib/routes/mckinsey/cn/index.ts +77 -40
- package/lib/routes/melonbooks/search.ts +1 -0
- package/lib/routes/metacritic/index.ts +2 -2
- package/lib/routes/meteoblue/namespace.ts +8 -0
- package/lib/routes/meteoblue/weathernews.ts +75 -0
- package/lib/routes/meteor/index.ts +1 -1
- package/lib/routes/microsoft/addon.ts +7 -9
- package/lib/routes/mihoyo/zzz/news.ts +106 -0
- package/lib/routes/missav/new.ts +2 -1
- package/lib/routes/mit/hanlab.ts +61 -0
- package/lib/routes/mit/namespace.ts +6 -0
- package/lib/routes/mittrchina/index.ts +3 -3
- package/lib/routes/mixcloud/config.ts +129 -0
- package/lib/routes/mixcloud/index.ts +163 -107
- package/lib/routes/mixcloud/user-playlist.ts +31 -0
- package/lib/routes/mixi2/community.ts +72 -0
- package/lib/routes/mixi2/discovery.ts +52 -0
- package/lib/routes/mixi2/home.ts +51 -0
- package/lib/routes/mixi2/namespace.ts +8 -0
- package/lib/routes/mixi2/user.ts +77 -0
- package/lib/routes/mixi2/utils.ts +56 -0
- package/lib/routes/modelscope/learn.ts +94 -0
- package/lib/routes/moodysmismicrosite/report.ts +1 -1
- package/lib/routes/musify/index.ts +130 -0
- package/lib/routes/musify/namespace.ts +9 -0
- package/lib/routes/musikguru/namespace.ts +9 -0
- package/lib/routes/musikguru/news.ts +146 -0
- package/lib/routes/musikguru/templates/description.art +21 -0
- package/lib/routes/myfans/post.ts +1 -0
- package/lib/routes/nankai/cc-notice.ts +114 -0
- package/lib/routes/nankai/jwc.ts +131 -0
- package/lib/routes/nankai/namespace.ts +7 -0
- package/lib/routes/nankai/notice.ts +84 -0
- package/lib/routes/nankai/yzb.ts +97 -0
- package/lib/routes/neu/yz.ts +172 -0
- package/lib/routes/newslaundry/explainer.ts +30 -0
- package/lib/routes/newslaundry/namespace.ts +8 -0
- package/lib/routes/newslaundry/nl-cheatsheet.ts +30 -0
- package/lib/routes/newslaundry/nl-collaborations.ts +30 -0
- package/lib/routes/newslaundry/podcast.ts +61 -0
- package/lib/routes/newslaundry/reports.ts +30 -0
- package/lib/routes/newslaundry/shot.ts +30 -0
- package/lib/routes/newslaundry/subscriber-only.ts +30 -0
- package/lib/routes/newslaundry/templates/description.art +28 -0
- package/lib/routes/newslaundry/utils.ts +108 -0
- package/lib/routes/newswav/latest.ts +100 -0
- package/lib/routes/newswav/namespace.ts +7 -0
- package/lib/routes/nhentai/index.ts +1 -0
- package/lib/routes/nifd/research.ts +16 -0
- package/lib/routes/nio/namespace.ts +9 -0
- package/lib/routes/nio/nioradio.ts +75 -0
- package/lib/routes/njust/cs.ts +58 -0
- package/lib/routes/njust/utils.ts +1 -1
- package/lib/routes/ntdm/video.ts +1 -1
- package/lib/routes/nuaa/utils/pypasswaf.ts +1 -1
- package/lib/routes/oilchem/index.ts +1 -1
- package/lib/routes/oncc/money18.ts +1 -1
- package/lib/routes/openai/cookbook.ts +1 -1
- package/lib/routes/paulgraham/article.ts +2 -2
- package/lib/routes/picuki/profile.ts +4 -0
- package/lib/routes/pixiv/bookmarks.ts +1 -1
- package/lib/routes/pixiv/novel-api/series/sfw.ts +1 -1
- package/lib/routes/pixiv/novels.ts +2 -2
- package/lib/routes/pixiv/user.ts +1 -1
- package/lib/routes/playno1/av.ts +1 -0
- package/lib/routes/pornhub/model.ts +3 -7
- package/lib/routes/pornhub/pornstar.ts +2 -7
- package/lib/routes/pornhub/users.ts +2 -7
- package/lib/routes/pornhub/utils.ts +12 -1
- package/lib/routes/qingting/podcast.ts +2 -1
- package/lib/routes/qipamaijia/index.ts +1 -0
- package/lib/routes/questmobile/report.ts +1 -1
- package/lib/routes/railway/index.ts +73 -0
- package/lib/routes/railway/namespace.ts +8 -0
- package/lib/routes/reuters/common.ts +0 -3
- package/lib/routes/rockthejvm/articles.ts +167 -0
- package/lib/routes/rockthejvm/namespace.ts +9 -0
- package/lib/routes/rockthejvm/templates/description.art +7 -0
- package/lib/routes/samrdprc/namespace.ts +7 -0
- package/lib/routes/samrdprc/news.ts +79 -0
- package/lib/routes/sankei/namespace.ts +7 -0
- package/lib/routes/sankei/news.ts +68 -0
- package/lib/routes/sankei/topics.ts +71 -0
- package/lib/routes/sciencenet/user.ts +1 -1
- package/lib/routes/scoop/apps.ts +188 -0
- package/lib/routes/scoop/namespace.ts +9 -0
- package/lib/routes/scoop/templates/description.art +56 -0
- package/lib/routes/scpta/namespace.ts +8 -0
- package/lib/routes/scpta/news.ts +101 -0
- package/lib/routes/seekingalpha/index.ts +1 -1
- package/lib/routes/sehuatang/index.ts +29 -31
- package/lib/routes/setn/index.ts +11 -2
- package/lib/routes/seu/cyber/index.ts +78 -0
- package/lib/routes/sicau/jiaowu.ts +42 -34
- package/lib/routes/sis001/forum.ts +1 -0
- package/lib/routes/sjtu/seiee/icisee.ts +67 -0
- package/lib/routes/sketis/isabelle-dev/blog/index.ts +1 -1
- package/lib/routes/smartlink/index.ts +13 -3
- package/lib/routes/sohu/mp.ts +17 -6
- package/lib/routes/sotwe/namespace.ts +1 -0
- package/lib/routes/sotwe/user.ts +98 -0
- package/lib/routes/spankbang/new-videos.ts +1 -0
- package/lib/routes/spglobal/ratings.ts +1 -1
- package/lib/routes/stanford/blog.ts +77 -0
- package/lib/routes/stanford/namespace.ts +7 -0
- package/lib/routes/syosetu/index.ts +1 -1
- package/lib/routes/t66y/index.ts +1 -0
- package/lib/routes/taobao/mysql.ts +157 -0
- package/lib/routes/taobao/namespace.ts +2 -2
- package/lib/routes/techcrunch/category.ts +48 -0
- package/lib/routes/telegram/channel.ts +2 -2
- package/lib/routes/tesla/cx.ts +1 -1
- package/lib/routes/test/index.ts +1 -1
- package/lib/routes/themoviedb/episodes.ts +1 -1
- package/lib/routes/thewirehindi/category.ts +78 -0
- package/lib/routes/thewirehindi/index.ts +43 -0
- package/lib/routes/thewirehindi/namespace.ts +7 -0
- package/lib/routes/thewirehindi/templates/description.art +9 -0
- package/lib/routes/thewirehindi/utils.ts +33 -0
- package/lib/routes/threads/utils.ts +2 -2
- package/lib/routes/tidb/blog.ts +314 -0
- package/lib/routes/tidb/namespace.ts +9 -0
- package/lib/routes/tumblr/namespace.ts +17 -0
- package/lib/routes/tumblr/posts.ts +74 -0
- package/lib/routes/tumblr/utils.ts +110 -0
- package/lib/routes/tver/namespace.ts +7 -0
- package/lib/routes/tver/series.ts +98 -0
- package/lib/routes/twitter/api/web-api/login.ts +1 -3
- package/lib/routes/twitter/api/web-api/utils.ts +23 -1
- package/lib/routes/twitter/utils.ts +21 -21
- package/lib/routes/udn/breaking-news.ts +2 -2
- package/lib/routes/uestc/auto.ts +1 -1
- package/lib/routes/uestc/cqe.ts +1 -1
- package/lib/routes/uestc/scse.ts +1 -1
- package/lib/routes/uestc/sice.ts +1 -1
- package/lib/routes/uestc/sise.ts +1 -1
- package/lib/routes/upc/jwc.ts +32 -46
- package/lib/routes/uptimerobot/rss.ts +1 -1
- package/lib/routes/visionias/daily-news-summary.ts +65 -0
- package/lib/routes/visionias/monthly-magazine.ts +68 -0
- package/lib/routes/visionias/news-today.ts +3 -13
- package/lib/routes/visionias/utils.ts +5 -5
- package/lib/routes/visionias/weekly-focus.ts +2 -2
- package/lib/routes/wdfxw/bookfree.ts +528 -0
- package/lib/routes/wdfxw/namespace.ts +8 -0
- package/lib/routes/wdfxw/templates/description.art +13 -0
- package/lib/routes/whu/swrh.ts +1 -1
- package/lib/routes/windsurf/blog.ts +118 -0
- package/lib/routes/windsurf/changelog.ts +100 -0
- package/lib/routes/windsurf/namespace.ts +9 -0
- package/lib/routes/windsurf/templates/description.art +21 -0
- package/lib/routes/x6d/index.ts +1 -1
- package/lib/routes/xbookcn/blog.ts +1 -0
- package/lib/routes/xiaohongshu/util.ts +1 -3
- package/lib/routes/xiaoyuzhou/pickup.ts +1 -1
- package/lib/routes/ximalaya/album.ts +32 -70
- package/lib/routes/ximalaya/types.ts +48 -0
- package/lib/routes/ximalaya/utils.ts +9 -7
- package/lib/routes/xmanhua/index.ts +1 -0
- package/lib/routes/xueqiu/cookies.ts +1 -1
- package/lib/routes/xueqiu/user.ts +1 -1
- package/lib/routes/xwenming/index.ts +184 -0
- package/lib/routes/xwenming/namespace.ts +9 -0
- package/lib/routes/yahoo/news/utils.ts +1 -1
- package/lib/routes/yande/post.ts +3 -0
- package/lib/routes/ymgal/game.ts +1 -0
- package/lib/routes/youtube/api/google.ts +27 -0
- package/lib/routes/youtube/api/youtubei.ts +70 -20
- package/lib/routes/youtube/community.ts +3 -1
- package/lib/routes/youtube/utils.ts +16 -14
- package/lib/routes/zaobao/util.ts +8 -5
- package/lib/routes/zhihu/activities.ts +3 -0
- package/lib/routes/zhihu/execlib/x-zse-96-v3.ts +5 -5
- package/lib/routes/zhizhuan100/namespace.ts +7 -0
- package/lib/routes/zhizhuan100/report.ts +79 -0
- package/lib/routes/zimuxia/portfolio.ts +1 -1
- package/lib/routes/zju/sis/index.ts +161 -0
- package/lib/server.ts +5 -0
- package/lib/types.ts +58 -54
- package/lib/utils/helpers.ts +1 -1
- package/lib/utils/logger.ts +1 -1
- package/lib/utils/proxy/index.ts +102 -18
- package/lib/utils/proxy/multi-proxy.ts +139 -0
- package/lib/utils/proxy/unify-proxy.ts +3 -1
- package/lib/utils/puppeteer-utils.test.ts +1 -1
- package/lib/utils/puppeteer.test.ts +14 -27
- package/lib/utils/puppeteer.ts +51 -37
- package/lib/utils/readable-social.test.ts +65 -0
- package/lib/utils/readable-social.ts +15 -1
- package/lib/utils/request-rewriter/fetch.ts +42 -4
- package/package.json +54 -56
- package/lib/routes/mixcloud/queries.ts +0 -2274
- package/lib/routes-deprecated/dev.to/top.js +0 -40
|
@@ -84,7 +84,7 @@ const parseItem = (item, tryGet) =>
|
|
|
84
84
|
|
|
85
85
|
const ldJson = JSON.parse($('script[type="application/ld+json"]').first().text());
|
|
86
86
|
const author = `${$('span.caas-author-byline-collapse').text()} @${$('span.caas-attr-provider').text()}`;
|
|
87
|
-
const body = $('.
|
|
87
|
+
const body = $('.atoms');
|
|
88
88
|
|
|
89
89
|
body.find('noscript').remove();
|
|
90
90
|
// remove padding
|
package/lib/routes/yande/post.ts
CHANGED
package/lib/routes/ymgal/game.ts
CHANGED
|
@@ -8,6 +8,9 @@ import ofetch from '@/utils/ofetch';
|
|
|
8
8
|
import * as cheerio from 'cheerio';
|
|
9
9
|
import NotFoundError from '@/errors/types/not-found';
|
|
10
10
|
import { Data } from '@/types';
|
|
11
|
+
import dayjs from 'dayjs';
|
|
12
|
+
import duration from 'dayjs/plugin/duration.js';
|
|
13
|
+
dayjs.extend(duration);
|
|
11
14
|
|
|
12
15
|
let count = 0;
|
|
13
16
|
const youtube = {};
|
|
@@ -102,6 +105,8 @@ export const getDataByUsername = async ({ username, embed, filterShorts }: { use
|
|
|
102
105
|
if (!playlistItems) {
|
|
103
106
|
throw new NotFoundError("This channel doesn't have any content.");
|
|
104
107
|
}
|
|
108
|
+
const videoIds = playlistItems.data.items.map((item) => item.snippet.resourceId.videoId).join(',');
|
|
109
|
+
const videoDetails = await utils.getVideos(videoIds, 'contentDetails', cache);
|
|
105
110
|
|
|
106
111
|
return {
|
|
107
112
|
title: `${userHandleData?.channelName || username} - YouTube`,
|
|
@@ -114,6 +119,7 @@ export const getDataByUsername = async ({ username, embed, filterShorts }: { use
|
|
|
114
119
|
const snippet = item.snippet;
|
|
115
120
|
const videoId = snippet.resourceId.videoId;
|
|
116
121
|
const img = utils.getThumbnail(snippet.thumbnails);
|
|
122
|
+
const detail = videoDetails?.data.items.find((d) => d.id === videoId);
|
|
117
123
|
return {
|
|
118
124
|
title: snippet.title,
|
|
119
125
|
description: utils.renderDescription(embed, videoId, img, utils.formatDescription(snippet.description)),
|
|
@@ -125,6 +131,7 @@ export const getDataByUsername = async ({ username, embed, filterShorts }: { use
|
|
|
125
131
|
{
|
|
126
132
|
url: getVideoUrl(videoId),
|
|
127
133
|
mime_type: 'text/html',
|
|
134
|
+
duration_in_seconds: detail?.contentDetails.duration ? dayjs.duration(detail.contentDetails.duration).asSeconds() : undefined,
|
|
128
135
|
},
|
|
129
136
|
],
|
|
130
137
|
};
|
|
@@ -140,6 +147,8 @@ export const getDataByChannelId = async ({ channelId, embed, filterShorts }: { c
|
|
|
140
147
|
const playlistId = filterShorts ? utils.getPlaylistWithShortsFilter(channelId) : originalPlaylistId;
|
|
141
148
|
|
|
142
149
|
const data = (await utils.getPlaylistItems(playlistId, 'snippet', cache)).data.items;
|
|
150
|
+
const videoIds = data.map((item) => item.snippet.resourceId.videoId).join(',');
|
|
151
|
+
const videoDetails = await utils.getVideos(videoIds, 'contentDetails', cache);
|
|
143
152
|
|
|
144
153
|
return {
|
|
145
154
|
title: `${data[0].snippet.channelTitle} - YouTube`,
|
|
@@ -151,6 +160,7 @@ export const getDataByChannelId = async ({ channelId, embed, filterShorts }: { c
|
|
|
151
160
|
const snippet = item.snippet;
|
|
152
161
|
const videoId = snippet.resourceId.videoId;
|
|
153
162
|
const img = utils.getThumbnail(snippet.thumbnails);
|
|
163
|
+
const detail = videoDetails?.data.items.find((d) => d.id === videoId);
|
|
154
164
|
return {
|
|
155
165
|
title: snippet.title,
|
|
156
166
|
description: utils.renderDescription(embed, videoId, img, utils.formatDescription(snippet.description)),
|
|
@@ -158,6 +168,13 @@ export const getDataByChannelId = async ({ channelId, embed, filterShorts }: { c
|
|
|
158
168
|
link: `https://www.youtube.com/watch?v=${videoId}`,
|
|
159
169
|
author: snippet.videoOwnerChannelTitle,
|
|
160
170
|
image: img.url,
|
|
171
|
+
attachments: [
|
|
172
|
+
{
|
|
173
|
+
url: getVideoUrl(videoId),
|
|
174
|
+
mime_type: 'text/html',
|
|
175
|
+
duration_in_seconds: detail?.contentDetails.duration ? dayjs.duration(detail.contentDetails.duration).asSeconds() : undefined,
|
|
176
|
+
},
|
|
177
|
+
],
|
|
161
178
|
};
|
|
162
179
|
}),
|
|
163
180
|
};
|
|
@@ -167,6 +184,8 @@ export const getDataByPlaylistId = async ({ playlistId, embed }: { playlistId: s
|
|
|
167
184
|
const playlistTitle = (await utils.getPlaylist(playlistId, 'snippet', cache)).data.items[0].snippet.title;
|
|
168
185
|
|
|
169
186
|
const data = (await utils.getPlaylistItems(playlistId, 'snippet', cache)).data.items.filter((d) => d.snippet.title !== 'Private video' && d.snippet.title !== 'Deleted video');
|
|
187
|
+
const videoIds = data.map((item) => item.snippet.resourceId.videoId).join(',');
|
|
188
|
+
const videoDetails = await utils.getVideos(videoIds, 'contentDetails', cache);
|
|
170
189
|
|
|
171
190
|
return {
|
|
172
191
|
title: `${playlistTitle} by ${data[0].snippet.channelTitle} - YouTube`,
|
|
@@ -176,6 +195,7 @@ export const getDataByPlaylistId = async ({ playlistId, embed }: { playlistId: s
|
|
|
176
195
|
const snippet = item.snippet;
|
|
177
196
|
const videoId = snippet.resourceId.videoId;
|
|
178
197
|
const img = utils.getThumbnail(snippet.thumbnails);
|
|
198
|
+
const detail = videoDetails?.data.items.find((d) => d.id === videoId);
|
|
179
199
|
return {
|
|
180
200
|
title: snippet.title,
|
|
181
201
|
description: utils.renderDescription(embed, videoId, img, utils.formatDescription(snippet.description)),
|
|
@@ -183,6 +203,13 @@ export const getDataByPlaylistId = async ({ playlistId, embed }: { playlistId: s
|
|
|
183
203
|
link: `https://www.youtube.com/watch?v=${videoId}`,
|
|
184
204
|
author: snippet.videoOwnerChannelTitle,
|
|
185
205
|
image: img.url,
|
|
206
|
+
attachments: [
|
|
207
|
+
{
|
|
208
|
+
url: getVideoUrl(videoId),
|
|
209
|
+
mime_type: 'text/html',
|
|
210
|
+
duration_in_seconds: detail?.contentDetails.duration ? dayjs.duration(detail.contentDetails.duration).asSeconds() : undefined,
|
|
211
|
+
},
|
|
212
|
+
],
|
|
186
213
|
};
|
|
187
214
|
}),
|
|
188
215
|
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getSubtitles } from 'youtube-caption-extractor';
|
|
1
2
|
import cache from '@/utils/cache';
|
|
2
3
|
import { Innertube } from 'youtubei.js';
|
|
3
4
|
import utils, { getVideoUrl } from '../utils';
|
|
@@ -6,6 +7,38 @@ import { parseRelativeDate } from '@/utils/parse-date';
|
|
|
6
7
|
|
|
7
8
|
const innertubePromise = Innertube.create();
|
|
8
9
|
|
|
10
|
+
function pad(n: number, width: number = 2) {
|
|
11
|
+
return String(n).padStart(width, '0');
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function toSrtTime(seconds: number): string {
|
|
15
|
+
const totalMs = Math.floor(seconds * 1000);
|
|
16
|
+
const hours = Math.floor(totalMs / 3_600_000);
|
|
17
|
+
const minutes = Math.floor((totalMs % 3_600_000) / 60000);
|
|
18
|
+
const secs = Math.floor((totalMs % 60000) / 1000);
|
|
19
|
+
const millis = totalMs % 1000;
|
|
20
|
+
return `${pad(hours)}:${pad(minutes)}:${pad(secs)},${pad(millis, 3)}`;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
type Subtitle = {
|
|
24
|
+
start: string;
|
|
25
|
+
dur: string;
|
|
26
|
+
text: string;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
function convertToSrt(segments: Subtitle[]): string {
|
|
30
|
+
return segments
|
|
31
|
+
.map((seg, index) => {
|
|
32
|
+
const start = Number.parseFloat(seg.start);
|
|
33
|
+
const end = start + Number.parseFloat(seg.dur);
|
|
34
|
+
return `${index + 1}
|
|
35
|
+
${toSrtTime(start)} --> ${toSrtTime(end)}
|
|
36
|
+
${seg.text}
|
|
37
|
+
`;
|
|
38
|
+
})
|
|
39
|
+
.join('\n');
|
|
40
|
+
}
|
|
41
|
+
|
|
9
42
|
export const getChannelIdByUsername = (username: string) =>
|
|
10
43
|
cache.tryGet(`youtube:getChannelIdByUsername:${username}`, async () => {
|
|
11
44
|
const innertube = await innertubePromise;
|
|
@@ -18,6 +51,13 @@ export const getDataByUsername = async ({ username, embed, filterShorts }: { use
|
|
|
18
51
|
return getDataByChannelId({ channelId, embed, filterShorts });
|
|
19
52
|
};
|
|
20
53
|
|
|
54
|
+
const getSubtitlesByVideoId = (videoId: string) =>
|
|
55
|
+
cache.tryGet(`youtube:getSubtitlesByVideoId:${videoId}`, async () => {
|
|
56
|
+
const subtitles = await getSubtitles({ videoID: videoId });
|
|
57
|
+
const srt = convertToSrt(subtitles);
|
|
58
|
+
return srt;
|
|
59
|
+
});
|
|
60
|
+
|
|
21
61
|
export const getDataByChannelId = async ({ channelId, embed }: { channelId: string; embed: boolean; filterShorts: boolean }): Promise<Data> => {
|
|
22
62
|
const innertube = await innertubePromise;
|
|
23
63
|
const channel = await innertube.getChannel(channelId);
|
|
@@ -29,27 +69,37 @@ export const getDataByChannelId = async ({ channelId, embed }: { channelId: stri
|
|
|
29
69
|
image: channel.metadata.avatar?.[0].url,
|
|
30
70
|
description: channel.metadata.description,
|
|
31
71
|
|
|
32
|
-
item:
|
|
33
|
-
.
|
|
34
|
-
|
|
35
|
-
|
|
72
|
+
item: await Promise.all(
|
|
73
|
+
videos.videos
|
|
74
|
+
.filter((video) => 'video_id' in video)
|
|
75
|
+
.map(async (video) => {
|
|
76
|
+
const srt = await getSubtitlesByVideoId(video.video_id);
|
|
77
|
+
const dataUrl = `data:text/plain;charset=utf-8,${encodeURIComponent(srt)}`;
|
|
36
78
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
79
|
+
const img = 'best_thumbnail' in video ? video.best_thumbnail?.url : 'thumbnails' in video ? video.thumbnails?.[0]?.url : undefined;
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
title: video.title.text || `YouTube Video ${video.video_id}`,
|
|
83
|
+
description: 'description_snippet' in video ? utils.renderDescription(embed, video.video_id, img, utils.formatDescription(video.description_snippet?.toHTML())) : null,
|
|
84
|
+
link: `https://www.youtube.com/watch?v=${video.video_id}`,
|
|
85
|
+
author: typeof video.author === 'string' ? video.author : video.author.name === 'N/A' ? undefined : video.author.name,
|
|
86
|
+
image: img,
|
|
87
|
+
pubDate: 'published' in video && video.published?.text ? parseRelativeDate(video.published.text) : undefined,
|
|
88
|
+
attachments: [
|
|
89
|
+
{
|
|
90
|
+
url: getVideoUrl(video.video_id),
|
|
91
|
+
mime_type: 'text/html',
|
|
92
|
+
duration_in_seconds: video.duration && 'seconds' in video.duration ? video.duration.seconds : undefined,
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
url: dataUrl,
|
|
96
|
+
mime_type: 'text/srt',
|
|
97
|
+
title: 'Subtitles',
|
|
98
|
+
},
|
|
99
|
+
],
|
|
100
|
+
};
|
|
101
|
+
})
|
|
102
|
+
),
|
|
53
103
|
};
|
|
54
104
|
};
|
|
55
105
|
|
|
@@ -35,7 +35,9 @@ async function handler(ctx) {
|
|
|
35
35
|
|
|
36
36
|
const channelMetadata = ytInitialData.metadata.channelMetadataRenderer;
|
|
37
37
|
const username = channelMetadata.title;
|
|
38
|
-
const communityTab = ytInitialData.contents.twoColumnBrowseResultsRenderer.tabs.find(
|
|
38
|
+
const communityTab = ytInitialData.contents.twoColumnBrowseResultsRenderer.tabs.find(
|
|
39
|
+
(tab) => tab.tabRenderer.endpoint.commandMetadata.webCommandMetadata.url.endsWith('/posts') || tab.tabRenderer.endpoint.commandMetadata.webCommandMetadata.url.endsWith('/community')
|
|
40
|
+
);
|
|
39
41
|
const list = communityTab.tabRenderer.content.sectionListRenderer.contents[0].itemSectionRenderer.contents;
|
|
40
42
|
|
|
41
43
|
if (list[0].messageRenderer) {
|
|
@@ -50,18 +50,18 @@ export const getChannelWithUsername = (username, part, cache) =>
|
|
|
50
50
|
);
|
|
51
51
|
return res;
|
|
52
52
|
});
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
53
|
+
export const getVideos = (id, part, cache) =>
|
|
54
|
+
cache.tryGet(`youtube:getVideos:${id}`, async () => {
|
|
55
|
+
const res = await exec((youtube) =>
|
|
56
|
+
youtube.videos.list({
|
|
57
|
+
part,
|
|
58
|
+
id,
|
|
59
|
+
})
|
|
60
|
+
);
|
|
61
|
+
return res;
|
|
62
|
+
});
|
|
63
63
|
export const getThumbnail = (thumbnails) => thumbnails.maxres || thumbnails.standard || thumbnails.high || thumbnails.medium || thumbnails.default;
|
|
64
|
-
export const formatDescription = (description) => description
|
|
64
|
+
export const formatDescription = (description) => description?.replaceAll(/\r\n|\r|\n/g, '<br>');
|
|
65
65
|
export const renderDescription = (embed, videoId, img, description) =>
|
|
66
66
|
art(path.join(__dirname, 'templates/description.art'), {
|
|
67
67
|
embed,
|
|
@@ -93,7 +93,9 @@ export async function getSubscriptionsRecusive(part, nextPageToken?) {
|
|
|
93
93
|
// recursively get next page
|
|
94
94
|
if (res.data.nextPageToken) {
|
|
95
95
|
const next = await getSubscriptionsRecusive(part, res.data.nextPageToken);
|
|
96
|
-
|
|
96
|
+
if (next.data.items) {
|
|
97
|
+
res.data.items = [...(res.data.items || []), ...next.data.items];
|
|
98
|
+
}
|
|
97
99
|
}
|
|
98
100
|
return res;
|
|
99
101
|
}
|
|
@@ -140,11 +142,12 @@ export const callApi = async <T>({ googleApi, youtubeiApi, params }: { googleApi
|
|
|
140
142
|
return await youtubeiApi(params);
|
|
141
143
|
};
|
|
142
144
|
|
|
143
|
-
|
|
145
|
+
export default {
|
|
144
146
|
getPlaylistItems,
|
|
145
147
|
getPlaylist,
|
|
146
148
|
getChannelWithId,
|
|
147
149
|
getChannelWithUsername,
|
|
150
|
+
getVideos,
|
|
148
151
|
getThumbnail,
|
|
149
152
|
formatDescription,
|
|
150
153
|
renderDescription,
|
|
@@ -155,4 +158,3 @@ const youtubeUtils = {
|
|
|
155
158
|
getVideoUrl,
|
|
156
159
|
getPlaylistWithShortsFilter,
|
|
157
160
|
};
|
|
158
|
-
export default youtubeUtils;
|
|
@@ -35,10 +35,10 @@ const parseList = async (
|
|
|
35
35
|
}> => {
|
|
36
36
|
const response = await got_ins.get(baseUrl + sectionUrl);
|
|
37
37
|
const $ = load(response.data);
|
|
38
|
-
let data = $('.card-listing .card');
|
|
38
|
+
let data = $('.card-listing .card .content-header a');
|
|
39
39
|
if (data.length === 0) {
|
|
40
40
|
// for HK version
|
|
41
|
-
data = $('.
|
|
41
|
+
data = $('[data-testid="article-list"] article a.article-link');
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
const title = $('meta[property="og:title"]').attr('content');
|
|
@@ -47,7 +47,7 @@ const parseList = async (
|
|
|
47
47
|
data.toArray().map((item) => {
|
|
48
48
|
const $item = $(item);
|
|
49
49
|
// addBack: for HK version
|
|
50
|
-
let link = $item.
|
|
50
|
+
let link = $item.attr('href');
|
|
51
51
|
|
|
52
52
|
if (link[0] !== '/') {
|
|
53
53
|
// https://www.zaobao.com/interactive-graphics
|
|
@@ -88,7 +88,7 @@ const parseList = async (
|
|
|
88
88
|
// HK
|
|
89
89
|
title = $1('h1.article-title').text();
|
|
90
90
|
const jsonText = $1("head script[type='application/ld+json']")
|
|
91
|
-
.eq(
|
|
91
|
+
.eq(0)
|
|
92
92
|
.text()
|
|
93
93
|
.replaceAll(/[\u0000-\u001F\u007F-\u009F]/g, '');
|
|
94
94
|
time = new Date(JSON.parse(jsonText)?.datePublished);
|
|
@@ -106,11 +106,14 @@ const parseList = async (
|
|
|
106
106
|
$1('#video-freemium-player').remove();
|
|
107
107
|
$1('script').remove();
|
|
108
108
|
$1('.bff-google-ad').remove();
|
|
109
|
+
$1('.bff-recommend-article').remove();
|
|
109
110
|
|
|
110
111
|
let articleBodyNode = $1('.articleBody');
|
|
111
112
|
if (articleBodyNode.length === 0) {
|
|
112
113
|
// for HK version
|
|
113
|
-
|
|
114
|
+
$1('.read-on-app-cover').remove();
|
|
115
|
+
$1('astro-island').remove();
|
|
116
|
+
$1('.further-reading').remove();
|
|
114
117
|
articleBodyNode = $1('.article-body');
|
|
115
118
|
}
|
|
116
119
|
|
|
@@ -103,6 +103,9 @@ async function handler(ctx) {
|
|
|
103
103
|
src="${contentItem.playlist[1].url}"></video></p>`;
|
|
104
104
|
|
|
105
105
|
break;
|
|
106
|
+
case 'link_card':
|
|
107
|
+
link = `<p><a href="${contentItem.url.split('?')[0]}" target="_blank"></a></p>`;
|
|
108
|
+
break;
|
|
106
109
|
|
|
107
110
|
default:
|
|
108
111
|
throw new Error(`Unknown type: ${contentItem.type}`);
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
/* eslint-disable @typescript-eslint/no-unused-expressions */
|
|
6
6
|
|
|
7
7
|
function i(e, t, n) {
|
|
8
|
-
(t[n] = 255 & (e >>> 24)), (t[n + 1] = 255 & (e >>> 16)), (t[n + 2] = 255 & (e >>> 8)), (t[n + 3] = 255 & e);
|
|
8
|
+
((t[n] = 255 & (e >>> 24)), (t[n + 1] = 255 & (e >>> 16)), (t[n + 2] = 255 & (e >>> 8)), (t[n + 3] = 255 & e));
|
|
9
9
|
}
|
|
10
10
|
function B(e, t) {
|
|
11
11
|
return ((255 & e[t]) << 24) | ((255 & e[t + 1]) << 16) | ((255 & e[t + 2]) << 8) | (255 & e[t + 3]);
|
|
@@ -16,7 +16,7 @@ function Q(e, t) {
|
|
|
16
16
|
function G(e) {
|
|
17
17
|
const t = Array.from({ length: 4 });
|
|
18
18
|
const n = Array.from({ length: 4 });
|
|
19
|
-
i(e, t, 0), (n[0] = h.zb[255 & t[0]]), (n[1] = h.zb[255 & t[1]]), (n[2] = h.zb[255 & t[2]]), (n[3] = h.zb[255 & t[3]]);
|
|
19
|
+
(i(e, t, 0), (n[0] = h.zb[255 & t[0]]), (n[1] = h.zb[255 & t[1]]), (n[2] = h.zb[255 & t[2]]), (n[3] = h.zb[255 & t[3]]));
|
|
20
20
|
const r = B(n, 0);
|
|
21
21
|
return r ^ Q(r, 2) ^ Q(r, 10) ^ Q(r, 18) ^ Q(r, 24);
|
|
22
22
|
}
|
|
@@ -29,19 +29,19 @@ const __g = {
|
|
|
29
29
|
a[c] = o[c] ^ t[c];
|
|
30
30
|
}
|
|
31
31
|
// eslint-disable-next-line unicorn/prefer-spread
|
|
32
|
-
(t = __g.r(a)), (n = n.concat(t)), i
|
|
32
|
+
((t = __g.r(a)), (n = n.concat(t)), i++);
|
|
33
33
|
}
|
|
34
34
|
return n;
|
|
35
35
|
},
|
|
36
36
|
r(e) {
|
|
37
37
|
const t = Array.from({ length: 16 });
|
|
38
38
|
const n = Array.from({ length: 36 });
|
|
39
|
-
(n[0] = B(e, 0)), (n[1] = B(e, 4)), (n[2] = B(e, 8)), (n[3] = B(e, 12));
|
|
39
|
+
((n[0] = B(e, 0)), (n[1] = B(e, 4)), (n[2] = B(e, 8)), (n[3] = B(e, 12)));
|
|
40
40
|
for (let r = 0; r < 32; r++) {
|
|
41
41
|
const o = G(n[r + 1] ^ n[r + 2] ^ n[r + 3] ^ h.zk[r]);
|
|
42
42
|
n[r + 4] = n[r] ^ o;
|
|
43
43
|
}
|
|
44
|
-
return i(n[35], t, 0), i(n[34], t, 4), i(n[33], t, 8), i(n[32], t, 12), t;
|
|
44
|
+
return (i(n[35], t, 0), i(n[34], t, 4), i(n[33], t, 8), i(n[32], t, 12), t);
|
|
45
45
|
},
|
|
46
46
|
};
|
|
47
47
|
const h = {
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { Route, ViewType } from '@/types';
|
|
2
|
+
import { parseRelativeDate } from '@/utils/parse-date';
|
|
3
|
+
import ofetch from '@/utils/ofetch';
|
|
4
|
+
import * as cheerio from 'cheerio';
|
|
5
|
+
|
|
6
|
+
export const route: Route = {
|
|
7
|
+
path: '/analytic',
|
|
8
|
+
categories: ['finance'],
|
|
9
|
+
view: ViewType.Articles,
|
|
10
|
+
example: '/zhizhuan100/analytic',
|
|
11
|
+
radar: [
|
|
12
|
+
{
|
|
13
|
+
source: ['www.zhizhuan100.com.cn/analysis'],
|
|
14
|
+
},
|
|
15
|
+
],
|
|
16
|
+
name: 'analytic',
|
|
17
|
+
maintainers: ['Cedaric'],
|
|
18
|
+
handler,
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
async function handler() {
|
|
22
|
+
const urlData = await ofetch('https://www.zhizhuan100.com.cn/analysis');
|
|
23
|
+
|
|
24
|
+
const $ = cheerio.load(urlData);
|
|
25
|
+
|
|
26
|
+
const bodyJsUrl: string | undefined = $('script[src*="Body.js"]').attr('src');
|
|
27
|
+
|
|
28
|
+
if (!bodyJsUrl) {
|
|
29
|
+
throw new Error('无法找到 Body.js 脚本文件');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const responseData = await ofetch(bodyJsUrl, {
|
|
33
|
+
parseResponse: (txt) => txt,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const htmlMatch = responseData.match(/document\.write\('(.*)'\);/s);
|
|
37
|
+
if (!htmlMatch) {
|
|
38
|
+
throw new Error('无法找到HTML内容');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const htmlContent = JSON.parse(`"${htmlMatch[1]}"`);
|
|
42
|
+
const $content = cheerio.load(htmlContent);
|
|
43
|
+
|
|
44
|
+
const listItems = $content('.w-list-item');
|
|
45
|
+
|
|
46
|
+
const items = listItems
|
|
47
|
+
.toArray()
|
|
48
|
+
.map((item) => {
|
|
49
|
+
const $item = $content(item);
|
|
50
|
+
const titleElement = $item.find('.w-list-title');
|
|
51
|
+
const dateElement = $item.find('.w-list-date');
|
|
52
|
+
const linkElement = $item.find('.w-list-link');
|
|
53
|
+
const imgElement = $item.find('.w-listpic-in');
|
|
54
|
+
|
|
55
|
+
const title = titleElement.text().trim() || '';
|
|
56
|
+
const dateText = dateElement.text().trim() || '';
|
|
57
|
+
const href = linkElement.attr('href') || '';
|
|
58
|
+
const imgSrc = imgElement.attr('src') || '';
|
|
59
|
+
|
|
60
|
+
const idMatch = href.match(/\/productinfo\/(\d+)\.html/);
|
|
61
|
+
const id = idMatch ? idMatch[1] : '';
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
title,
|
|
65
|
+
pubDate: parseRelativeDate(dateText),
|
|
66
|
+
link: href.startsWith('http') ? href : `https://www.zhizhuan100.com.cn${href}`,
|
|
67
|
+
description: `<img src="${imgSrc.startsWith('//') ? 'https:' + imgSrc : imgSrc}" alt="${title}">`,
|
|
68
|
+
guid: id,
|
|
69
|
+
};
|
|
70
|
+
})
|
|
71
|
+
.filter((item) => item.title);
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
title: '智篆商业-消费报告',
|
|
75
|
+
link: 'https://www.zhizhuan100.com.cn/analysis',
|
|
76
|
+
allowEmpty: true,
|
|
77
|
+
item: items,
|
|
78
|
+
};
|
|
79
|
+
}
|