mirror of
https://github.com/kodjodevf/mangayomi-extensions.git
synced 2026-02-14 02:41:39 +00:00
597 lines
17 KiB
JavaScript
597 lines
17 KiB
JavaScript
const mangayomiSources = [{
|
|
"name": "拷贝漫画",
|
|
"lang": "zh",
|
|
"baseUrl": "https://www.mangacopy.com",
|
|
"apiUrl": "https://api.mangacopy.com",
|
|
"iconUrl": "https://hi77-overseas.mangafuna.xyz/static/free.ico",
|
|
"typeSource": "single",
|
|
"itemType": "manga",
|
|
"isNsfw": false,
|
|
"version": "0.0.2",
|
|
"dateFormat": "",
|
|
"dateFormatLocale": "",
|
|
"pkgPath": "manga/src/zh/copymanga.js"
|
|
}];
|
|
|
|
class DefaultExtension extends MProvider {
|
|
stringUTF8(text) {
|
|
var bytes = [];
|
|
for (var i = 0; i < text.length; i++) {
|
|
bytes.push(text.charCodeAt(i));
|
|
}
|
|
var charCodes = [];
|
|
var i = 0;
|
|
while (i < bytes.length) {
|
|
var byte1 = bytes[i];
|
|
var charCode;
|
|
|
|
if (byte1 < 0x80) {
|
|
charCode = byte1;
|
|
i += 1;
|
|
} else if (byte1 < 0xE0) {
|
|
var byte2 = bytes[i + 1];
|
|
charCode = ((byte1 & 0x1F) << 6) | (byte2 & 0x3F);
|
|
i += 2;
|
|
} else if (byte1 < 0xF0) {
|
|
var byte2 = bytes[i + 1];
|
|
var byte3 = bytes[i + 2];
|
|
charCode = ((byte1 & 0x0F) << 12) | ((byte2 & 0x3F) << 6) | (byte3 & 0x3F);
|
|
i += 3;
|
|
} else {
|
|
var byte2 = bytes[i + 1];
|
|
var byte3 = bytes[i + 2];
|
|
var byte4 = bytes[i + 3];
|
|
charCode = ((byte1 & 0x07) << 18) | ((byte2 & 0x3F) << 12) | ((byte3 & 0x3F) << 6) | (byte4 & 0x3F);
|
|
i += 4;
|
|
}
|
|
|
|
charCodes.push(charCode);
|
|
}
|
|
return String.fromCharCode.apply(null, charCodes);
|
|
}
|
|
|
|
base64encode(str) {
|
|
const base64EncodeChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
var out, i, len;
|
|
var c1, c2, c3;
|
|
len = str.length;
|
|
i = 0;
|
|
out = "";
|
|
while (i < len) {
|
|
c1 = str.charCodeAt(i++) & 0xff;
|
|
if (i == len) {
|
|
out += base64EncodeChars.charAt(c1 >> 2);
|
|
out += base64EncodeChars.charAt((c1 & 0x3) << 4);
|
|
out += "==";
|
|
break;
|
|
}
|
|
c2 = str.charCodeAt(i++);
|
|
if (i == len) {
|
|
out += base64EncodeChars.charAt(c1 >> 2);
|
|
out += base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4));
|
|
out += base64EncodeChars.charAt((c2 & 0xF) << 2);
|
|
out += "=";
|
|
break;
|
|
}
|
|
c3 = str.charCodeAt(i++);
|
|
out += base64EncodeChars.charAt(c1 >> 2);
|
|
out += base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4));
|
|
out += base64EncodeChars.charAt(((c2 & 0xF) << 2) | ((c3 & 0xC0) >> 6));
|
|
out += base64EncodeChars.charAt(c3 & 0x3F);
|
|
}
|
|
return out;
|
|
}
|
|
|
|
decode(result) {
|
|
var iv = result.substring(0, 16);
|
|
result = result.replace(iv, "");
|
|
const bytes = [];
|
|
for (var i = 0; i < result.length; i = i + 2) {
|
|
bytes.push(parseInt(result.substr(i, 2), 16));
|
|
}
|
|
var charString = String.fromCharCode.apply(null, bytes);
|
|
const data = this.base64encode(charString);
|
|
const text = cryptoHandler(data, iv, "xxxmanga.woo.key", false);
|
|
return text;
|
|
}
|
|
|
|
getHeaders(url) {
|
|
return {
|
|
Referer: this.source.baseUrl
|
|
};
|
|
}
|
|
|
|
async getManga(url) {
|
|
const res = await new Client().get(this.source.apiUrl + url);
|
|
const datas = JSON.parse(res.body);
|
|
const manga = [];
|
|
for (const data of datas["results"]["list"]) {
|
|
manga.push({
|
|
name: this.stringUTF8(data["name"]),
|
|
imageUrl: data["cover"],
|
|
link: data["path_word"]
|
|
});
|
|
}
|
|
return {
|
|
list: manga,
|
|
hasNextPage: true
|
|
};
|
|
}
|
|
|
|
async getPopular(page) {
|
|
return await this.getManga(`/api/v3/comics?free_type=1&limit=16&offset=${(page-1)*16}&ordering=-popular&_update=true`);
|
|
}
|
|
|
|
async getLatestUpdates(page) {
|
|
return await this.getManga(`/api/v3/comics?free_type=1&limit=16&offset=${(page-1)*16}&ordering=-datetime_updated&_update=true`);
|
|
}
|
|
|
|
async search(query, page, filters) {
|
|
if (query != "") {
|
|
const res = await new Client().get(this.source.apiUrl + `/api/v3/search/comic?platform=1&q=${query}&limit=16&offset=${(page -1)*16}&q_type=&_update=true`, {
|
|
"webp": '1',
|
|
"region": '1',
|
|
"platform": '1',
|
|
"version": '2022.10.20',
|
|
"accept": 'application/json',
|
|
'content-encoding': 'gzip, compress, br'
|
|
});
|
|
const datas = JSON.parse(res.body)["results"]["list"];
|
|
const manga = [];
|
|
for (const data of datas) {
|
|
manga.push({
|
|
name: this.stringUTF8(data["name"]),
|
|
imageUrl: data["cover"],
|
|
link: data["path_word"]
|
|
});
|
|
}
|
|
return {
|
|
list: manga,
|
|
hasNextPage: true
|
|
};
|
|
}
|
|
var type, region, sort;
|
|
for (const filter of filters) {
|
|
if (filter["type"] == "type") {
|
|
type = filter["values"][filter["state"]]["value"];
|
|
}
|
|
if (filter["type"] == "region") {
|
|
region = filter["values"][filter["state"]]["value"];
|
|
}
|
|
if (filter["type"] == "sort") {
|
|
sort = filter["values"][filter["state"]]["value"];
|
|
}
|
|
}
|
|
return await this.getManga(`/api/v3/comics?free_type=1&limit=16&offset=${(page-1)*16}&theme=${type}&top=${region}&ordering=${sort}&_update=true`);
|
|
}
|
|
|
|
async getDetail(url) {
|
|
url = url.substringAfter("/comic/");
|
|
const res = await new Client().get(this.source.apiUrl + `/api/v3/comic2/${url}`);
|
|
const data = JSON.parse(res.body)["results"]["comic"];
|
|
const title = this.stringUTF8(data["name"]);
|
|
const cover = data["cover"];
|
|
const desc = this.stringUTF8(data["brief"]);
|
|
const author_ = [];
|
|
for (const a of data["author"]) {
|
|
author_.push(this.stringUTF8(a["name"]));
|
|
}
|
|
const author = author_.join(",");
|
|
const status = data["status"]["value"];
|
|
const genres = data["theme"].map(e => this.stringUTF8(e["name"]));
|
|
const chapters = [];
|
|
const res_ = await new Client().get(this.source.baseUrl + `/comicdetail/${url}/chapters`, {
|
|
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36"
|
|
});
|
|
const ch_text = JSON.parse(res_.body)["results"];
|
|
const chapter_datas = JSON.parse(this.decode(ch_text));
|
|
for (const ch of chapter_datas["groups"]["default"]["chapters"]) {
|
|
chapters.push({
|
|
name: ch["name"],
|
|
url: url + "|" + ch["id"]
|
|
});
|
|
}
|
|
chapters.reverse();
|
|
return {
|
|
name: title,
|
|
imageUrl: cover,
|
|
description: desc,
|
|
author: author,
|
|
status: status,
|
|
genre: genres,
|
|
episodes: chapters,
|
|
link: "/comic/" + url
|
|
}
|
|
}
|
|
|
|
async getPageList(url) {
|
|
const urls = url.split("|");
|
|
const res = await new Client().get(this.source.baseUrl + `/comic/${urls[0]}/chapter/${urls[1]}`, {
|
|
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36"
|
|
});
|
|
const img_text = res.body.match(/contentKey="(.*)"/)[1];
|
|
const results = JSON.parse(this.decode(img_text));
|
|
return results.map(e => e["url"]);
|
|
}
|
|
|
|
getFilterList() {
|
|
return [{
|
|
type: "type",
|
|
name: "分类",
|
|
type_name: "SelectFilter",
|
|
values: [{
|
|
type_name: "SelectOption",
|
|
name: "全部",
|
|
value: ""
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "愛情",
|
|
value: "aiqing"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "歡樂向",
|
|
value: "huanlexiang"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "冒险",
|
|
value: "maoxian"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "奇幻",
|
|
value: "qihuan"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "百合",
|
|
value: "baihe"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "校园",
|
|
value: "xiaoyuan"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "科幻",
|
|
value: "kehuan"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "東方",
|
|
value: "dongfang"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "生活",
|
|
value: "shenghuo"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "轻小说",
|
|
value: "qingxiaoshuo"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "格鬥",
|
|
value: "gedou"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "耽美",
|
|
value: "danmei"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "悬疑",
|
|
value: "xuanyi"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "神鬼",
|
|
value: "shengui"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "其他",
|
|
value: "qita"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "职场",
|
|
value: "zhichang"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "萌系",
|
|
value: "mengxi"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "治愈",
|
|
value: "zhiyu"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "長條",
|
|
value: "changtiao"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "四格",
|
|
value: "sige"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "舰娘",
|
|
value: "jianniang"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "节操",
|
|
value: "jiecao"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "TL",
|
|
value: "teenslove"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "竞技",
|
|
value: "jingji"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "搞笑",
|
|
value: "gaoxiao"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "伪娘",
|
|
value: "weiniang"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "热血",
|
|
value: "rexue"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "後宮",
|
|
value: "hougong"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "美食",
|
|
value: "meishi"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "性转换",
|
|
value: "xingzhuanhuan"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "侦探",
|
|
value: "zhentan"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "励志",
|
|
value: "lizhi"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "AA",
|
|
value: "aa"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "彩色",
|
|
value: "COLOR"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "音乐舞蹈",
|
|
value: "yinyuewudao"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "异世界",
|
|
value: "yishijie"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "战争",
|
|
value: "zhanzheng"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "历史",
|
|
value: "lishi"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "机战",
|
|
value: "jizhan"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "惊悚",
|
|
value: "jingsong"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "C99",
|
|
value: "comiket99"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "恐怖",
|
|
value: "恐怖"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "都市",
|
|
value: "dushi"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "C97",
|
|
value: "comiket97"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "穿越",
|
|
value: "chuanyue"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "C96",
|
|
value: "comiket96"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "重生",
|
|
value: "chongsheng"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "魔幻",
|
|
value: "mohuan"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "宅系",
|
|
value: "zhaixi"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "武侠",
|
|
value: "wuxia"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "C98",
|
|
value: "C98"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "生存",
|
|
value: "shengcun"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "C95",
|
|
value: "comiket95"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "FATE",
|
|
value: "fate"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "無修正",
|
|
value: "Uncensored"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "转生",
|
|
value: "zhuansheng"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "LoveLive",
|
|
value: "loveLive"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "男同",
|
|
value: "nantong"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "仙侠",
|
|
value: "xianxia"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "玄幻",
|
|
value: "xuanhuan"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "真人",
|
|
value: "zhenren"
|
|
},
|
|
],
|
|
},
|
|
{
|
|
type: "region",
|
|
name: "地区",
|
|
type_name: "SelectFilter",
|
|
values: [{
|
|
type_name: "SelectOption",
|
|
name: "全部",
|
|
value: ""
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "日本",
|
|
value: "japan"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "韩国",
|
|
value: "korea"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "欧美",
|
|
value: "west"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "完结",
|
|
value: "finish"
|
|
},
|
|
],
|
|
},
|
|
{
|
|
type: "sort",
|
|
name: "排序",
|
|
type_name: "SelectFilter",
|
|
values: [{
|
|
type_name: "SelectOption",
|
|
name: "更新时间⬇️",
|
|
value: "-datetime_updated"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "更新时间⬆️",
|
|
value: "datetime_updated"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "热度⬇️",
|
|
value: "-popular"
|
|
},
|
|
{
|
|
type_name: "SelectOption",
|
|
name: "热度⬆️",
|
|
value: "popular"
|
|
},
|
|
],
|
|
},
|
|
];
|
|
}
|
|
|
|
getSourcePreferences() {
|
|
throw new Error("getSourcePreferences not implemented");
|
|
}
|
|
} |