Files
kodjodevf-mangayomi-extensions/javascript/novel/src/ar/kolnovel.js

494 lines
17 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// prettier-ignore
const mangayomiSources = [{
"name": "ملوك الروايات",
"lang": "ar",
"baseUrl": "https://kolnovel.com",
"apiUrl": "",
"iconUrl": "https://www.google.com/s2/favicons?sz=256&domain=https://kolnovel.com",
"typeSource": "single",
"itemType": 2,
"version": "0.0.1",
"pkgPath": "novel/src/ar/kolnovel.js",
"notes": ""
}];
class DefaultExtension extends MProvider {
headers = {
"Sec-Fetch-Mode": "cors",
"Accept-Encoding": "gzip, deflate",
"User-Agent":
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
};
getHeaders(url) {
throw new Error("getHeaders not implemented");
}
cleanTitle(title) {
if (!/[-_&@#%^)(*\s]*(كول|kol)/i.test(title)) return title;
title = title.replace(/[-_&@#%^)(*\s]*(كول|kol)/i, "");
title = title.replace(/[-_&@#%^)(*+،؛:]+/g, " ");
title = title.replace(/\s+/g, " ").trim();
return title;
}
novelFromElement(res) {
const doc = new Document(res.body);
const elements = doc.select("div.listupd article");
const list = [];
for (const el of elements) {
const name = this.cleanTitle(el.selectFirst("h2 a").text);
const imageUrl = el.selectFirst("img").getSrc;
const link = el.selectFirst("h2 a").getHref;
list.push({ name, imageUrl, link });
}
const hasNextPage = doc.selectFirst("div.hpage > a.r").text == "Next ";
return { list, hasNextPage };
}
novelFromJson(json) {
const list = [];
for (const el of json.series[0].all) {
const name = this.cleanTitle(el.post_title);
const imageUrl = el.post_image;
const link = el.post_link;
list.push({ name, imageUrl, link });
}
return { list, hasNextPage: false };
}
async getPopular(page) {
const res = await new Client().get(
`${this.getBaseUrl()}/series/?page=${page}&order=popular`,
this.headers,
);
return this.novelFromElement(res);
}
async getLatestUpdates(page) {
const res = await new Client().get(
`${this.getBaseUrl()}/series/?page=${page}&order=update`,
this.headers,
);
return this.novelFromElement(res);
}
async search(query, page, filters) {
const keyword = query.trim().replace(/\s+/g, "+");
if (keyword) {
const res = await new Client().post(
`${this.getBaseUrl()}/wp-admin/admin-ajax.php`,
{
"Content-Type": "application/x-www-form-urlencoded",
},
`action=ts_ac_do_search&ts_ac_query=${keyword}`,
);
return this.novelFromJson(JSON.parse(res.body));
}
let url = `${this.getBaseUrl()}/series/?page=${page}`;
filters.forEach((filter) => {
if (filter.type === "GenreFilter") {
const genre = filter.state.filter((e) => e.state);
genre.forEach((gen) => (url += `${this.ll(url)}genre[]=${gen.value}`));
} else if (filter.type === "TypeFilter") {
const type = filter.state.filter((e) => e.state);
type.forEach((ty) => (url += `${this.ll(url)}type[]=${ty.value}`));
} else if (filter.type === "OrderFilter") {
if (filter.values?.[filter.state]?.value)
url += `${this.ll(url)}order=${filter.values[filter.state].value}`;
} else if (filter.type === "StatusFilter") {
if (filter.values?.[filter.state]?.value)
url += `${this.ll(url)}status=${filter.values[filter.state].value}`;
}
});
const res = await new Client().get(url, this.headers);
return this.novelFromElement(res);
}
toStatus(status) {
return (
{
Ongoing: 0,
Completed: 1,
Hiatus: 2,
}[status] ?? 5 // 5 => unknown
);
}
async getDetail(url) {
const res = await new Client().get(url, this.headers);
const doc = new Document(res.body);
const info = doc.selectFirst("div.sertoinfo");
const subInfo = info.selectFirst("div.sertoauth");
const rewName = info.selectFirst("h1.entry-title").text;
const name = this.cleanTitle(rewName);
const imageUrl = doc.selectFirst("img.attachment-post-thumbnail")?.getSrc;
const scanlator = subInfo.selectFirst(".serl:contains('المترجم') a").text;
let description =
info.selectFirst("div.sersys.entry-content p").text + "\n\n";
const lang = subInfo.selectFirst(
".serl:contains('اللغة الأم') .serval",
)?.text;
if (lang) description += `اللغة الأم: ${lang}\n`;
const releaseYear = subInfo.selectFirst(
".serl:contains('صدر في سنة') .serval",
)?.text;
if (releaseYear) description += `سنة الصدور: ${releaseYear}\n`;
const types = subInfo
.select(".serl:contains('نوع') a")
.map((el) => el.text)
.join(", ");
if (types) description += `الانواع: ${types}\n`;
const altTitle = this.cleanTitle(info.selectFirst("span.alter")?.text);
if (altTitle) description += `اسم آخر للعمل: ${altTitle}\n`;
const genre = info.select("div.sertogenre a").map((el) => el.text);
const author = subInfo.selectFirst(".serl:contains('الكاتب') a").text;
const status = this.toStatus(info.selectFirst("div.sertostat span").text);
const chapters = [];
for (const el of doc.select("div.sertobody div.bixbox ul li > a")) {
const url = el.getHref;
const dateUpload = this.parseDate(el.selectFirst("div.epl-date").text);
// Chapter name
let title = el.selectFirst("div.epl-title").text.trim();
const num = el.selectFirst("div.epl-num").text.trim();
if (title.includes(num)) title = title.replace(num, "").trim();
if (title.includes(rewName)) title = title.replace(rewName, "").trim();
if (title.includes(name)) title = title.replace(name, "").trim();
const numMatch = num.match(/(?:الفصل|chapter)\s+(\d+(?:\.\d+)?)/i);
if (numMatch) {
title = title.replace(numMatch[0], "").trim();
if (!title.includes(`(${numMatch[1]})`)) {
title = title.replace(numMatch[1], "").trim();
}
}
title = title.replace(/\s{2,}/g, " ").trim();
const finalName =
title && num ? `${num}: ${title}` : title ? title : num ? num : "?";
chapters.push({ name: finalName, url, dateUpload, scanlator });
}
return {
name,
imageUrl,
description,
genre,
author,
status,
chapters,
};
}
extractIdFromUrl(url) {
const match = url.match(/-(\d+)\/?$/);
return match ? match[1] : null;
}
// For novel html content
async getHtmlContent(name, url) {
const id = this.extractIdFromUrl(url);
const res = await new Client().get(
`${this.getBaseUrl()}/wp-json/wp/v2/posts/${id}`,
this.headers,
);
return this.cleanHtmlContent(JSON.parse(res.body));
}
// Clean html up for reader
async cleanHtmlContent(html) {
return `<h2 style="text-align: center;">${html.title.rendered}</h2><hr><br>${html.content.rendered}`;
}
getFilterList() {
return [
{
type: "StatusFilter",
name: "الحالة",
type_name: "SelectFilter",
values: [
{
type_name: "SelectOption",
name: "الكل",
value: "",
},
{
type_name: "SelectOption",
name: "مستمر",
value: "ongoing",
},
{
type_name: "SelectOption",
name: "متوقف مؤقتًا",
value: "hiatus",
},
{
type_name: "SelectOption",
name: "مكتمل",
value: "completed",
},
],
state: 0,
},
{
type: "OrderFilter",
name: "ترتيب حسب",
type_name: "SelectFilter",
values: [
{
type_name: "SelectOption",
name: "الإعداد الأولي",
value: "",
},
{
type_name: "SelectOption",
name: "A-Z",
value: "title",
},
{
type_name: "SelectOption",
name: "Z-A",
value: "titlereverse",
},
{
type_name: "SelectOption",
name: "أخر التحديثات",
value: "update",
},
{
type_name: "SelectOption",
name: "أخر ما تم إضافته",
value: "latest",
},
{
type_name: "SelectOption",
name: "الرائجة",
value: "popular",
},
{
type_name: "SelectOption",
name: "التقييم",
value: "rating",
},
],
state: 0,
},
{
type_name: "GroupFilter",
type: "GenreFilter",
name: "تصنيف",
state: [
["Romance", "romance"],
["Shounen Ai", "shounen-ai"],
["Wuxia", "wuxia"],
["Xianxia", "xianxia"],
["XUANHUAN", "xuanhuan"],
[
"أبطال خارقين",
"%d8%a3%d8%a8%d8%b7%d8%a7%d9%84-%d8%ae%d8%a7%d8%b1%d9%82%d9%8a%d9%86",
],
["أساطير", "%d8%a3%d8%b3%d8%a7%d8%b7%d9%8a%d8%b1"],
["أشباح", "%d8%a3%d8%b4%d8%a8%d8%a7%d8%ad"],
["أكشن", "action"],
["ألعاب", "%d8%a3%d9%84%d8%b9%d8%a7%d8%a8"],
["إثارة", "excitement"],
["إسلامي", "%d8%a5%d8%b3%d9%84%d8%a7%d9%85%d9%8a"],
["إنتقال الى عالم أخر", "isekai"],
["إيتشي", "etchi"],
["اكاديمي", "%d8%a7%d9%83%d8%a7%d8%af%d9%8a%d9%85%d9%8a"],
["اكشن", "%d8%a7%d9%83%d8%b4%d9%86"],
["الإثارة", "%d8%a7%d9%84%d8%a5%d8%ab%d8%a7%d8%b1%d8%a9"],
["الخيال العلمي", "sci-fi"],
["الدراما", "%d8%a7%d9%84%d8%af%d8%b1%d8%a7%d9%85%d8%a7"],
[
"المغامرات",
"%d8%a7%d9%84%d9%85%d8%ba%d8%a7%d9%85%d8%b1%d8%a7%d8%aa",
],
["انتقام", "%d8%a7%d9%86%d8%aa%d9%82%d8%a7%d9%85"],
["بطل مضاد", "%d8%a8%d8%b7%d9%84-%d9%85%d8%b6%d8%a7%d8%af"],
["بطل ناضج", "%d8%a8%d8%b7%d9%84-%d9%86%d8%a7%d8%b6%d8%ac"],
["بقاء", "%d8%a8%d9%82%d8%a7%d8%a1"],
[
"بناء مملكة",
"%d8%a8%d9%86%d8%a7%d8%a1-%d9%85%d9%85%d9%84%d9%83%d8%a9",
],
["بوليسي", "policy"],
["تاريخ", "%d8%aa%d8%a7%d8%b1%d9%8a%d8%ae"],
["تاريخي", "historical"],
["تحقيقات", "%d8%aa%d8%ad%d9%82%d9%8a%d9%82"],
["تشويق", "%d8%aa%d8%b4%d9%88%d9%8a%d9%82"],
["تقمص شخصيات", "rpg"],
["تلاعب", "%d8%aa%d9%84%d8%a7%d8%b9%d8%a8"],
["تناسخ", "%d8%aa%d9%86%d8%a7%d8%b3%d8%ae"],
["جريمة", "crime"],
["جوسى", "josei"],
["جوسي", "%d8%ac%d9%88%d8%b3%d9%8a"],
["حريم", "harem"],
[
"حل الألغاز",
"%d8%ad%d9%84-%d8%a7%d9%84%d8%a3%d9%84%d8%ba%d8%a7%d8%b2",
],
["حياة مدرسية", "school-life"],
[
"خارق للطبيعة",
"%d8%ae%d8%a7%d8%b1%d9%82-%d9%84%d9%84%d8%b7%d8%a8%d9%8a%d8%b9%d8%a9",
],
["خيال", "%d8%ae%d9%8a%d8%a7%d9%84"],
["خيال علمي", "%d8%ae%d9%8a%d8%a7%d9%84-%d8%b9%d9%84%d9%85%d9%8a"],
["خيالي", "%d8%ae%d9%8a%d8%a7%d9%84%d9%8a"],
["خيالي(فانتازيا)", "fantasy"],
["دراما", "drama"],
["درامي", "%d8%af%d8%b1%d8%a7%d9%85%d9%8a"],
["رعب", "horror"],
["رعب كوني", "%d8%b1%d8%b9%d8%a8-%d9%83%d9%88%d9%86%d9%8a"],
["رعب نفسي", "%d8%b1%d8%b9%d8%a8-%d9%86%d9%81%d8%b3%d9%8a"],
["رومانسي", "romantic"],
["رومانسية", "%d8%b1%d9%88%d9%85%d8%a7%d9%86%d8%b3%d9%8a%d8%a9"],
["رومنسية", "%d8%b1%d9%88%d9%85%d9%86%d8%b3%d9%8a%d8%a9"],
["زنزانة", "%d8%b2%d9%86%d8%b2%d8%a7%d9%86%d8%a9"],
["زيانشيا", "%d8%b2%d9%8a%d8%a7%d9%86%d8%b4%d9%8a%d8%a7"],
["ستيم بانك", "%d8%b3%d8%aa%d9%8a%d9%85-%d8%a8%d8%a7%d9%86%d9%83"],
["سحر", "magic"],
[
"سفر بالزمن",
"%d8%b3%d9%81%d8%b1-%d8%a8%d8%a7%d9%84%d8%b2%d9%85%d9%86",
],
[
"سفر عبر الزمن",
"%d8%b3%d9%81%d8%b1-%d8%b9%d8%a8%d8%b1-%d8%a7%d9%84%d8%b2%d9%85%d9%86",
],
["سياسة", "%d8%b3%d9%8a%d8%a7%d8%b3%d8%a9"],
["سينن", "senen"],
["شريحة من الحياة", "slice-of-life"],
["شعر", "%d8%b4%d8%b9%d8%b1"],
["شوانهوان", "%d8%b4%d9%88%d8%a7%d9%86%d9%87%d9%88%d8%a7%d9%86"],
["شوجو", "shojo"],
["شونين", "shonen"],
["طبي", "medical"],
["ظواهر خارقة للطبيعة", "supernatural"],
["عائلي", "%d8%b9%d8%a7%d8%a6%d9%84%d9%8a"],
["عموض", "%d8%b9%d9%85%d9%88%d8%b6"],
["غموض", "mysteries"],
["فانتازي", "%d9%81%d8%a7%d9%86%d8%aa%d8%a7%d8%b2%d9%8a"],
["فانتازيا", "%d9%81%d8%a7%d9%86%d8%aa%d8%a7%d8%b2%d9%8a%d8%a7"],
["فانفيك", "%d9%81%d8%a7%d9%86%d9%81%d9%8a%d9%83"],
["فنتازيا", "%d9%81%d9%86%d8%aa%d8%a7%d8%b2%d9%8a%d8%a7"],
["فنون القتال", "martial-arts"],
["فنون قتال", "%d9%81%d9%86%d9%88%d9%86-%d9%82%d8%aa%d8%a7%d9%84"],
["قصة قصيرة", "%d9%82%d8%b5%d8%a9-%d9%82%d8%b5%d9%8a%d8%b1%d8%a9"],
["قوة خارقة", "%d9%82%d9%88%d8%a9-%d8%ae%d8%a7%d8%b1%d9%82%d8%a9"],
["قوى خارقة", "superpower"],
["كوميدي", "comedy"],
["كوميديا", "%d9%83%d9%88%d9%85%d9%8a%d8%af%d9%8a%d8%a7"],
["كوميدية", "%d9%83%d9%88%d9%85%d9%8a%d8%af%d9%8a%d8%a9"],
["مأساة", "%d9%85%d8%a3%d8%b3%d8%a7%d8%a9"],
["مأساوي", "tragedy"],
["مؤامرة", "%d9%85%d8%a4%d8%a7%d9%85%d8%b1%d8%a9"],
["ما بعد الكارثة", "after-the-disaster"],
[
"ما بعد نهاية العالم",
"%d9%85%d8%a7-%d8%a8%d8%b9%d8%af-%d9%86%d9%87%d8%a7%d9%8a%d8%a9-%d8%a7%d9%84%d8%b9%d8%a7%d9%84%d9%85",
],
[
"مضاد البطل",
"%d9%85%d8%b6%d8%a7%d8%af-%d8%a7%d9%84%d8%a8%d8%b7%d9%84",
],
["مغامرات", "%d9%85%d8%ba%d8%a7%d9%85%d8%b1%d8%a7%d8%aa"],
["مغامرة", "adventure"],
["ميكا", "mechanical"],
["ناضج", "mature"],
["نظام", "%d9%86%d8%b8%d8%a7%d9%85"],
["نفسي", "psychological"],
["ون شوت", "%d9%88%d9%86-%d8%b4%d9%88%d8%aa"],
["ووكسيا", "%d9%88%d9%88%d9%83%d8%b3%d9%8a%d8%a7"],
].map((x) => ({ type_name: "CheckBox", name: x[0], value: x[1] })),
},
{
type_name: "GroupFilter",
type: "TypeFilter",
name: "النوع",
state: [
["إنجليزية", "english"],
["رواية لايت", "light-novel"],
[
"رواية مؤلفة",
"%d8%b1%d9%88%d8%a7%d9%8a%d8%a9-%d9%85%d8%a4%d9%84%d9%81%d8%a9",
],
["رواية ويب", "web-novel"],
["صينية", "chinese"],
["عربية", "arabic"],
["كورية", "korean"],
["مؤلفة", "%d9%85%d8%a4%d9%84%d9%81%d8%a9"],
["ون شوت", "%d9%88%d9%86-%d8%b4%d9%88%d8%aa"],
["يابانية", "japanese"],
].map((x) => ({ type_name: "CheckBox", name: x[0], value: x[1] })),
},
];
}
getBaseUrl() {
const preference = new SharedPreferences();
var base_url = preference.get("base_url");
if (base_url.length == 0) {
return this.source.baseUrl;
}
if (base_url.endsWith("/")) {
return base_url.slice(0, -1);
}
return base_url;
}
getSourcePreferences() {
return [
{
key: "base_url",
editTextPreference: {
title: "تعديل الرابط",
summary: "",
value: this.source.baseUrl,
dialogTitle: "تعديل",
dialogMessage: `Defaul URL ${this.source.baseUrl}`,
},
},
];
}
parseDate(date) {
const months = {
يناير: "January",
فبراير: "February",
مارس: "March",
أبريل: "April",
ابريل: "April",
مايو: "May",
يونيو: "June",
يوليو: "July",
أغسطس: "August",
اغسطس: "August",
سبتمبر: "September",
أكتوبر: "October",
اكتوبر: "October",
نوفمبر: "November",
ديسمبر: "December",
};
const [monthAr, day, year] = date.split(/[\s,]+/);
const monthEnglish = months[monthAr] || "";
if (!monthEnglish) return "";
return new Date(`${monthEnglish} ${day}, ${year}`).getTime().toString();
}
ll(url) {
return url.includes("?") ? "&" : "?";
}
}