Files
kodjodevf-mangayomi-extensions/javascript/manga/src/all/webtoons.js

635 lines
15 KiB
JavaScript

// prettier-ignore
const mangayomiSources = [{
"name": "Webtoons",
"langs": ["en", "fr", "id", "th", "es", "zh", "de"],
"baseUrl": "https://www.webtoons.com",
"apiUrl": "",
"iconUrl": "https://upload.wikimedia.org/wikipedia/commons/0/09/Naver_Line_Webtoon_logo.png",
"typeSource": "single",
"isManga": true,
"isNsfw": false,
"version": "0.0.45",
"dateFormat": "",
"dateFormatLocale": "",
"pkgPath": "manga/src/all/webtoons.js"
}];
class DefaultExtension extends MProvider {
headers = {
"User-Agent":
"Mozilla/5.0 (Linux; Android 13; Mobile) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Mobile Safari/537.36",
};
mobileUrl = "https://m.webtoons.com";
getFormattedUrl(preferenceKey) {
const preference = new SharedPreferences();
let url = preference.get(preferenceKey) || this.source.baseUrl;
return url.endsWith("/") ? url.slice(0, -1) : url;
}
getBaseUrl() {
return this.getFormattedUrl("domain_url");
}
getMobileUrl() {
return this.getFormattedUrl("mobile_url");
}
mangaFromElement(doc) {
const list = [];
for (const el of doc.select(
`div.webtoon_list_wrap li a, ul.webtoon_list li a`,
)) {
const imageUrl = el.selectFirst("img").getSrc;
const name = el.selectFirst("strong.title").text;
const link = el.getHref;
list.push({ name, imageUrl, link });
}
return list;
}
async getPopular(page) {
const res = await new Client().get(
`${this.getBaseUrl()}/${this.langCode()}/originals`,
);
const doc = new Document(res.body);
return {
list: this.mangaFromElement(doc),
hasNextPage: false,
};
}
async getLatestUpdates(page) {
const res = await new Client().get(
`${this.getBaseUrl()}/${this.langCode()}/originals?sortOrder=UPDATE`,
);
const doc = new Document(res.body);
return {
list: this.mangaFromElement(doc),
hasNextPage: false,
};
}
async search(query, page, filters) {
const keyword = query.trim().replace(/\s+/g, "+");
let url = `${this.getBaseUrl()}/${this.langCode()}`;
let hasNextPage = false;
const getFilterValue = (type, defaultValue = "") => {
const filter = filters.find((f) => f.type === type);
return filter?.values?.[filter.state]?.value ?? defaultValue;
};
if (query) {
url += `/search/${getFilterValue("searchType")}?keyword=${keyword}&page=${page}`;
} else {
const sortOrder = getFilterValue("sortOrder");
const rankingType = getFilterValue("rankingType");
const weekday = getFilterValue("weekday");
const genreType = getFilterValue("genre");
if (rankingType) {
// const genreParam = genreType ? `&subTabGenreCode=${genreType}` : "";
url += `/ranking/${rankingType}`;
} else if (weekday) {
url += `/originals/${weekday}?sortOrder=${sortOrder}`;
} else if (genreType) {
url += `/genres/${genreType}?sortOrder=${sortOrder}`;
}
}
const res = await new Client().get(url);
const doc = new Document(res.body);
const list = this.mangaFromElement(doc);
if (query) {
hasNextPage = list.length !== 0;
}
return {
list,
hasNextPage,
};
}
async getDetail(url) {
let res = await new Client().get(url);
let doc = new Document(res.body);
const info = doc.selectFirst("div.cont_box");
const name = info.selectFirst("h1.subj, h3.subj").text;
const genre =
Array.from(info.select("p.genre")).map((el) => el.text) != ""
? Array.from(info.select("p.genre")).map((el) => el.text)
: [info.selectFirst("div.info h2").text];
const author =
info
.selectFirst("div.author_area")
.text.replace(/\s+/g, " ")
.replace(/author info/g, "")
.trim() ?? info.selectFirst("a.author").text;
const dayInfoText = info?.selectFirst("p.day_info")?.text || "";
const status =
dayInfoText.includes("UP") ||
dayInfoText.includes("EVERY") ||
dayInfoText.includes("NOUVEAU")
? 0
: dayInfoText.includes("END") ||
dayInfoText.includes("TERMINÉ") ||
dayInfoText.includes("COMPLETED")
? 1
: -1; // UNKNOWN
const description = info
.selectFirst("p.summary")
.text.replace(/\s+/g, " ")
.trim();
// chapters
const chapters = [];
res = await new Client().get(
url.replace(this.getBaseUrl(), this.getMobileUrl()),
this.headers,
);
doc = new Document(res.body);
for (const el of doc.select("ul#_episodeList li[id*=episode] a")) {
const url = el.getHref.replace(this.getMobileUrl(), this.getBaseUrl());
let name = el.selectFirst(".sub_title > span.ellipsis")?.text;
const chapterElement = el.selectFirst("div.row > div.num");
if (chapterElement) {
const chapterText = chapterElement.text;
const hashIndex = chapterText.indexOf("#");
if (hashIndex > -1) {
name += " Ch. " + chapterText.substring(hashIndex + 1);
}
}
const dateUpload = new Date(
this.formatDateString(
el.selectFirst(".sub_info .date")?.text,
this.source.lang,
),
)
.getTime()
.toString();
chapters.push({
name,
url,
dateUpload,
});
}
return {
name,
link: url,
genre,
description,
author,
status,
episodes: chapters,
};
}
langCode() {
return {
en: "en",
fr: "fr",
id: "id",
th: "th",
es: "es",
zh: "zh-hant",
de: "de",
}[this.source.lang];
}
formatDateString(dateStr, lang) {
// Month translations for supported languages
const monthTranslations = {
en: [
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec",
],
fr: [
"janv.",
"févr.",
"mars",
"avr.",
"mai",
"juin",
"juil.",
"août",
"sept.",
"oct.",
"nov.",
"déc.",
],
id: [
"Jan",
"Feb",
"Mar",
"Apr",
"Mei",
"Jun",
"Jul",
"Agt",
"Sep",
"Okt",
"Nov",
"Des",
],
th: [
"ม.ค.",
"ก.พ.",
"มี.ค.",
"เม.ย.",
"พ.ค.",
"มิ.ย.",
"ก.ค.",
"ส.ค.",
"ก.ย.",
"ต.ค.",
"พ.ย.",
"ธ.ค.",
],
es: [
"ene.",
"feb.",
"mar.",
"abr.",
"may.",
"jun.",
"jul.",
"ago.",
"sep.",
"oct.",
"nov.",
"dic.",
],
zh: [], // No need for month names; uses yyyy年MM月dd日 format
de: [], // No need for month names; uses dd.MM.yyyy format
};
const months = monthTranslations[lang];
let parts;
let month;
let day;
let year;
// Handle formats based on the language
switch (lang) {
case "zh":
// Expected format: yyyy年MM月dd日
const match = dateStr.match(/(\d{4})年(\d{1,2})月(\d{1,2})日/);
year = match[1];
month = match[2];
day = match[3];
case "de":
// Expected format: dd.MM.yyyy
parts = dateStr.split(".");
if (parts.length === 3) {
month = parts[1];
day = parts[0];
year = parts[2];
}
case "es":
case "fr":
case "id":
case "th":
// Expected format: dd MMM yyyy
parts = dateStr.split(" ");
if (parts.length === 3) {
month = months.indexOf(parts[1]) + 1;
day = parts[0];
year = parts[2];
}
break;
default:
parts = dateStr.split(" ");
if (parts.length === 3) {
month = months.indexOf(parts[0]) + 1;
day = parts[1].replace(",", "");
year = parts[2];
}
}
if (!month || !year || !day) {
return Date.now();
}
return `${year}-${month.toString().padStart(2, "0")}-${day.toString().padStart(2, "0")}`;
}
async getPageList(url) {
const res = await new Client().get(url);
const doc = new Document(res.body);
const urls = [];
const imageElement = doc.selectFirst("div#_imageList");
const img_urls = imageElement.select("img");
for (let i = 0; i < img_urls.length; i++) {
urls.push(img_urls[i].attr("data-url"));
}
return urls;
}
getFilterList() {
return [
{
type: "header",
name: "Filter Priority: Search > Ranking > Day > Genre | Sort applies to Day/Genre",
type_name: "HeaderFilter",
},
{
type: "separator",
type_name: "SeparatorFilter",
},
{
type: "searchType",
name: "Search Type",
type_name: "SelectFilter",
values: [
{
type_name: "SelectOption",
name: "Originals",
value: "originals",
},
{
type_name: "SelectOption",
name: "Canvas",
value: "canvas",
},
],
state: 0,
},
{
type: "separator",
type_name: "SeparatorFilter",
},
{
type: "rankingType",
name: "Ranking Category",
type_name: "SelectFilter",
values: [
{
type_name: "SelectOption",
name: "Not Selected",
value: "",
},
{
type_name: "SelectOption",
name: "Trending",
value: "trending",
},
{
type_name: "SelectOption",
name: "Popular",
value: "popular",
},
{
type_name: "SelectOption",
name: "Originals",
value: "originals",
},
{
type_name: "SelectOption",
name: "Canvas",
value: "canvas",
},
],
},
{
type: "separator",
type_name: "SeparatorFilter",
},
{
type: "sortOrder",
name: "Sort By (For Schedule & Genres)",
type_name: "SelectFilter",
values: [
{ type_name: "SelectOption", name: "Popular (MANA)", value: "MANA" },
{ type_name: "SelectOption", name: "Likes", value: "LIKEIT" },
{ type_name: "SelectOption", name: "Newest", value: "UPDATE" },
],
state: 0,
appliesTo: ["weekday", "genre"],
},
{
type: "weekday",
name: "Update Schedule",
type_name: "SelectFilter",
values: [
{
type_name: "SelectOption",
name: "Day",
value: "",
data: "",
},
{
type_name: "SelectOption",
name: "Monday",
value: "monday",
data: "MONDAY",
},
{
type_name: "SelectOption",
name: "Tuesday",
value: "tuesday",
data: "TUESDAY",
},
{
type_name: "SelectOption",
name: "Wednesday",
value: "wednesday",
data: "WEDNESDAY",
},
{
type_name: "SelectOption",
name: "Thursday",
value: "thursday",
data: "THURSDAY",
},
{
type_name: "SelectOption",
name: "Friday",
value: "friday",
data: "FRIDAY",
},
{
type_name: "SelectOption",
name: "Saturday",
value: "saturday",
data: "SATURDAY",
},
{
type_name: "SelectOption",
name: "Sunday",
value: "sunday",
data: "SUNDAY",
},
{
type_name: "SelectOption",
name: "Completed",
value: "complete",
data: "COMPLETE",
},
],
},
{
type: "genre",
name: "Genre",
type_name: "SelectFilter",
values: [
{
type_name: "SelectOption",
name: "All Genres",
value: "",
data: "",
},
{
type_name: "SelectOption",
name: "Drama",
value: "drama",
data: "DRAMA",
},
{
type_name: "SelectOption",
name: "Fantasy",
value: "fantasy",
data: "FANTASY",
},
{
type_name: "SelectOption",
name: "Comedy",
value: "comedy",
data: "COMEDY",
},
{
type_name: "SelectOption",
name: "Action",
value: "action",
data: "ACTION",
},
{
type_name: "SelectOption",
name: "Slice of Life",
value: "slice_of_life",
data: "SLICE_OF_LIFE",
},
{
type_name: "SelectOption",
name: "Romance",
value: "romance",
data: "ROMANCE",
},
{
type_name: "SelectOption",
name: "Superhero",
value: "super_hero",
data: "SUPER_HERO",
},
{
type_name: "SelectOption",
name: "Sci-Fi",
value: "sf",
data: "SF",
},
{
type_name: "SelectOption",
name: "Thriller",
value: "thriller",
data: "THRILLER",
},
{
type_name: "SelectOption",
name: "Supernatural",
value: "supernatural",
data: "SUPERNATURAL",
},
{
type_name: "SelectOption",
name: "Mystery",
value: "mystery",
data: "MYSTERY",
},
{
type_name: "SelectOption",
name: "Sports",
value: "sports",
data: "SPORTS",
},
{
type_name: "SelectOption",
name: "Historical",
value: "historical",
data: "HISTORICAL",
},
{
type_name: "SelectOption",
name: "Heartwarming",
value: "heartwarming",
data: "HEARTWARMING",
},
{
type_name: "SelectOption",
name: "Horror",
value: "horror",
data: "HORROR",
},
{
type_name: "SelectOption",
name: "Graphic Novel",
value: "graphic_novel",
data: "GRAPHIC_NOVEL",
},
{
type_name: "SelectOption",
name: "Informative",
value: "tiptoon",
data: "TIPTOON",
},
],
},
];
}
// Preferences
getSourcePreferences() {
return [
{
key: "domain_url",
editTextPreference: {
title: "Override BaseUrl",
summary: "",
value: this.source.baseUrl,
dialogTitle: "URL",
dialogMessage: "",
},
},
{
key: "mobile_url",
editTextPreference: {
title: "Override mobileUrl",
summary: "",
value: this.mobileUrl,
dialogTitle: "URL",
dialogMessage: "",
},
},
];
}
}