mirror of
https://github.com/kodjodevf/mangayomi-extensions.git
synced 2026-02-14 10:51:17 +00:00
371 lines
16 KiB
JavaScript
371 lines
16 KiB
JavaScript
const mangayomiSources = [{
|
||
"name": "Mangalib",
|
||
"lang": "ru",
|
||
"baseUrl": "https://mangalib.org/ru",
|
||
"apiUrl": "https://api.mangalib.me/api",
|
||
"iconUrl": "https://mangalib.org/static/images/logo/ml/icon-180.png",
|
||
"typeSource": "single",
|
||
"isManga": true,
|
||
"isNsfw": true,
|
||
"version": "0.0.1",
|
||
"dateFormat": "",
|
||
"dateFormatLocale": "",
|
||
"pkgPath": "manga/src/ru/mangalib.js"
|
||
}];
|
||
|
||
// filters: https://api.mangalib.me/api/constants?fields[]=genres&fields[]=tags&fields[]=types&fields[]=scanlateStatus&fields[]=status&fields[]=format&fields[]=ageRestriction
|
||
// directory: https://api.mangalib.me/api/manga?q=encodeURIComponent(query)&sort_by=(|chap_count|views|rate_avg|releaseDate|last_chapter_at|created_at|name|rus_name)&sort_type=(|asc)&page=2
|
||
// details: https://api.mangalib.me/api/manga/34466--jeonjijeog-dogja-sijeom_?fields[]=chap_count&fields[]=summary&fields[]=genres&fields[]=authors&fields[]=artists
|
||
// chapters: https://api.mangalib.me/api/manga/34466--jeonjijeog-dogja-sijeom_/chapters
|
||
// pages: https://api.mangalib.me/api/manga/34466--jeonjijeog-dogja-sijeom_/chapter?number=147&volume=1
|
||
// image servers: https://api.mangalib.me/api/constants?fields[]=imageServers
|
||
class DefaultExtension extends MProvider {
|
||
constructor () {
|
||
super();
|
||
this.client = new Client();
|
||
this.apiHeaders = {
|
||
'accept': '*/*',
|
||
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36'};
|
||
this.getFilterUrl = this.source.apiUrl + '/constants?fields[]=genres&fields[]=tags&fields[]=types&fields[]=scanlateStatus&fields[]=status&fields[]=format&fields[]=ageRestriction';
|
||
}
|
||
parseStatus(status) {
|
||
return {
|
||
"Онгоинг": 0,
|
||
"Завершён": 1,
|
||
"Приостановлен": 2,
|
||
"Выпуск прекращён": 3,
|
||
"Анонс": 4
|
||
}[status] ?? 5;
|
||
}
|
||
async parseMangaList(url) {
|
||
const res = await this.client.get(url, this.apiHeaders);
|
||
const json = JSON.parse(res.body);
|
||
let mangas = json.data.map(manga => ({
|
||
name: manga.rus_name,
|
||
imageUrl: manga.cover.default,
|
||
link: manga.slug_url
|
||
}));
|
||
return { "list": mangas, "hasNextPage": json.meta.has_next_page };
|
||
}
|
||
async getPopular(page) {
|
||
return await this.parseMangaList(this.source.apiUrl + `/manga?page=${page}`);
|
||
}
|
||
async getLatestUpdates(page) {
|
||
return await this.parseMangaList(this.source.apiUrl + `/manga?page=${page}&sort_by=last_chapter_at`);
|
||
}
|
||
async search(query, page, filters) {
|
||
let url = `${this.source.apiUrl}/manga?q=${encodeURIComponent(query)}`
|
||
// Search sometimes failed because filters were empty. I experienced this mostly on android...
|
||
if (!filters || filters.length == 0) {
|
||
return await this.parseMangaList(url + `&page=${page}`);
|
||
}
|
||
for (const filter of filters[0].state) {
|
||
if (filter.state == true)
|
||
url += `&types[]=${filter.value}`;
|
||
}
|
||
for (const filter of filters[1].state) {
|
||
if (filter.state == true)
|
||
url += `&caution[]=${filter.value}`;
|
||
}
|
||
|
||
const minChapF = filters[2].state[0];
|
||
const maxChapF = filters[2].state[1];
|
||
const minChap = minChapF.values[minChapF.state].value;
|
||
const maxChap = maxChapF.values[maxChapF.state].value;
|
||
url += minChap ? `&chap_count_min=${minChap}` : '';
|
||
url += maxChap ? `&chap_count_max=${maxChap}` : '';
|
||
|
||
const minYearF = filters[3].state[0];
|
||
const maxYearF = filters[3].state[1];
|
||
const minYear = minYearF.values[minYearF.state].value;
|
||
const maxYear = maxYearF.values[maxYearF.state].value;
|
||
url += minYear ? `&year_min=${minYear}` : '';
|
||
url += maxYear ? `&year_max=${maxYear}` : '';
|
||
|
||
for (const filter of filters[4].state) {
|
||
if (filter.state == 1)
|
||
url += `&genres[]=${filter.value}`;
|
||
else if (filter.state == 2)
|
||
url += `&genres_exclude[]=${filter.value}`;
|
||
}
|
||
for (const filter of filters[5].state) {
|
||
if (filter.state == true)
|
||
url += `&status[]=${filter.value}`;
|
||
}
|
||
for (const filter of filters[6].state) {
|
||
if (filter.state == true)
|
||
url += `&scanlate_status[]=${filter.value}`;
|
||
}
|
||
for (const filter of filters[7].state) {
|
||
if (filter.state == 1)
|
||
url += `&format[]=${filter.value}`;
|
||
else if (filter.state == 2)
|
||
url += `&format_exclude[]=${filter.value}`;
|
||
}
|
||
const sortVal = filters[8].values[filters[8].state.index].value;
|
||
const sortType = filters[8].state.ascending ? 'asc' : '';
|
||
url += sortVal ? `&sort_by=${sortVal}` : '';
|
||
url += sortType ? `&sort_type=${sortType}` : '';
|
||
|
||
return await this.parseMangaList(url + `&page=${page}`);
|
||
}
|
||
async getDetail(url) {
|
||
const infoRes = await this.client.get(`${this.source.apiUrl}/manga/${url}?fields[]=chap_count&fields[]=summary&fields[]=genres&fields[]=authors&fields[]=artists`, this.apiHeaders);
|
||
const chapterRes = await this.client.get(`${this.source.apiUrl}/manga/${url}/chapters`, this.apiHeaders);
|
||
|
||
const info = JSON.parse(infoRes.body).data;
|
||
const chapters = JSON.parse(chapterRes.body).data;
|
||
const chapterBaseUrl = `${this.source.apiUrl}/manga/${url}/chapter`;
|
||
|
||
return {
|
||
name: info.name,
|
||
imageUrl: info.cover.default,
|
||
author: info.authors.map(x => x.name).join(', '),
|
||
artist: info.artists.map(x => x.name).join(', '),
|
||
status: this.parseStatus(info.status.label),
|
||
description: info.summary,
|
||
genre: info.genres.map(x => x.name),
|
||
chapters: chapters.map(c => ({
|
||
name: `Том ${c.volume} Глава ${c.number}` + (c.name ? `: ${c.name}` : ''),
|
||
url: `${chapterBaseUrl}?number=${c.number}&volume=${c.volume}`,
|
||
dateUpload: new Date(c.branches[0].created_at).valueOf().toString(),
|
||
scanlator: c.branches[0].teams.map(x => x.name).join(', ')
|
||
})).reverse()
|
||
};
|
||
}
|
||
// For manga chapter pages
|
||
async getPageList(url) {
|
||
const serverId = new SharedPreferences().get('imageServer');
|
||
|
||
let res = await this.client.get(`${this.source.apiUrl}/constants?fields[]=imageServers`, this.apiHeaders);
|
||
const imageServers = JSON.parse(res.body).data.imageServers;
|
||
const imageServer = imageServers.find(x => x.id == serverId).url;
|
||
|
||
res = await this.client.get(url, this.apiHeaders);
|
||
const chapter = JSON.parse(res.body).data;
|
||
return chapter.pages.map(img => ({url: imageServer + img.url, headers: this.apiHeaders}));
|
||
}
|
||
getFilterList() {
|
||
const chapterCounts = ['1','5','10','20','30','40','50','100','200','500','1000','2000','5000','10000'].map(x => [x, x]);
|
||
const years = [...range(1980, new Date().getFullYear() + 1, -1), ...range(1930, 1971, -10)].map(x => {
|
||
x = x.toString();
|
||
return [x, x];
|
||
});
|
||
return [
|
||
{
|
||
type_name: "GroupFilter",
|
||
type: "type",
|
||
name: "Тип",
|
||
state: [
|
||
["Манга", 1],
|
||
["OEL-манга", 4],
|
||
["Манхва", 5],
|
||
["Маньхуа", 6],
|
||
["Руманга", 8],
|
||
["Комикс", 9]
|
||
].map(x => ({ type_name: 'CheckBox', name: x[0], value: `${x[1]}` }))
|
||
},
|
||
{
|
||
type_name: "GroupFilter",
|
||
type: "age_restriction",
|
||
name: "возрастной рейтинг",
|
||
state: [
|
||
["Нет", 0],
|
||
["6+", 1],
|
||
["12+", 2],
|
||
["16+", 3],
|
||
["18+", 4]
|
||
].map(x => ({ type_name: 'CheckBox', name: x[0], value: `${x[1]}` }))
|
||
},
|
||
{
|
||
type_name: "GroupFilter",
|
||
type: "chapter_count",
|
||
name: "Количество глав",
|
||
state: [
|
||
{
|
||
type_name: "SelectFilter",
|
||
type: "chap_count_min",
|
||
name: "от",
|
||
state: 0,
|
||
values: [['от', ''], ...chapterCounts].map(x => ({ type_name: 'SelectOption', name: x[0], value: x[1] }))
|
||
},
|
||
{
|
||
type_name: "SelectFilter",
|
||
type: "chap_count_max",
|
||
name: "до",
|
||
state: 0,
|
||
values: [['до', ''], ...chapterCounts].map(x => ({ type_name: 'SelectOption', name: x[0], value: x[1] }))
|
||
}
|
||
]
|
||
},
|
||
{
|
||
type_name: "GroupFilter",
|
||
type: "years",
|
||
name: "Год выпуска",
|
||
state: [
|
||
{
|
||
type_name: "SelectFilter",
|
||
type: "year_min",
|
||
name: "от",
|
||
state: 0,
|
||
values: [['от', ''], ...years].map(x => ({ type_name: 'SelectOption', name: x[0], value: x[1] }))
|
||
},
|
||
{
|
||
type_name: "SelectFilter",
|
||
type: "year_max",
|
||
name: "до",
|
||
state: 0,
|
||
values: [['до', ''], ...years].map(x => ({ type_name: 'SelectOption', name: x[0], value: x[1] }))
|
||
}
|
||
]
|
||
},
|
||
{
|
||
type_name: "GroupFilter",
|
||
type: "genre",
|
||
name: "Жанры",
|
||
state: [
|
||
["Арт", 32, false],
|
||
["Безумие", 91, false],
|
||
["Боевик", 34, false],
|
||
["Боевые искусства", 35, false],
|
||
["Вампиры", 36, false],
|
||
["Военное", 89, false],
|
||
["Гарем", 37, false],
|
||
["Гендерная интрига", 38, false],
|
||
["Героическое фэнтези", 39, false],
|
||
["Демоны", 81, false],
|
||
["Детектив", 40, false],
|
||
["Детское", 88, false],
|
||
["Драма", 43, false],
|
||
["Игра", 44, false],
|
||
["Исекай", 79, false],
|
||
["История", 45, false],
|
||
["Киберпанк", 46, false],
|
||
["Кодомо", 76, false],
|
||
["Комедия", 47, false],
|
||
["Космос", 83, false],
|
||
["Магия", 85, false],
|
||
["Махо-сёдзё", 48, false],
|
||
["Машины", 90, false],
|
||
["Меха", 49, false],
|
||
["Мистика", 50, false],
|
||
["Музыка", 80, false],
|
||
["Научная фантастика", 51, false],
|
||
["Омегаверс", 77, false],
|
||
["Пародия", 86, false],
|
||
["Повседневность", 52, false],
|
||
["Полиция", 82, false],
|
||
["Постапокалиптика", 53, false],
|
||
["Приключения", 54, false],
|
||
["Психология", 55, false],
|
||
["Романтика", 56, false],
|
||
["Самурайский боевик", 57, false],
|
||
["Сверхъестественное", 58, false],
|
||
["Сёдзё", 59, false],
|
||
["Сёдзё-ай", 60, false],
|
||
["Сёнэн-ай", 62, true],
|
||
["Спорт", 63, false],
|
||
["Супер сила", 87, false],
|
||
["Сэйнэн", 64, false],
|
||
["Трагедия", 65, false],
|
||
["Триллер", 66, false],
|
||
["Ужасы", 67, false],
|
||
["Фантастика", 68, false],
|
||
["Фэнтези", 69, false],
|
||
["Хентай", 84, false],
|
||
["Эротика", 71, true],
|
||
["Этти", 72, false]
|
||
].map(x => ({ type_name: 'TriState', name: x[0], value: `${x[1]}` }))
|
||
},
|
||
{
|
||
type_name: "GroupFilter",
|
||
type: "status",
|
||
name: "Статус титула",
|
||
state: [
|
||
["Онгоинг", 1],
|
||
["Завершён", 2],
|
||
["Анонс", 3],
|
||
["Приостановлен", 4],
|
||
["Выпуск прекращён", 5]
|
||
].map(x => ({ type_name: 'CheckBox', name: x[0], value: `${x[1]}` }))
|
||
},
|
||
{
|
||
type_name: "GroupFilter",
|
||
type: "translation_status",
|
||
name: "Статус перевода",
|
||
state: [
|
||
["Продолжается", 1],
|
||
["Завершён", 2],
|
||
["Заморожен", 3],
|
||
["Заброшен", 4]
|
||
].map(x => ({ type_name: 'CheckBox', name: x[0], value: `${x[1]}` }))
|
||
},
|
||
{
|
||
type_name: "GroupFilter",
|
||
type: "format",
|
||
name: "Формат выпуска",
|
||
state: [
|
||
["4-кома (Ёнкома)", 1],
|
||
["Сборник", 2],
|
||
["Додзинси", 3],
|
||
["В цвете", 4],
|
||
["Сингл", 5],
|
||
["Веб", 6],
|
||
["Вебтун", 7]
|
||
].map(x => ({ type_name: 'TriState', name: x[0], value: `${x[1]}` }))
|
||
},
|
||
{
|
||
type_name: "SortFilter",
|
||
type: "sort",
|
||
name: "Сортировать",
|
||
state: {
|
||
type_name: "SortState",
|
||
index: 0,
|
||
ascending: false
|
||
},
|
||
values: [
|
||
['По популярности', ''],
|
||
['По рейтингу', 'rate_avg'],
|
||
['По просмотрам', 'views'],
|
||
['Количество глав', 'chap_count'],
|
||
['дата релиза', 'releaseDate'],
|
||
['дата обновления', 'last_chapter_at'],
|
||
['дата добавления', 'created_at'],
|
||
['По названию (A-Z)', 'name'],
|
||
['По названию (A-Я)', 'rus_name']
|
||
].map(x => ({ type_name: 'SelectOption', name: x[0], value: x[1] }))
|
||
}
|
||
];
|
||
}
|
||
getSourcePreferences() {
|
||
const imageServers = ['Первый', 'Второй', 'Сжатия', 'Скачивание', 'Crop pages'];
|
||
const imageServerValuess = ['main', 'secondary', 'compress', 'download', 'crop'];
|
||
return [
|
||
{
|
||
key: 'imageServer',
|
||
listPreference: {
|
||
title: 'Image Server',
|
||
summary: '',
|
||
valueIndex: 0,
|
||
entries: imageServers,
|
||
entryValues: imageServerValuess
|
||
}
|
||
}
|
||
];
|
||
}
|
||
}
|
||
|
||
function range (first, last, step) {
|
||
if (last <= first)
|
||
return [];
|
||
if (!step) {
|
||
step = 1;
|
||
}
|
||
if (!last) {
|
||
last = first;
|
||
first = 0;
|
||
}
|
||
const start = step > 0 ? first : last - 1;
|
||
let length = Math.ceil((last - first) / Math.abs(step));
|
||
return Array.from(new Array(length), (x, i) => start + i * step);
|
||
} |