feat(route): creative-comic (#10981)
This commit is contained in:
parent
a492cee523
commit
2d537bf3f0
|
|
@ -246,6 +246,12 @@ pageClass: routes
|
|||
|
||||
见 [#bilibili](/social-media.html#bilibili)
|
||||
|
||||
## CCC 創作集
|
||||
|
||||
### 漫畫
|
||||
|
||||
<Route author="TonyRL" example="/creative-comic/book/117" path="/creative-comic/book/:id/:coverOnly?/:quality?" :paramsDesc="['漫畫 ID,可在 URL 中找到', '僅獲取封面,非 `true` 時將獲取**全部**頁面,預設 `true`', '閱讀品質,標準畫質 `1`,高畫質 `2`,預設 `1`']" radar="1" rssbud="1"/>
|
||||
|
||||
## DLsite
|
||||
|
||||
### 当前日期发售的新产品
|
||||
|
|
|
|||
|
|
@ -0,0 +1,60 @@
|
|||
const { parseDate } = require('@/utils/parse-date');
|
||||
const { art } = require('@/utils/render');
|
||||
const path = require('path');
|
||||
const { getUuid, getBook, getChapter, getChapters, getImgEncrypted, getImgKey, decrypt, getRealKey, siteHost } = require('./utils');
|
||||
|
||||
module.exports = async (ctx) => {
|
||||
const { id, coverOnly = 'true', quality = '1' } = ctx.params;
|
||||
const uuid = await getUuid(ctx.cache.tryGet);
|
||||
let { data: book } = await getBook(id, uuid);
|
||||
let { data: chapters } = await getChapters(id, uuid);
|
||||
book = book.data;
|
||||
chapters = chapters.data;
|
||||
|
||||
const items = await Promise.all(
|
||||
chapters.chapters.map(async (c) => {
|
||||
let pages;
|
||||
if (coverOnly !== 'true' && coverOnly !== '1') {
|
||||
let { data: chapter } = await getChapter(c.id, uuid);
|
||||
chapter = chapter.data;
|
||||
|
||||
if (chapter.chapter.free_day === null || chapter.chapter.free_day === 0) {
|
||||
pages = await Promise.all(
|
||||
chapter.chapter.proportion.map(async (p) => {
|
||||
let { data: imgKey } = await getImgKey(p.id, uuid);
|
||||
imgKey = imgKey.data.key;
|
||||
|
||||
const realKey = getRealKey(imgKey);
|
||||
const encrypted = await getImgEncrypted(p.id, quality);
|
||||
|
||||
return ctx.cache.tryGet(`${siteHost}/fs/chapter_content/encrypt/${p.id}/${quality}`, () => decrypt(encrypted, realKey));
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
title: c.vol_name,
|
||||
description: art(path.join(__dirname, 'templates/chapter.art'), {
|
||||
chapter: c,
|
||||
pages,
|
||||
cover: c.image1,
|
||||
}),
|
||||
pubDate: parseDate(c.online_at),
|
||||
updated: parseDate(c.updated_at),
|
||||
link: `https://www.creative-comic.tw/reader_comic/${c.id}`,
|
||||
author: book.author.map((author) => author.name).join(', '),
|
||||
category: book.tags.map((tag) => tag.name),
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
ctx.state.data = {
|
||||
title: `${book.name} | CCC創作集`,
|
||||
description: `${book.brief} ${book.description}`,
|
||||
link: book.share_link,
|
||||
image: book.image1,
|
||||
item: items,
|
||||
language: 'zh-hant',
|
||||
};
|
||||
};
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
module.exports = {
|
||||
'/book/:id/:coverOnly?/:quality?': ['TonyRL'],
|
||||
};
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
module.exports = {
|
||||
'creative-comic.tw': {
|
||||
_name: 'CCC 創作集',
|
||||
'.': [
|
||||
{
|
||||
title: '漫畫',
|
||||
docs: 'https://docs.rsshub.app/anime.html#ccc-chuang-zuo-ji',
|
||||
source: ['/book/:id/*'],
|
||||
target: '/creative-comic/:id',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
module.exports = (router) => {
|
||||
router.get('/book/:id/:coverOnly?/:quality?', require('./book'));
|
||||
};
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
{{ if chapter }}
|
||||
<p>{{ chapter.name }}</p>
|
||||
{{ /if }}
|
||||
{{ if pages }}
|
||||
{{ each pages page }}
|
||||
<br><img src="{{ page }}">
|
||||
{{ /each }}
|
||||
{{ /if }}
|
||||
{{ if cover }}
|
||||
<img src="{{ cover }}">
|
||||
{{ /if }}
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
const got = require('@/utils/got');
|
||||
const CryptoJS = require('crypto-js');
|
||||
|
||||
const apiHost = 'https://api.creative-comic.tw';
|
||||
const siteHost = 'https://www.creative-comic.tw';
|
||||
const device = 'web_desktop';
|
||||
const DEFAULT_TOKEN = 'freeforccc2020reading';
|
||||
|
||||
const getBook = (bookId, uuid) =>
|
||||
got(`${apiHost}/book/${bookId}/info`, {
|
||||
headers: {
|
||||
device,
|
||||
uuid,
|
||||
},
|
||||
});
|
||||
|
||||
const getChapter = (id, uuid) =>
|
||||
got(`${apiHost}/book/chapter/${id}`, {
|
||||
headers: {
|
||||
device,
|
||||
uuid,
|
||||
},
|
||||
});
|
||||
|
||||
const getChapters = (bookId, uuid) =>
|
||||
got(`${apiHost}/book/${bookId}/chapter`, {
|
||||
headers: {
|
||||
device,
|
||||
uuid,
|
||||
},
|
||||
});
|
||||
|
||||
const getImgEncrypted = async (pageId, quality) => {
|
||||
const { data: res } = await got(`${siteHost}/fs/chapter_content/encrypt/${pageId}/${quality}`, {
|
||||
headers: {
|
||||
device,
|
||||
},
|
||||
responseType: 'buffer',
|
||||
});
|
||||
return Buffer.from(res).toString('base64');
|
||||
};
|
||||
|
||||
const getImgKey = (pageId, uuid) =>
|
||||
got(`${apiHost}/book/chapter/image/${pageId}`, {
|
||||
headers: {
|
||||
device,
|
||||
uuid,
|
||||
},
|
||||
});
|
||||
|
||||
const getUuid = (tryGet) =>
|
||||
tryGet('creative-comic:uuid', async () => {
|
||||
const { data } = await got(`${apiHost}/guest`, {
|
||||
headers: {
|
||||
device,
|
||||
},
|
||||
});
|
||||
return data.data;
|
||||
});
|
||||
|
||||
const decrypt = (encrypted, secrets) =>
|
||||
CryptoJS.AES.decrypt(encrypted, CryptoJS.enc.Hex.parse(secrets.key), {
|
||||
iv: CryptoJS.enc.Hex.parse(secrets.iv),
|
||||
mode: CryptoJS.mode.CBC,
|
||||
padding: CryptoJS.pad.Pkcs7,
|
||||
}).toString(CryptoJS.enc.Utf8);
|
||||
|
||||
const token2Key = (token) => {
|
||||
const t = CryptoJS.SHA512(token).toString();
|
||||
return {
|
||||
key: t.substring(0, 64),
|
||||
iv: t.substring(30, 62), // t.substr(30, 32)
|
||||
};
|
||||
};
|
||||
|
||||
const getRealKey = (imgKey, token = DEFAULT_TOKEN) => {
|
||||
const secrets = token2Key(token);
|
||||
const key = decrypt(imgKey, secrets);
|
||||
const realKey = key.split(':');
|
||||
return {
|
||||
key: realKey[0],
|
||||
iv: realKey[1],
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
getBook,
|
||||
getChapter,
|
||||
getChapters,
|
||||
getImgEncrypted,
|
||||
getImgKey,
|
||||
getUuid,
|
||||
decrypt,
|
||||
token2Key,
|
||||
getRealKey,
|
||||
};
|
||||
Loading…
Reference in New Issue