feat(route): creative-comic (#10981)

This commit is contained in:
Tony 2022-10-01 23:37:10 +03:00 committed by GitHub
parent a492cee523
commit 2d537bf3f0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 192 additions and 0 deletions

View File

@ -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
### 当前日期发售的新产品

View File

@ -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',
};
};

View File

@ -0,0 +1,3 @@
module.exports = {
'/book/:id/:coverOnly?/:quality?': ['TonyRL'],
};

View File

@ -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',
},
],
},
};

View File

@ -0,0 +1,3 @@
module.exports = (router) => {
router.get('/book/:id/:coverOnly?/:quality?', require('./book'));
};

View File

@ -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 }}

View File

@ -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,
};