mirror of
https://github.com/venera-app/venera-configs.git
synced 2025-09-27 08:27:24 +00:00
Create komiic.js
This commit is contained in:
441
komiic.js
Normal file
441
komiic.js
Normal file
@@ -0,0 +1,441 @@
|
||||
class Komiic extends ComicSource {
|
||||
|
||||
// 此漫画源的名称
|
||||
name = "Komiic"
|
||||
|
||||
// 唯一标识符
|
||||
key = "Komiic"
|
||||
|
||||
version = "1.0.0"
|
||||
|
||||
minAppVersion = "1.0.0"
|
||||
|
||||
// 更新链接
|
||||
url = "https://raw.githubusercontent.com/venera-app/venera_configs/master/komiic.js"
|
||||
|
||||
get headers() {
|
||||
let token = this.loadData('token')
|
||||
let headers = {
|
||||
'Referer': 'https://komiic.com/',
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36',
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
if (token) {
|
||||
headers['Authorization'] = `Bearer ${token}`
|
||||
}
|
||||
return headers
|
||||
}
|
||||
|
||||
async queryJson(query) {
|
||||
let operationName = query["operationName"]
|
||||
|
||||
let res = await Network.post(
|
||||
'https://komiic.com/api/query',
|
||||
this.headers,
|
||||
query
|
||||
)
|
||||
|
||||
if (res.status !== 200) {
|
||||
throw `Invalid Status Code ${res.status}`
|
||||
}
|
||||
|
||||
let json = JSON.parse(res.body)
|
||||
|
||||
if (json.errors != undefined) {
|
||||
if(json.errors[0].message.toString().indexOf('token is expired') >= 0){
|
||||
throw 'Login expired'
|
||||
}
|
||||
throw json.errors[0].message
|
||||
}
|
||||
|
||||
return json
|
||||
}
|
||||
|
||||
async queryComics(query) {
|
||||
let operationName = query["operationName"]
|
||||
let json = await this.queryJson(query)
|
||||
|
||||
function parseComic(comic) {
|
||||
let author = ''
|
||||
if (comic.authors.length > 0) {
|
||||
author = comic.authors[0].name
|
||||
}
|
||||
let tags = []
|
||||
comic.categories.forEach((c) => {
|
||||
tags.push(c.name)
|
||||
})
|
||||
|
||||
function getTimeDifference(date) {
|
||||
const now = new Date();
|
||||
const timeDifference = now - date;
|
||||
|
||||
const millisecondsPerHour = 1000 * 60 * 60;
|
||||
const millisecondsPerDay = millisecondsPerHour * 24;
|
||||
|
||||
if (timeDifference < millisecondsPerHour) {
|
||||
return '剛剛更新';
|
||||
} else if (timeDifference < millisecondsPerDay) {
|
||||
const hours = Math.floor(timeDifference / millisecondsPerHour);
|
||||
return `${hours}小時前更新`;
|
||||
} else {
|
||||
const days = Math.floor(timeDifference / millisecondsPerDay);
|
||||
return `${days}天前更新`;
|
||||
}
|
||||
}
|
||||
|
||||
let updateTime = new Date(comic.dateUpdated)
|
||||
let description = getTimeDifference(updateTime)
|
||||
|
||||
return {
|
||||
id: comic.id,
|
||||
title: comic.title,
|
||||
subTitle: author,
|
||||
cover: comic.imageUrl,
|
||||
tags: tags,
|
||||
description: description
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
comics: json.data[operationName].map(parseComic),
|
||||
// 没找到最大页数的接口
|
||||
maxPage: null
|
||||
}
|
||||
}
|
||||
|
||||
/// 账号
|
||||
/// 设置为null禁用账号功能
|
||||
account = {
|
||||
/// 登录
|
||||
/// 返回任意值表示登录成功
|
||||
login: async (account, pwd) => {
|
||||
let res = await Network.post(
|
||||
'https://komiic.com/api/login',
|
||||
this.headers,
|
||||
{
|
||||
email: account,
|
||||
password: pwd
|
||||
}
|
||||
)
|
||||
|
||||
if (res.status === 200) {
|
||||
this.saveData('token', JSON.parse(res.body).token)
|
||||
return 'ok'
|
||||
}
|
||||
|
||||
throw 'Failed to login'
|
||||
},
|
||||
|
||||
// 退出登录时将会调用此函数
|
||||
logout: () => {
|
||||
this.deleteData('token')
|
||||
},
|
||||
|
||||
registerWebsite: "https://komiic.com/register"
|
||||
}
|
||||
|
||||
/// 探索页面
|
||||
/// 一个漫画源可以有多个探索页面
|
||||
explore = [
|
||||
{
|
||||
/// 标题
|
||||
/// 标题同时用作标识符, 不能重复
|
||||
title: "Komiic",
|
||||
|
||||
/// singlePageWithMultiPart 或者 multiPageComicList
|
||||
type: "multiPageComicList",
|
||||
|
||||
load: async (page) => {
|
||||
return await this.queryComics({ "operationName": "recentUpdate", "variables": { "pagination": { "limit": 20, "offset": (page - 1) * 20, "orderBy": "DATE_UPDATED", "status": "", "asc": true } }, "query": "query recentUpdate($pagination: Pagination!) {\n recentUpdate(pagination: $pagination) {\n id\n title\n status\n year\n imageUrl\n authors {\n id\n name\n __typename\n }\n categories {\n id\n name\n __typename\n }\n dateUpdated\n monthViews\n views\n favoriteCount\n lastBookUpdate\n lastChapterUpdate\n __typename\n }\n}" })
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
category = {
|
||||
title: "Komiic",
|
||||
enableRankingPage: true,
|
||||
parts: [
|
||||
{
|
||||
name: "主题",
|
||||
|
||||
type: "fixed",
|
||||
|
||||
categories: ['全部', '愛情', '神鬼', '校園', '搞笑', '生活', '懸疑', '冒險', '職場', '魔幻', '後宮', '魔法', '格鬥', '宅男', '勵志', '耽美', '科幻', '百合', '治癒', '萌系', '熱血', '競技', '推理', '雜誌', '偵探', '偽娘', '美食', '恐怖', '四格', '社會', '歷史', '戰爭', '舞蹈', '武俠', '機戰', '音樂', '體育', '黑道'],
|
||||
|
||||
itemType: "category",
|
||||
|
||||
// 若提供, 数量需要和`categories`一致, `categoryComics.load`方法将会收到此参数
|
||||
categoryParams: ['0', '1', '3', '4', '5', '6', '7', '8', '10', '11', '2', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '24', '25', '26', '27', '9', '28', '31', '32', '33', '34', '35', '36', '37', '40', '42']
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
/// 分类漫画页面, 即点击分类标签后进入的页面
|
||||
categoryComics = {
|
||||
load: async (category, param, options, page) => {
|
||||
return await this.queryComics({ "operationName": "comicByCategory", "variables": { "categoryId": param, "pagination": { "limit": 30, "offset": (page - 1) * 30, "orderBy": options[0], "asc": false, "status": options[1] } }, "query": "query comicByCategory($categoryId: ID!, $pagination: Pagination!) {\n comicByCategory(categoryId: $categoryId, pagination: $pagination) {\n id\n title\n status\n year\n imageUrl\n authors {\n id\n name\n __typename\n }\n categories {\n id\n name\n __typename\n }\n dateUpdated\n monthViews\n views\n favoriteCount\n lastBookUpdate\n lastChapterUpdate\n __typename\n }\n}" })
|
||||
},
|
||||
// 提供选项
|
||||
optionList: [
|
||||
{
|
||||
options: [
|
||||
"DATE_UPDATED-更新",
|
||||
"VIEWS-觀看數",
|
||||
"FAVORITE_COUNT-喜愛數",
|
||||
],
|
||||
notShowWhen: null,
|
||||
showWhen: null
|
||||
},
|
||||
{
|
||||
options: [
|
||||
"-全部",
|
||||
"ONGOING-連載中",
|
||||
"END-完結",
|
||||
],
|
||||
notShowWhen: null,
|
||||
showWhen: null
|
||||
},
|
||||
],
|
||||
ranking: {
|
||||
options: [
|
||||
"MONTH_VIEWS-月",
|
||||
"VIEWS-綜合"
|
||||
],
|
||||
load: async (option, page) => {
|
||||
return this.queryComics({ "operationName": "hotComics", "variables": { "pagination": { "limit": 20, "offset": (page - 1) * 20, "orderBy": option, "status": "", "asc": true } }, "query": "query hotComics($pagination: Pagination!) {\n hotComics(pagination: $pagination) {\n id\n title\n status\n year\n imageUrl\n authors {\n id\n name\n __typename\n }\n categories {\n id\n name\n __typename\n }\n dateUpdated\n monthViews\n views\n favoriteCount\n lastBookUpdate\n lastChapterUpdate\n __typename\n }\n}" })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 搜索
|
||||
search = {
|
||||
load: async (keyword, options, page) => {
|
||||
let json = await this.queryJson({ "operationName": "searchComicAndAuthorQuery", "variables": { "keyword": keyword }, "query": "query searchComicAndAuthorQuery($keyword: String!) {\n searchComicsAndAuthors(keyword: $keyword) {\n comics {\n id\n title\n status\n year\n imageUrl\n authors {\n id\n name\n __typename\n }\n categories {\n id\n name\n __typename\n }\n dateUpdated\n monthViews\n views\n favoriteCount\n lastBookUpdate\n lastChapterUpdate\n __typename\n }\n authors {\n id\n name\n chName\n enName\n wikiLink\n comicCount\n views\n __typename\n }\n __typename\n }\n}" })
|
||||
|
||||
function parseComic(comic) {
|
||||
let author = ''
|
||||
if (comic.authors.length > 0) {
|
||||
author = comic.authors[0].name
|
||||
}
|
||||
let tags = []
|
||||
comic.categories.forEach((c) => {
|
||||
tags.push(c.name)
|
||||
})
|
||||
|
||||
function getTimeDifference(date) {
|
||||
const now = new Date();
|
||||
const timeDifference = now - date;
|
||||
|
||||
const millisecondsPerHour = 1000 * 60 * 60;
|
||||
const millisecondsPerDay = millisecondsPerHour * 24;
|
||||
|
||||
if (timeDifference < millisecondsPerHour) {
|
||||
return '剛剛更新';
|
||||
} else if (timeDifference < millisecondsPerDay) {
|
||||
const hours = Math.floor(timeDifference / millisecondsPerHour);
|
||||
return `${hours}小時前更新`;
|
||||
} else {
|
||||
const days = Math.floor(timeDifference / millisecondsPerDay);
|
||||
return `${days}天前更新`;
|
||||
}
|
||||
}
|
||||
|
||||
let updateTime = new Date(comic.dateUpdated)
|
||||
let description = getTimeDifference(updateTime)
|
||||
|
||||
return {
|
||||
id: comic.id,
|
||||
title: comic.title,
|
||||
subTitle: author,
|
||||
cover: comic.imageUrl,
|
||||
tags: tags,
|
||||
description: description
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
comics: json.data.searchComicsAndAuthors.comics.map(parseComic),
|
||||
// 没找到最大页数的接口
|
||||
maxPage: 1
|
||||
}
|
||||
},
|
||||
|
||||
optionList: []
|
||||
}
|
||||
|
||||
/// 收藏
|
||||
favorites = {
|
||||
/// 是否为多收藏夹
|
||||
multiFolder: true,
|
||||
/// 添加或者删除收藏
|
||||
addOrDelFavorite: async (comicId, folderId, isAdding) => {
|
||||
let query = {}
|
||||
if (isAdding) {
|
||||
query = { "operationName": "addComicToFolder", "variables": { "comicId": comicId, "folderId": folderId }, "query": "mutation addComicToFolder($comicId: ID!, $folderId: ID!) {\n addComicToFolder(comicId: $comicId, folderId: $folderId)\n}" }
|
||||
} else {
|
||||
query = { "operationName": "removeComicToFolder", "variables": { "comicId": comicId, "folderId": folderId }, "query": "mutation removeComicToFolder($comicId: ID!, $folderId: ID!) {\n removeComicToFolder(comicId: $comicId, folderId: $folderId)\n}" }
|
||||
}
|
||||
await this.queryJson(query)
|
||||
return "ok"
|
||||
},
|
||||
// 加载收藏夹, 仅当multiFolder为true时有效
|
||||
// 当comicId不为null时, 需要同时返回包含该漫画的收藏夹
|
||||
loadFolders: async (comicId) => {
|
||||
let json = await this.queryJson({ "operationName": "myFolder", "variables": {}, "query": "query myFolder {\n folders {\n id\n key\n name\n views\n comicCount\n dateCreated\n dateUpdated\n __typename\n }\n}" })
|
||||
let folders = {}
|
||||
json.data.folders.forEach((f) => {
|
||||
folders[f.id] = f.name
|
||||
})
|
||||
let favorited = null
|
||||
if (comicId) {
|
||||
let json2 = await this.queryJson({ "operationName": "comicInAccountFolders", "variables": { "comicId": comicId }, "query": "query comicInAccountFolders($comicId: ID!) {\n comicInAccountFolders(comicId: $comicId)\n}" })
|
||||
favorited = json2.data.comicInAccountFolders
|
||||
}
|
||||
return {
|
||||
folders: folders,
|
||||
favorited: favorited
|
||||
}
|
||||
},
|
||||
/// 创建收藏夹
|
||||
addFolder: async (name) => {
|
||||
let json = await this.queryJson({ "operationName": "createFolder", "variables": { "name": name }, "query": "mutation createFolder($name: String!) {\n createFolder(name: $name) {\n id\n key\n name\n account {\n id\n nickname\n __typename\n }\n comicCount\n views\n dateCreated\n dateUpdated\n __typename\n }\n}" })
|
||||
return "ok"
|
||||
},
|
||||
deleteFolder: async (id) => {
|
||||
let json = await this.queryJson({ "operationName": "removeFolder", "variables": { "folderId": id }, "query": "mutation removeFolder($folderId: ID!) {\n removeFolder(folderId: $folderId)\n}" })
|
||||
return "ok"
|
||||
},
|
||||
/// 加载漫画
|
||||
loadComics: async (page, folder) => {
|
||||
let json = await this.queryJson({ "operationName": "folderComicIds", "variables": { "folderId": folder, "pagination": { "limit": 30, "offset": (page - 1) * 30, "orderBy": "DATE_UPDATED", "status": "", "asc": true } }, "query": "query folderComicIds($folderId: ID!, $pagination: Pagination!) {\n folderComicIds(folderId: $folderId, pagination: $pagination) {\n folderId\n key\n comicIds\n __typename\n }\n}" })
|
||||
let ids = json.data.folderComicIds.comicIds
|
||||
if (ids.length == 0) {
|
||||
return {
|
||||
comics: [],
|
||||
maxPage: 1
|
||||
}
|
||||
}
|
||||
return this.queryComics({ "operationName": "comicByIds", "variables": { "comicIds": ids }, "query": "query comicByIds($comicIds: [ID]!) {\n comicByIds(comicIds: $comicIds) {\n id\n title\n status\n year\n imageUrl\n authors {\n id\n name\n __typename\n }\n categories {\n id\n name\n __typename\n }\n dateUpdated\n monthViews\n views\n favoriteCount\n lastBookUpdate\n lastChapterUpdate\n __typename\n }\n}" })
|
||||
}
|
||||
}
|
||||
|
||||
/// 单个漫画相关
|
||||
comic = {
|
||||
// 加载漫画信息
|
||||
loadInfo: async (id) => {
|
||||
let json1 = await this.queryJson({ "operationName": "recommendComicById", "variables": { "comicId": id }, "query": "query recommendComicById($comicId: ID!) {\n recommendComicById(comicId: $comicId)\n}" })
|
||||
let recommend = json1.data.recommendComicById
|
||||
recommend.push(id)
|
||||
|
||||
let getFavoriteStatus = async () => {
|
||||
let token = this.loadData('token')
|
||||
if (!token) {
|
||||
return false
|
||||
}
|
||||
let json = await this.queryJson({ "operationName": "comicInAccountFolders", "variables": { "comicId": id }, "query": "query comicInAccountFolders($comicId: ID!) {\n comicInAccountFolders(comicId: $comicId)\n}" })
|
||||
let folders = json.data.comicInAccountFolders
|
||||
return folders.length !== 0
|
||||
}
|
||||
|
||||
let getChapter = async () => {
|
||||
let json = await this.queryJson({ "operationName": "chapterByComicId", "variables": { "comicId": id }, "query": "query chapterByComicId($comicId: ID!) {\n chaptersByComicId(comicId: $comicId) {\n id\n serial\n type\n dateCreated\n dateUpdated\n size\n __typename\n }\n}" })
|
||||
let all = json.data.chaptersByComicId
|
||||
let books = [], chapters = []
|
||||
all.forEach((c) => {
|
||||
if(c.type == 'book') {
|
||||
books.push(c)
|
||||
} else {
|
||||
chapters.push(c)
|
||||
}
|
||||
})
|
||||
let res = new Map()
|
||||
books.forEach((c) => {
|
||||
let name = '卷' + c.serial
|
||||
res.set(c.id, name)
|
||||
})
|
||||
chapters.forEach((c) => {
|
||||
let name = c.serial
|
||||
res.set(c.id, name)
|
||||
})
|
||||
return res
|
||||
}
|
||||
|
||||
let results = await Promise.all([
|
||||
this.queryComics({ "operationName": "comicByIds", "variables": { "comicIds": recommend }, "query": "query comicByIds($comicIds: [ID]!) {\n comicByIds(comicIds: $comicIds) {\n id\n title\n status\n year\n imageUrl\n authors {\n id\n name\n __typename\n }\n categories {\n id\n name\n __typename\n }\n dateUpdated\n monthViews\n views\n favoriteCount\n lastBookUpdate\n lastChapterUpdate\n __typename\n }\n}" }),
|
||||
getChapter.call()
|
||||
])
|
||||
|
||||
let info = results[0].comics.pop()
|
||||
|
||||
return {
|
||||
// string 标题
|
||||
title: info.title,
|
||||
// string 封面url
|
||||
cover: info.cover,
|
||||
// map<string, string[]> 标签
|
||||
tags: {
|
||||
"作者": [info.subTitle],
|
||||
"更新": [info.description],
|
||||
"标签": info.tags
|
||||
},
|
||||
// map<string, string>?, key为章节id, value为章节名称
|
||||
chapters: results[1],
|
||||
recommend: results[0].comics
|
||||
}
|
||||
},
|
||||
// 获取章节图片
|
||||
loadEp: async (comicId, epId) => {
|
||||
let json = await this.queryJson({ "operationName": "imagesByChapterId", "variables": { "chapterId": epId }, "query": "query imagesByChapterId($chapterId: ID!) {\n imagesByChapterId(chapterId: $chapterId) {\n id\n kid\n height\n width\n __typename\n }\n}" })
|
||||
return {
|
||||
images: json.data.imagesByChapterId.map((i) => {
|
||||
return `https://komiic.com/api/image/${i.kid}`
|
||||
})
|
||||
}
|
||||
},
|
||||
// 可选, 调整图片加载的行为
|
||||
onImageLoad: (url, comicId, epId) => {
|
||||
return {
|
||||
headers: {
|
||||
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36',
|
||||
'referer': `https://komiic.com/comic/${comicId}/chapter/${epId}/images/all`
|
||||
}
|
||||
}
|
||||
},
|
||||
// 加载评论
|
||||
loadComments: async (comicId, subId, page, replyTo) => {
|
||||
let operationName = replyTo ? "messageChan" : "getMessagesByComicId"
|
||||
let promise = replyTo
|
||||
? this.queryJson({ "operationName": "messageChan", "variables": { "messageId": replyTo }, "query": "query messageChan($messageId: ID!) {\n messageChan(messageId: $messageId) {\n id\n comicId\n account {\n id\n nickname\n profileText\n profileTextColor\n profileBackgroundColor\n profileImageUrl\n __typename\n }\n message\n replyTo {\n id\n __typename\n }\n upCount\n downCount\n dateUpdated\n dateCreated\n __typename\n }\n}" })
|
||||
: this.queryJson({ "operationName": "getMessagesByComicId", "variables": { "comicId": comicId, "pagination": { "limit": 100, "offset": (page - 1) * 100, "orderBy": "DATE_UPDATED", "asc": true } }, "query": "query getMessagesByComicId($comicId: ID!, $pagination: Pagination!) {\n getMessagesByComicId(comicId: $comicId, pagination: $pagination) {\n id\n comicId\n account {\n id\n nickname\n profileText\n profileTextColor\n profileBackgroundColor\n profileImageUrl\n __typename\n }\n message\n replyTo {\n id\n message\n account {\n id\n nickname\n profileText\n profileTextColor\n profileBackgroundColor\n profileImageUrl\n __typename\n }\n __typename\n }\n upCount\n downCount\n dateUpdated\n dateCreated\n __typename\n }\n}" })
|
||||
let json = await promise
|
||||
return {
|
||||
comments: json.data[operationName].map(e => {
|
||||
return {
|
||||
// string
|
||||
userName: e.account.nickname,
|
||||
// string
|
||||
avatar: e.account.profileImageUrl,
|
||||
// string
|
||||
content: e.message,
|
||||
// string?
|
||||
time: e.dateUpdated,
|
||||
// number?
|
||||
// TODO: 没有数量信息, 但是设为null会禁用回复功能
|
||||
replyCount: 0,
|
||||
// string
|
||||
id: e.id,
|
||||
}
|
||||
}),
|
||||
maxPage: null,
|
||||
}
|
||||
},
|
||||
// 发送评论, 返回任意值表示成功
|
||||
sendComment: async (comicId, subId, content, replyTo) => {
|
||||
if (!replyTo) {
|
||||
replyTo = "0"
|
||||
}
|
||||
let json = await this.queryJson({ "operationName": "addMessageToComic", "variables": { "comicId": comicId, "message": content, "replyToId": replyTo }, "query": "mutation addMessageToComic($comicId: ID!, $replyToId: ID!, $message: String!) {\n addMessageToComic(message: $message, comicId: $comicId, replyToId: $replyToId) {\n id\n message\n comicId\n account {\n id\n nickname\n __typename\n }\n replyTo {\n id\n message\n account {\n id\n nickname\n profileText\n profileTextColor\n profileBackgroundColor\n profileImageUrl\n __typename\n }\n __typename\n }\n dateCreated\n dateUpdated\n __typename\n }\n}" })
|
||||
return "ok"
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user