// 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 `

${html.title.rendered}



${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("?") ? "&" : "?"; } }