hexo-bilibili-bangumi
Version:
159 lines • 5.13 kB
JavaScript
const fs = require('hexo-fs');
const path = require('path');
const axios = require('axios');
const hexoLog = require('hexo-log');
const SIMKL_API = 'https://api.simkl.com';
const CACHE_DIR = '_data';
const CACHE_FILE = 'bangumis.json';
const EXTRA_CACHE_FILE = 'extra_bangumis.json';
const USER_AGENT = 'HCLonely/hexo-bilibili-bangumi';
const MAX_RETRIES = 3;
const RETRY_DELAY = 1000;
const STATUS_MAP = {
plantowatch: 'wantWatch',
watching: 'watching',
completed: 'watched'
};
const log = typeof hexoLog.default === 'function' ? hexoLog.default({
debug: false,
silent: false
}) : hexoLog({
debug: false,
silent: false
});
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
const fetchWithRetry = async (status, config, type, retries = MAX_RETRIES) => {
try {
const response = await axios({
method: 'GET',
url: `${SIMKL_API}/sync/all-items/${type}/${status}?memos=yes`,
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${config.token}`,
'simkl-api-key': config.clientId,
'User-Agent': USER_AGENT
}
});
return response;
} catch (error) {
if (retries > 0) {
await delay(RETRY_DELAY);
return fetchWithRetry(status, config, type, retries - 1);
}
throw error;
}
};
const formatBangumiData = (bangumi, coverMirror) => ({
title: bangumi.show?.title,
type: bangumi.anime_type,
cover: `${coverMirror}https://simkl.in/posters/${bangumi.show?.poster}_m.jpg`,
totalCount: bangumi.total_episodes_count,
id: bangumi.show?.ids?.simkl,
des: bangumi.show?.ids?.wikien?.replaceAll('_', ' ') || bangumi.show?.ids?.wikijp || '-',
year: bangumi.show?.year || '-',
myStars: bangumi.user_rating,
myComment: bangumi.memo?.text || '-',
progress: Math.round(bangumi.watched_episodes_count / (bangumi.total_episodes_count || 1) * 100),
ep_status: bangumi.watched_episodes_count,
new_ep: bangumi.total_episodes_count
});
const getData = async (config, coverMirror, type) => {
const $data = {
wantWatch: [],
watching: [],
watched: []
};
for (const status of ['plantowatch', 'watching', 'completed']) {
try {
const response = await fetchWithRetry(status, config, type);
if (response?.status !== 200) {
throw new Error(`API请求失败: ${response?.status}`);
}
const animeList = response?.data?.anime || [];
const mappedStatus = STATUS_MAP[status];
animeList.forEach(bangumi => {
$data[mappedStatus].push(formatBangumiData(bangumi, coverMirror));
});
} catch (error) {
log.error(`获取SIMKL ${status}数据失败: ${error.message}`);
throw error;
}
}
return $data;
};
const ensureDataDir = (sourceDir, dataDir = CACHE_DIR) => {
const dir = path.join(sourceDir, dataDir);
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir);
}
return dir;
};
const writeDataToFile = (filePath, data, errorMsg, successMsg) => {
try {
fs.writeFileSync(filePath, JSON.stringify(data));
log.info(successMsg);
} catch (err) {
log.error(errorMsg);
console.error(err);
}
};
const mergeExtraData = (bangumis, extras, order) => {
const result = {
...bangumis
};
const {
wantWatchExtra,
watchingExtra,
watchedExtra
} = extras;
['wantWatch', 'watching', 'watched'].forEach(key => {
const extra = {
wantWatch: wantWatchExtra,
watching: watchingExtra,
watched: watchedExtra
}[key];
if (extra) {
result[key] = order === 1 ? [...extra, ...result[key]] : [...result[key], ...extra];
}
});
return result;
};
module.exports.getSimklData = async ({
vmid,
type,
sourceDir,
extraOrder,
pagination,
coverMirror
}) => {
try {
log.info('Getting SIMKL bangumi data, please wait...');
const startTime = new Date().getTime();
const [clientId, token] = vmid.split('-');
const bangumis = await getData({
token,
clientId
}, coverMirror, type);
const totalCount = Object.values(bangumis).reduce((sum, arr) => sum + arr.length, 0);
const endTime = new Date().getTime();
log.info(`${totalCount} bangumis have been loaded in ${endTime - startTime} ms`);
ensureDataDir(sourceDir);
const mainDataPath = path.join(sourceDir, `/${CACHE_DIR}/${CACHE_FILE}`);
writeDataToFile(mainDataPath, bangumis, 'Failed to write data to _data/bangumis.json', 'SIMKL bangumis data has been saved');
if (pagination) {
let allBangumis = {
...bangumis
};
const extraDataPath = path.join(sourceDir, `/${CACHE_DIR}/${EXTRA_CACHE_FILE}`);
if (fs.existsSync(extraDataPath)) {
const extraData = JSON.parse(fs.readFileSync(extraDataPath));
allBangumis = mergeExtraData(allBangumis, extraData, extraOrder);
}
const paginationPath = path.join(sourceDir, '/bangumis.json');
writeDataToFile(paginationPath, allBangumis, 'Failed to write data to bangumis.json', 'SIMKL bangumis data (with extras) has been saved');
}
} catch (error) {
log.error(`获取SIMKL数据失败: ${error.message}`);
throw error;
}
};