UNPKG

hexo-bangumis

Version:
194 lines (178 loc) 6.21 kB
/* eslint-disable no-underscore-dangle */ const fs = require('hexo-fs'); const path = require('path'); const fetch = require('node-fetch'); const log = require('hexo-log')({ debug: false, silent: false }); const LIMIT = 100; const USER_AGENT = 'mmdjiji/hexo-bangumis (https://github.com/mmdjiji/hexo-bangumis)'; const BGMTV_TYPE = { 1: '书籍', 2: '动画', 3: '音乐', 4: '游戏', 6: '三次元' }; // get a user's bangumi list const getBangumiList = async (bgmtv_uid) => { const wantWatch = []; // type=1 const watching = []; // type=3 const watched = []; // type=2 if (bgmtv_uid) { let offset = 0; let total = 0; do { // eslint-disable-next-line no-mixed-operators const req = await (await fetch(`https://api.bgm.tv/v0/users/${bgmtv_uid}/collections?subject_type=2&limit=${LIMIT}&offset=${offset}`, { headers: { 'User-Agent': USER_AGENT } })).json(); // eslint-disable-next-line prefer-destructuring total = req.total; for (const i of req.data) { const { subject_id, updated_at } = i; if (i.type === 1) { wantWatch.push({ subject_id, updated_at }); } else if (i.type === 3) { watching.push({ subject_id, updated_at }); } else if (i.type === 2) { watched.push({ subject_id, updated_at }); } } offset += LIMIT; } while (offset < total); log.info(`Get bangumi list successfully, found ${total} bangumis`); } return { wantWatch, watching, watched }; }; // get a bangumi by id // jsdelivr -> raw -> bgmtv const getBangumi = async (bgm, cachePath) => { const bangumi_id = bgm.subject_id; const savedPath = path.join(cachePath, `/${bangumi_id}.json`); if (await fs.exists(savedPath)) { try { const read = await JSON.parse(await fs.readFile(savedPath)); if (read.id === bangumi_id) { if (read?.eps > 0) { return read; } } else { throw new Error(`Id not match when trying to load id = ${bangumi_id}`); } } catch (error) { // invalid bangumi console.error(error); return undefined; } } try { const req = await fetch(`https://api.bgm.tv/v0/subjects/${bangumi_id}`, { headers: { 'User-Agent': USER_AGENT } }); if (req.status === 200) { const item = await req.json(); const obj = { id: item.id, name: item.name, name_cn: item.name_cn, type: BGMTV_TYPE[item.type], image: /\w+\/\w+\/\w+.jpg$/.exec(item.images.common)[0], link: `https://bgm.tv/subject/${item.id}`, eps: item.eps, collection: item.collection, date: item.date, summary: item.summary?.trim(), rating: item.rating, updated_at: bgm.updated_at }; fs.writeFile(savedPath, JSON.stringify(obj), (err) => { if (err) { log.info(`Failed to write data to cache/${bangumi_id}.json`); console.error(err); } }); return obj; } } catch (error) { console.log(error); log.info(`Failed to get bangumi (${bangumi_id}), please check network!`); return undefined; } fs.writeFile(savedPath, '{}', (err) => { // mark as invalid bangumi if (err) { log.info(`Failed to write data to cache/${bangumi_id}.json`); console.error(err); } }); log.info(`Get bangumi (${bangumi_id}) Failed, maybe invalid!`); }; const getImage = (image_url, imagesPath, image_level) => { if (image_url && !fs.existsSync(`${imagesPath}/${image_url}`)) { fetch(`https://lain.bgm.tv/pic/cover/${image_level}/${image_url}`, { method: 'GET', headers: { 'Content-Type': 'application/octet-stream' } }).then((res) => res.buffer()) .then((image) => { fs.writeFile(`${imagesPath}/${image_url}`, image, 'binary', (err) => { console.error(err); }); }); } }; module.exports.getBgmData = async (bgmtv_uid, download_image, image_level, source_dir) => { // create folders if not exist const bangumisPath = path.join(source_dir, '/_data/bangumis'); const cachePath = path.join(bangumisPath, '/cache'); const imagesPath = path.join(source_dir, '/images/bangumis'); const pathList = [bangumisPath, cachePath, imagesPath]; for (const i of pathList) { if (!fs.existsSync(i)) { fs.mkdirsSync(i); } } // get user's bangumi list const bangumiList = bgmtv_uid ? (await getBangumiList(bgmtv_uid)) : (await JSON.parse(fs.readFileSync(path.join(bangumisPath, '/index.json')))); if (bgmtv_uid) { fs.writeFile(path.join(bangumisPath, '/index.json'), JSON.stringify(bangumiList), (err) => { if (err) { log.info('Failed to write data to bangumis/index.json'); console.error(err); } }); } // for each bangumi, get its information in detail const batch = async (list) => { const result = []; for (const item of list) { const info = await getBangumi(item, cachePath); if (info) { result.push(info); if (download_image) { getImage(info.image, imagesPath, image_level); } log.info(`Get bangumi 《${info.name_cn || info.name}》 (${info.id}) Success!`); } } return result; }; const wantWatch = (await batch(bangumiList.wantWatch)).sort((a, b) => a.updated_at - b.updated_at); const watching = (await batch(bangumiList.watching)).sort((a, b) => a.updated_at - b.updated_at); const watched = (await batch(bangumiList.watched)).sort((a, b) => a.updated_at - b.updated_at); const result = { wantWatch, watching, watched }; fs.writeFile(path.join(bangumisPath, '/bangumis.json'), JSON.stringify(result), (err) => { if (err) { log.info('Failed to write data to cache/bangumis.json'); console.error(err); } }); const total = bangumiList.wantWatch.length + bangumiList.watching.length + bangumiList.watched.length; const succeed = result.wantWatch.length + result.watching.length + result.watched.length; const failed = total - succeed; log.info(`Generated bangumis.json, total ${total} bangumis, ${succeed} succeed, ${failed} failed`); };