Merge pull request #259 from Swakshan/fix/repo

fix: Refresh contain issue
This commit is contained in:
Moustapha Kodjo Amadou
2025-05-20 12:11:21 +01:00
committed by GitHub
10 changed files with 2379 additions and 2087 deletions

View File

@@ -1,39 +1,45 @@
const mangayomiSources = [{ const mangayomiSources = [
{
"name": "Autoembed", "name": "Autoembed",
"lang": "all", "lang": "all",
"baseUrl": "https://watch.autoembed.cc", "baseUrl": "https://watch.autoembed.cc",
"apiUrl": "https://tom.autoembed.cc", "apiUrl": "https://tom.autoembed.cc",
"iconUrl": "https://www.google.com/s2/favicons?sz=64&domain=https://autoembed.cc/", "iconUrl":
"https://www.google.com/s2/favicons?sz=64&domain=https://autoembed.cc/",
"typeSource": "multi", "typeSource": "multi",
"isManga": false, "isManga": false,
"itemType": 1, "itemType": 1,
"version": "1.2.6", "version": "1.2.7",
"dateFormat": "", "dateFormat": "",
"dateFormatLocale": "", "dateFormatLocale": "",
"pkgPath": "anime/src/all/autoembed.js" "pkgPath": "anime/src/all/autoembed.js"
}]; }
];
class DefaultExtension extends MProvider { class DefaultExtension extends MProvider {
decodeBase64 = function (f) { decodeBase64 = function (f) {
var g = {}, var g = {},
b = 65, b = 65,
d = 0, d = 0,
a, c = 0, a,
h, e = "", c = 0,
h,
e = "",
k = String.fromCharCode, k = String.fromCharCode,
l = f.length; l = f.length;
for (a = ""; 91 > b;) a += k(b++); for (a = ""; 91 > b; ) a += k(b++);
a += a.toLowerCase() + "0123456789+/"; a += a.toLowerCase() + "0123456789+/";
for (b = 0; 64 > b; b++) g[a.charAt(b)] = b; for (b = 0; 64 > b; b++) g[a.charAt(b)] = b;
for (a = 0; a < l; a++) for (a = 0; a < l; a++)
for (b = g[f.charAt(a)], d = (d << 6) + b, c += 6; 8 <= c;)((h = d >>> (c -= 8) & 255) || a < l - 2) && (e += k(h)); for (b = g[f.charAt(a)], d = (d << 6) + b, c += 6; 8 <= c; )
return e ((h = (d >>> (c -= 8)) & 255) || a < l - 2) && (e += k(h));
return e;
}; };
getHeaders(url) { getHeaders(url) {
return { return {
Referer: url, Referer: url,
Origin: url Origin: url,
} };
} }
getPreference(key) { getPreference(key) {
@@ -42,7 +48,7 @@ class DefaultExtension extends MProvider {
} }
async tmdbRequest(slug) { async tmdbRequest(slug) {
var api = `https://94c8cb9f702d-tmdb-addon.baby-beamup.club/${slug}` var api = `https://94c8cb9f702d-tmdb-addon.baby-beamup.club/${slug}`;
var response = await new Client().get(api); var response = await new Client().get(api);
var body = JSON.parse(response.body); var body = JSON.parse(response.body);
return body; return body;
@@ -53,25 +59,22 @@ class DefaultExtension extends MProvider {
var results = body.metas; var results = body.metas;
for (let i in results) { for (let i in results) {
var result = results[i]; var result = results[i];
var id = result.id var id = result.id;
var media_type = result.type; var media_type = result.type;
items.push({ items.push({
name: result.name, name: result.name,
imageUrl: result.poster, imageUrl: result.poster,
link: `${media_type}||${id}`, link: `${media_type}||${id}`,
description: result.description, description: result.description,
genre: result.genre genre: result.genre,
}); });
} }
return items; return items;
} }
async getSearchInfo(slug) { async getSearchInfo(slug) {
var body = await this.tmdbRequest(`catalog/movie/${slug}`); var body = await this.tmdbRequest(`catalog/movie/${slug}`);
var popMovie = await this.getSearchItems(body); var popMovie = await this.getSearchItems(body);
body = await this.tmdbRequest(`catalog/series/${slug}`); body = await this.tmdbRequest(`catalog/series/${slug}`);
var popSeries = await this.getSearchItems(body); var popSeries = await this.getSearchItems(body);
@@ -81,17 +84,15 @@ class DefaultExtension extends MProvider {
if (priority === "series") { if (priority === "series") {
fullList = [...popSeries, ...popMovie]; fullList = [...popSeries, ...popMovie];
} else { } else {
fullList = [...popMovie, ...popSeries] fullList = [...popMovie, ...popSeries];
} }
var hasNextPage = slug.indexOf("search=") > -1 ? false : true; var hasNextPage = slug.indexOf("search=") > -1 ? false : true;
return { return {
list: fullList, list: fullList,
hasNextPage hasNextPage,
}; };
} }
async getPopular(page) { async getPopular(page) {
var skip = (page - 1) * 20; var skip = (page - 1) * 20;
return await this.getSearchInfo(`tmdb.popular/skip=${skip}.json`); return await this.getSearchInfo(`tmdb.popular/skip=${skip}.json`);
@@ -102,56 +103,72 @@ class DefaultExtension extends MProvider {
async getLatestUpdates(page) { async getLatestUpdates(page) {
var trend_window = this.getPreference("pref_latest_time_window"); var trend_window = this.getPreference("pref_latest_time_window");
var skip = (page - 1) * 20; var skip = (page - 1) * 20;
return await this.getSearchInfo(`tmdb.trending/genre=${trend_window}&skip=${skip}.json`); return await this.getSearchInfo(
`tmdb.trending/genre=${trend_window}&skip=${skip}.json`
);
} }
async search(query, page, filters) { async search(query, page, filters) {
return await this.getSearchInfo(`tmdb.popular/search=${query}.json`); return await this.getSearchInfo(`tmdb.popular/search=${query}.json`);
} }
async getDetail(url) { async getDetail(url) {
var baseUrl = this.source.baseUrl;
var linkSlug = `${baseUrl}/title/`;
if (url.includes(linkSlug)) {
url = url.replace(linkSlug, "");
var id = url.replace("t", "");
if (url.includes("t")) {
url = `series||tmdb:${id}`;
} else {
url = `movie||tmdb:${id}`;
}
}
var parts = url.split("||"); var parts = url.split("||");
var media_type = parts[0]; var media_type = parts[0];
var id = parts[1]; var id = parts[1];
var body = await this.tmdbRequest(`meta/${media_type}/${id}.json`) var body = await this.tmdbRequest(`meta/${media_type}/${id}.json`);
var result = body.meta; var result = body.meta;
var tmdb_id = id.substring(5,) var tmdb_id = id.substring(5);
media_type = media_type == "series" ? "tv" : media_type; media_type = media_type == "series" ? "tv" : media_type;
var dateNow = Date.now().valueOf(); var dateNow = Date.now().valueOf();
var release = result.released ? new Date(result.released).valueOf() : dateNow var release = result.released
? new Date(result.released).valueOf()
: dateNow;
var chaps = []; var chaps = [];
var item = { var item = {
name: result.name, name: result.name,
imageUrl: result.poster, imageUrl: result.poster,
link: `${this.source.baseUrl}/${media_type}/${tmdb_id}`, link: `${linkSlug}${linkCode}`,
description: result.description, description: result.description,
genre: result.genre, genre: result.genre,
}; };
var link = `${media_type}||${tmdb_id}` var link = `${media_type}||${tmdb_id}`;
if (media_type == "tv") { if (media_type == "tv") {
var videos = result.videos;
var videos = result.videos
for (var i in videos) { for (var i in videos) {
var video = videos[i]; var video = videos[i];
var seasonNum = video.season; var seasonNum = video.season;
if (!seasonNum) continue; if (!seasonNum) continue;
release = video.released ? new Date(video.released).valueOf() : dateNow release = video.released ? new Date(video.released).valueOf() : dateNow;
if (release < dateNow) { if (release < dateNow) {
var episodeNum = video.episode var episodeNum = video.episode;
var name = `S${seasonNum}:E${episodeNum} - ${video.name}` var name = `S${seasonNum}:E${episodeNum} - ${video.name}`;
var eplink = `${link}||${seasonNum}||${episodeNum}` var eplink = `${link}||${seasonNum}||${episodeNum}`;
chaps.push({ chaps.push({
name: name, name: name,
url: eplink, url: eplink,
dateUpload: release.toString(), dateUpload: release.toString(),
}) });
} }
} }
} else { } else {
@@ -160,7 +177,7 @@ class DefaultExtension extends MProvider {
name: "Movie", name: "Movie",
url: link, url: link,
dateUpload: release.toString(), dateUpload: release.toString(),
}) });
} }
} }
@@ -171,67 +188,68 @@ class DefaultExtension extends MProvider {
// Extracts the streams url for different resolutions from a hls stream. // Extracts the streams url for different resolutions from a hls stream.
async extractStreams(url, lang = "", hdr = {}, host = "") { async extractStreams(url, lang = "", hdr = {}, host = "") {
var streams = [{ var streams = [
{
url: url, url: url,
originalUrl: url, originalUrl: url,
quality: `${lang} Auto`, quality: `${lang} Auto`,
headers: hdr headers: hdr,
}]; },
];
var pref = this.getPreference("autoembed_split_stream_quality"); var pref = this.getPreference("autoembed_split_stream_quality");
if (!pref) return streams if (!pref) return streams;
const response = await new Client().get(url, hdr); const response = await new Client().get(url, hdr);
const body = response.body; const body = response.body;
const lines = body.split('\n'); const lines = body.split("\n");
for (let i = 0; i < lines.length; i++) { for (let i = 0; i < lines.length; i++) {
if (lines[i].startsWith('#EXT-X-STREAM-INF:')) { if (lines[i].startsWith("#EXT-X-STREAM-INF:")) {
var resolution = lines[i].match(/RESOLUTION=(\d+x\d+)/)[1]; var resolution = lines[i].match(/RESOLUTION=(\d+x\d+)/)[1];
resolution = `${lang} ${resolution}` resolution = `${lang} ${resolution}`;
var m3u8Url = lines[i + 1].trim(); var m3u8Url = lines[i + 1].trim();
m3u8Url = m3u8Url.replace("./", `${url}/`) m3u8Url = m3u8Url.replace("./", `${url}/`);
if (host.length > 0) { if (host.length > 0) {
m3u8Url = `${host}${m3u8Url}` m3u8Url = `${host}${m3u8Url}`;
} }
streams.push({ streams.push({
url: m3u8Url, url: m3u8Url,
originalUrl: m3u8Url, originalUrl: m3u8Url,
quality: resolution, quality: resolution,
headers: hdr headers: hdr,
}); });
} }
} }
return streams return streams;
} }
// For some streams, we can form stream url using a default template. // For some streams, we can form stream url using a default template.
async splitStreams(url, lang = "", hdr = {}) { async splitStreams(url, lang = "", hdr = {}) {
var streams = [{ var streams = [
{
url: url, url: url,
originalUrl: url, originalUrl: url,
quality: `${lang} - Auto`, quality: `${lang} - Auto`,
headers: hdr headers: hdr,
}]; },
];
var pref = this.getPreference("autoembed_split_stream_quality"); var pref = this.getPreference("autoembed_split_stream_quality");
if (!pref) return streams if (!pref) return streams;
var quality = ["360", "480", "720", "1080"];
var quality = ["360", "480", "720", "1080"]
for (var q of quality) { for (var q of quality) {
var link = url var link = url;
if (q != "auto") { if (q != "auto") {
link = link.replace("index.m3u8", `${q}/index.m3u8`) link = link.replace("index.m3u8", `${q}/index.m3u8`);
q = `${q}p` q = `${q}p`;
} }
streams.push({ streams.push({
url: link, url: link,
originalUrl: link, originalUrl: link,
quality: `${lang} - ${q}`, quality: `${lang} - ${q}`,
headers: hdr headers: hdr,
}); });
} }
return streams; return streams;
@@ -241,7 +259,7 @@ class DefaultExtension extends MProvider {
async sortStreams(streams) { async sortStreams(streams) {
var sortedStreams = []; var sortedStreams = [];
var copyStreams = streams.slice() var copyStreams = streams.slice();
var pref = this.getPreference("pref_video_resolution"); var pref = this.getPreference("pref_video_resolution");
for (var i in streams) { for (var i in streams) {
var stream = streams[i]; var stream = streams[i];
@@ -254,70 +272,74 @@ class DefaultExtension extends MProvider {
break; break;
} }
} }
return [...sortedStreams, ...copyStreams] return [...sortedStreams, ...copyStreams];
} }
// Gets subtitles based on TMDB id. // Gets subtitles based on TMDB id.
async getSubtitleList(id, s, e) { async getSubtitleList(id, s, e) {
var subPref = parseInt(this.getPreference("autoembed_pref_subtitle_source")); var subPref = parseInt(
this.getPreference("autoembed_pref_subtitle_source")
);
var api = `https://sub.wyzie.ru/search?id=${id}` var api = `https://sub.wyzie.ru/search?id=${id}`;
var hdr = {} var hdr = {};
if (subPref === 2) { if (subPref === 2) {
api = `https://sources.hexa.watch/subs/${id}` api = `https://sources.hexa.watch/subs/${id}`;
hdr = { "Origin": "https://api.hexa.watch" } hdr = { "Origin": "https://api.hexa.watch" };
if (s != "0") api = `${api}/${s}/${e}` if (s != "0") api = `${api}/${s}/${e}`;
} else { } else {
if (s != "0") api = `${api}&season=${s}&episode=${e}` if (s != "0") api = `${api}&season=${s}&episode=${e}`;
} }
var response = await new Client().get(api, hdr); var response = await new Client().get(api, hdr);
var body = JSON.parse(response.body); var body = JSON.parse(response.body);
var subs = [] var subs = [];
for (var sub of body) { for (var sub of body) {
subs.push({ subs.push({
file: sub.url, file: sub.url,
label: sub.display label: sub.display,
}) });
} }
return subs return subs;
} }
// For anime episode video list // For anime episode video list
async getVideoList(url) { async getVideoList(url) {
var streamAPI = parseInt(this.getPreference("autoembed_stream_source_3")) var streamAPI = parseInt(this.getPreference("autoembed_stream_source_3"));
var nativeSubs = this.getPreference("autoembed_pref_navtive_subtitle") var nativeSubs = this.getPreference("autoembed_pref_navtive_subtitle");
var parts = url.split("||"); var parts = url.split("||");
var media_type = parts[0]; var media_type = parts[0];
var id = parts[1]; var id = parts[1];
var s = "0" var s = "0";
var e = "0" var e = "0";
if (media_type == "tv") { if (media_type == "tv") {
s = parts[2] s = parts[2];
e = parts[3] e = parts[3];
} }
var tmdb = id var tmdb = id;
var streams = [] var streams = [];
var subtitles = [] var subtitles = [];
switch (streamAPI) { switch (streamAPI) {
case 2: { case 2: {
if (media_type == "tv") { if (media_type == "tv") {
id = `${id}/${s}/${e}` id = `${id}/${s}/${e}`;
} }
var api = `https://play2.123embed.net/server/3?path=/${media_type}/${id}` var api = `https://play2.123embed.net/server/3?path=/${media_type}/${id}`;
var response = await new Client().get(api); var response = await new Client().get(api);
if (response.statusCode != 200) { if (response.statusCode != 200) {
throw new Error("play2.123embed.net unavailable\nPlease choose a different server"); throw new Error(
"play2.123embed.net unavailable\nPlease choose a different server"
);
} }
var body = JSON.parse(response.body); var body = JSON.parse(response.body);
var link = body.playlist[0].file var link = body.playlist[0].file;
streams.push({ streams.push({
url: link, url: link,
originalUrl: link, originalUrl: link,
@@ -328,97 +350,112 @@ class DefaultExtension extends MProvider {
} }
case 3: { case 3: {
if (media_type == "tv") { if (media_type == "tv") {
id = `${id}&s=${s}&e=${e}` id = `${id}&s=${s}&e=${e}`;
} }
var api = `https://autoembed.cc/embed/player.php?id=${id}` var api = `https://autoembed.cc/embed/player.php?id=${id}`;
var response = await new Client().get(api); var response = await new Client().get(api);
if (response.statusCode != 200) { if (response.statusCode != 200) {
throw new Error("autoembed.cc unavailable\nPlease choose a different server"); throw new Error(
"autoembed.cc unavailable\nPlease choose a different server"
);
} }
var body = response.body var body = response.body;
var sKey = '"file": ' var sKey = '"file": ';
var eKey = "]});" var eKey = "]});";
var start = body.indexOf(sKey) var start = body.indexOf(sKey);
if (start < 0) { if (start < 0) {
throw new Error("autoembed.cc videos unavailable\nPlease choose a different server"); throw new Error(
"autoembed.cc videos unavailable\nPlease choose a different server"
);
} }
start += sKey.length start += sKey.length;
var end = body.substring(start,).indexOf(eKey) + start - 1 var end = body.substring(start).indexOf(eKey) + start - 1;
var strms = JSON.parse(body.substring(start, end) + "]") var strms = JSON.parse(body.substring(start, end) + "]");
for (var strm of strms) { for (var strm of strms) {
var link = strm.file var link = strm.file;
var lang = strm.title var lang = strm.title;
var streamSplit = await this.splitStreams(link, lang); var streamSplit = await this.splitStreams(link, lang);
streams = [...streams, ...streamSplit] streams = [...streams, ...streamSplit];
} }
break; break;
} }
case 4: { case 4: {
if (media_type == "tv") { if (media_type == "tv") {
id = `${id}&season=${s}&episode=${e}` id = `${id}&season=${s}&episode=${e}`;
} }
var api = `https://flicky.host/player/desi.php?id=${id}` var api = `https://flicky.host/player/desi.php?id=${id}`;
var response = await new Client().get(api, { "Referer": "https://flicky.host/", "sec-fetch-dest": "iframe" }); var response = await new Client().get(api, {
"Referer": "https://flicky.host/",
"sec-fetch-dest": "iframe",
});
if (response.statusCode != 200) { if (response.statusCode != 200) {
throw new Error("flicky.host unavailable\nPlease choose a different server"); throw new Error(
"flicky.host unavailable\nPlease choose a different server"
);
} }
var body = response.body var body = response.body;
var sKey = 'streams = ' var sKey = "streams = ";
var eKey = "];" var eKey = "];";
var start = body.indexOf(sKey) var start = body.indexOf(sKey);
if (start < 0) { if (start < 0) {
throw new Error("flicky.host videos unavailable\nPlease choose a different server"); throw new Error(
"flicky.host videos unavailable\nPlease choose a different server"
);
} }
start += sKey.length start += sKey.length;
var end = body.substring(start,).indexOf(eKey) + start + 1 var end = body.substring(start).indexOf(eKey) + start + 1;
var strms = JSON.parse(body.substring(start, end)) var strms = JSON.parse(body.substring(start, end));
for (var strm of strms) { for (var strm of strms) {
var link = strm.url var link = strm.url;
var lang = strm.language var lang = strm.language;
var streamSplit = await this.splitStreams(link, lang); var streamSplit = await this.splitStreams(link, lang);
streams = [...streams, ...streamSplit] streams = [...streams, ...streamSplit];
} }
break; break;
} }
case 5: { case 5: {
if (media_type == "tv") { if (media_type == "tv") {
id = `${id}/${s}/${e}` id = `${id}/${s}/${e}`;
} }
var api = `https://vidapi.click/api/video/${media_type}/${id}` var api = `https://vidapi.click/api/video/${media_type}/${id}`;
var response = await new Client().get(api); var response = await new Client().get(api);
if (response.statusCode != 200) { if (response.statusCode != 200) {
throw new Error("vidapi.click unavailable\nPlease choose a different server"); throw new Error(
"vidapi.click unavailable\nPlease choose a different server"
);
} }
var body = JSON.parse(response.body); var body = JSON.parse(response.body);
var link = body.sources[0].file var link = body.sources[0].file;
if(nativeSubs) subtitles = body.tracks if (nativeSubs) subtitles = body.tracks;
streams = await this.extractStreams(link); streams = await this.extractStreams(link);
break; break;
} }
case 6: { case 6: {
if (media_type == "tv") { if (media_type == "tv") {
id = `${id}/${s}/${e}` id = `${id}/${s}/${e}`;
} }
var api = `https://sources.hexa.watch/plsdontscrapemeuwu/${id}` var api = `https://sources.hexa.watch/plsdontscrapemeuwu/${id}`;
var hdr = { "Origin": "https://api.hexa.watch" } var hdr = { "Origin": "https://api.hexa.watch" };
var response = await new Client().get(api, hdr); var response = await new Client().get(api, hdr);
if (response.statusCode != 200) { if (response.statusCode != 200) {
throw new Error("hexa.watch unavailable\nPlease choose a different server"); throw new Error(
"hexa.watch unavailable\nPlease choose a different server"
);
} }
var body = JSON.parse(response.body); var body = JSON.parse(response.body);
var strms = body.streams var strms = body.streams;
for (var strm of strms) { for (var strm of strms) {
var streamLink = strm.url; var streamLink = strm.url;
if (streamLink.length > 0) { if (streamLink.length > 0) {
@@ -426,7 +463,7 @@ class DefaultExtension extends MProvider {
url: strm.url, url: strm.url,
originalUrl: strm.url, originalUrl: strm.url,
quality: `${strm.label} - Auto`, quality: `${strm.label} - Auto`,
headers: strm.headers headers: strm.headers,
}); });
} }
} }
@@ -434,35 +471,39 @@ class DefaultExtension extends MProvider {
} }
case 7: { case 7: {
if (media_type == "tv") { if (media_type == "tv") {
id = `${id}/${s}/${e}` id = `${id}/${s}/${e}`;
} }
var api = `https://vidsrc.su/embed/${media_type}/${id}` var api = `https://vidsrc.su/embed/${media_type}/${id}`;
var response = await new Client().get(api); var response = await new Client().get(api);
if (response.statusCode != 200) { if (response.statusCode != 200) {
throw new Error("vidsrc.su unavailable\nPlease choose a different server"); throw new Error(
"vidsrc.su unavailable\nPlease choose a different server"
);
} }
var body = response.body var body = response.body;
var sKey = 'fixedServers = ' var sKey = "fixedServers = ";
var eKey = "];" var eKey = "];";
var start = body.indexOf(sKey) var start = body.indexOf(sKey);
if (start < 0) { if (start < 0) {
throw new Error("vidsrc.su videos unavailable\nPlease choose a different server"); throw new Error(
"vidsrc.su videos unavailable\nPlease choose a different server"
);
} }
start += sKey.length start += sKey.length;
var end = body.substring(start,).indexOf(eKey) + start + 1 var end = body.substring(start).indexOf(eKey) + start + 1;
var strms = body.substring(start, end) var strms = body.substring(start, end);
// Split the data into lines // Split the data into lines
var lines = strms.split('\n'); var lines = strms.split("\n");
// Regex to match URLs in quotes that start with https:// // Regex to match URLs in quotes that start with https://
var regex = /url:\s*'(https:\/\/[^']+)'/; var regex = /url:\s*'(https:\/\/[^']+)'/;
var availableStreams = []; var availableStreams = [];
// Process each line // Process each line
lines.forEach(line => { lines.forEach((line) => {
var match = line.match(regex); var match = line.match(regex);
if (match && match[1]) { if (match && match[1]) {
// Extract the label from the line // Extract the label from the line
@@ -478,93 +519,108 @@ class DefaultExtension extends MProvider {
for (var stream of availableStreams) { for (var stream of availableStreams) {
var streamSplit = await this.extractStreams(stream.url, stream.label); var streamSplit = await this.extractStreams(stream.url, stream.label);
streams = [...streams, ...streamSplit] streams = [...streams, ...streamSplit];
} }
if (nativeSubs) { if (nativeSubs) {
// subtitles // subtitles
sKey = 'const subtitles = ' sKey = "const subtitles = ";
eKey = "];" eKey = "];";
start = body.indexOf(sKey) start = body.indexOf(sKey);
if (start < 0) { if (start < 0) {
break; // no need for native subtitle if not found. break; // no need for native subtitle if not found.
} }
start += sKey.length start += sKey.length;
end = body.substring(start,).indexOf(eKey) + start + 1 end = body.substring(start).indexOf(eKey) + start + 1;
var natSubs = JSON.parse(body.substring(start, end)) var natSubs = JSON.parse(body.substring(start, end));
natSubs.forEach(sub=>{ natSubs.forEach((sub) => {
subtitles.push({ subtitles.push({
file: sub.url, file: sub.url,
label: sub.display label: sub.display,
}) });
}) });
} }
break; break;
} }
case 8: { case 8: {
function reverse(str) { function reverse(str) {
return str.split("").reverse().join("") return str.split("").reverse().join("");
} }
if (media_type == "tv") { if (media_type == "tv") {
id = `${id}/${s}/${e}` id = `${id}/${s}/${e}`;
} }
var baseUrl = "https://embed.su" var baseUrl = "https://embed.su";
var embedUrl = `${baseUrl}/embed/${media_type}/${id}` var embedUrl = `${baseUrl}/embed/${media_type}/${id}`;
var response = await new Client().get(embedUrl, this.getHeaders(baseUrl)); var response = await new Client().get(
embedUrl,
this.getHeaders(baseUrl)
);
var body = response.body var body = response.body;
var sKey = "JSON.parse(atob(`"; var sKey = "JSON.parse(atob(`";
var start = body.indexOf(sKey) + sKey.length; var start = body.indexOf(sKey) + sKey.length;
var end = body.substring(start,).indexOf("`") + start var end = body.substring(start).indexOf("`") + start;
var configHash = body.substring(start, end) var configHash = body.substring(start, end);
var config = JSON.parse(this.decodeBase64(configHash)); var config = JSON.parse(this.decodeBase64(configHash));
var encodedHash = this.decodeBase64(config.hash); var encodedHash = this.decodeBase64(config.hash);
var decodeHash = reverse(encodedHash.split(".").map((item) => reverse(item)).join("")) var decodeHash = reverse(
encodedHash = JSON.parse(this.decodeBase64(decodeHash)) encodedHash
var serverHash = encodedHash[0].hash .split(".")
.map((item) => reverse(item))
.join("")
);
encodedHash = JSON.parse(this.decodeBase64(decodeHash));
var serverHash = encodedHash[0].hash;
var api = `${baseUrl}/api/e/${serverHash}` var api = `${baseUrl}/api/e/${serverHash}`;
response = await new Client().get(api, this.getHeaders(baseUrl)); response = await new Client().get(api, this.getHeaders(baseUrl));
var jsonRes = JSON.parse(response.body); var jsonRes = JSON.parse(response.body);
streams = await this.extractStreams(jsonRes.source, "", this.getHeaders(baseUrl), baseUrl); streams = await this.extractStreams(
if (nativeSubs) subtitles = jsonRes.subtitles jsonRes.source,
"",
this.getHeaders(baseUrl),
baseUrl
);
if (nativeSubs) subtitles = jsonRes.subtitles;
break; break;
} }
default: { default: {
if (media_type == "tv") { if (media_type == "tv") {
id = `${id}/${s}/${e}` id = `${id}/${s}/${e}`;
} }
var api = `${this.source.apiUrl}/api/getVideoSource?type=${media_type}&id=${id}` var api = `${this.source.apiUrl}/api/getVideoSource?type=${media_type}&id=${id}`;
var response = await new Client().get(api, this.getHeaders(this.source.apiUrl)); var response = await new Client().get(
api,
this.getHeaders(this.source.apiUrl)
);
if (response.statusCode != 200) { if (response.statusCode != 200) {
throw new Error("tom.autoembed.cc unavailable\nPlease choose a different server"); throw new Error(
"tom.autoembed.cc unavailable\nPlease choose a different server"
);
} }
var body = JSON.parse(response.body); var body = JSON.parse(response.body);
var link = body.videoSource var link = body.videoSource;
if (nativeSubs) subtitles = body.subtitles if (nativeSubs) subtitles = body.subtitles;
streams = await this.extractStreams(link); streams = await this.extractStreams(link);
break; break;
} }
} }
if (streams.length < 1) { if (streams.length < 1) {
throw new Error("No streams unavailable\nPlease choose a different server"); throw new Error(
"No streams unavailable\nPlease choose a different server"
);
} }
var apiSubs = await this.getSubtitleList(tmdb, s, e) var apiSubs = await this.getSubtitleList(tmdb, s, e);
streams[0].subtitles = [...subtitles, ...apiSubs] streams[0].subtitles = [...subtitles, ...apiSubs];
return await this.sortStreams(streams) return await this.sortStreams(streams);
} }
// For manga chapter pages // For manga chapter pages
@@ -576,71 +632,83 @@ class DefaultExtension extends MProvider {
} }
getSourcePreferences() { getSourcePreferences() {
return [{ return [
key: 'pref_latest_time_window', {
key: "pref_latest_time_window",
listPreference: { listPreference: {
title: 'Preferred latest trend time window', title: "Preferred latest trend time window",
summary: '', summary: "",
valueIndex: 0, valueIndex: 0,
entries: ["Day", "Week"], entries: ["Day", "Week"],
entryValues: ["day", "week"] entryValues: ["day", "week"],
} },
}, { },
key: 'pref_video_resolution', {
key: "pref_video_resolution",
listPreference: { listPreference: {
title: 'Preferred video resolution', title: "Preferred video resolution",
summary: '', summary: "",
valueIndex: 0, valueIndex: 0,
entries: ["Auto", "1080p", "720p", "360p"], entries: ["Auto", "1080p", "720p", "360p"],
entryValues: ["auto", "1080", "720", "360"] entryValues: ["auto", "1080", "720", "360"],
} },
}, { },
key: 'pref_content_priority', {
key: "pref_content_priority",
listPreference: { listPreference: {
title: 'Preferred content priority', title: "Preferred content priority",
summary: 'Choose which type of content to show first', summary: "Choose which type of content to show first",
valueIndex: 0, valueIndex: 0,
entries: ["Movies", "Series"], entries: ["Movies", "Series"],
entryValues: ["movies", "series"] entryValues: ["movies", "series"],
} },
}, },
{ {
key: 'autoembed_split_stream_quality', key: "autoembed_split_stream_quality",
"switchPreferenceCompat": { "switchPreferenceCompat": {
'title': 'Split stream into different quality streams', "title": "Split stream into different quality streams",
"summary": "Split stream Auto into 360p/720p/1080p", "summary": "Split stream Auto into 360p/720p/1080p",
"value": true "value": true,
} },
}, },
{ {
key: 'autoembed_stream_source_3', key: "autoembed_stream_source_3",
listPreference: { listPreference: {
title: 'Preferred stream source', title: "Preferred stream source",
summary: '', summary: "",
valueIndex: 0, valueIndex: 0,
entries: ["tom.autoembed.cc", "123embed.net", "autoembed.cc - Indian languages", "flicky.host - Indian languages", "vidapi.click", "hexa.watch", "vidsrc.su", "embed.su"], entries: [
entryValues: ["1", "2", "3", "4", "5", "6", "7", "8"] "tom.autoembed.cc",
} "123embed.net",
"autoembed.cc - Indian languages",
"flicky.host - Indian languages",
"vidapi.click",
"hexa.watch",
"vidsrc.su",
"embed.su",
],
entryValues: ["1", "2", "3", "4", "5", "6", "7", "8"],
},
}, },
{ {
key: 'autoembed_pref_navtive_subtitle', key: "autoembed_pref_navtive_subtitle",
"switchPreferenceCompat": { "switchPreferenceCompat": {
'title': 'Use native subtitles as well', "title": "Use native subtitles as well",
"summary": "Use subtitles provided by the source along with subtitle API", "summary":
"value": true "Use subtitles provided by the source along with subtitle API",
} "value": true,
},
}, },
{ {
key: 'autoembed_pref_subtitle_source', key: "autoembed_pref_subtitle_source",
listPreference: { listPreference: {
title: 'Preferred subtitle source', title: "Preferred subtitle source",
summary: '', summary: "",
valueIndex: 0, valueIndex: 0,
entries: ["sub.wyzie.ru", "hexa.watch"], entries: ["sub.wyzie.ru", "hexa.watch"],
entryValues: ["1", "2"] entryValues: ["1", "2"],
} },
}, },
]; ];
} }
} }

View File

@@ -1,23 +1,29 @@
const mangayomiSources = [{ const mangayomiSources = [
{
"name": "Soaper", "name": "Soaper",
"id": 764093578,
"lang": "all", "lang": "all",
"baseUrl": "https://soaper.cc", "baseUrl": "https://soaper.cc",
"apiUrl": "", "apiUrl": "",
"iconUrl": "https://www.google.com/s2/favicons?sz=128&domain=https://soaper.cc/", "iconUrl":
"https://www.google.com/s2/favicons?sz=128&domain=https://soaper.cc/",
"typeSource": "multi", "typeSource": "multi",
"version": "1.0.4", "version": "1.0.5",
"itemType": 1, "itemType": 1,
"dateFormat": "", "dateFormat": "",
"dateFormatLocale": "", "dateFormatLocale": "",
"pkgPath": "anime/src/all/soaper.js" "pkgPath": "anime/src/all/soaper.js"
}]; }
];
// Authors: - Swakshan, kodjodevf
class DefaultExtension extends MProvider { class DefaultExtension extends MProvider {
getHeaders(url) { getHeaders(url) {
return { return {
"Referer": url, Referer: url,
"Origin": url Origin: url,
} };
} }
getPreference(key) { getPreference(key) {
@@ -25,56 +31,56 @@ class DefaultExtension extends MProvider {
} }
getBasueUrl() { getBasueUrl() {
return this.getPreference("soaper_override_base_url") return this.getPreference("soaper_override_base_url");
} }
async request(slug) { async request(slug) {
const baseUrl = this.getBasueUrl() const baseUrl = this.getBasueUrl();
var url = `${baseUrl}/${slug}` var url = `${baseUrl}/${slug}`;
var res = await new Client().get(url, this.getHeaders(baseUrl)); var res = await new Client().get(url, this.getHeaders(baseUrl));
var doc = new Document(res.body); var doc = new Document(res.body);
return doc return doc;
} }
async requestJSON(slug, data) { async requestJSON(slug, data) {
const baseUrl = this.getBasueUrl() const baseUrl = this.getBasueUrl();
var url = `${baseUrl}/${slug}` var url = `${baseUrl}/${slug}`;
var res = await new Client().post(url, this.getHeaders(baseUrl), data); var res = await new Client().post(url, this.getHeaders(baseUrl), data);
return JSON.parse(res.body); return JSON.parse(res.body);
} }
async formatList(slug, page) { async formatList(slug, page) {
const baseUrl = this.getPreference("soaper_override_base_url") const baseUrl = this.getPreference("soaper_override_base_url");
slug = parseInt(page) > 1 ? `${slug}?page=${page}` : slug slug = parseInt(page) > 1 ? `${slug}?page=${page}` : slug;
var doc = await this.request(slug); var doc = await this.request(slug);
var list = []; var list = [];
var movies = doc.select(".thumbnail.text-center") var movies = doc.select(".thumbnail.text-center");
for (var movie of movies) { for (var movie of movies) {
var linkSection = movie.selectFirst("div.img-group > a") var linkSection = movie.selectFirst("div.img-group > a");
var link = linkSection.getHref.substring(1,); var link = linkSection.getHref.substring(1);
var poster = linkSection.selectFirst("img").getSrc var poster = linkSection.selectFirst("img").getSrc;
var imageUrl = `${baseUrl}${poster}` var imageUrl = `${baseUrl}${poster}`;
var name = movie.selectFirst("h5").selectFirst("a").text; var name = movie.selectFirst("h5").selectFirst("a").text;
list.push({ name, imageUrl, link }); list.push({ name, imageUrl, link });
} }
var hasNextPage = false var hasNextPage = false;
if (slug.indexOf("search.html?") == -1) { if (slug.indexOf("search.html?") == -1) {
var pagination = doc.select("ul.pagination > li") var pagination = doc.select("ul.pagination > li");
var last_page_num = parseInt(pagination[pagination.length - 2].text); var last_page_num = parseInt(pagination[pagination.length - 2].text);
hasNextPage = page < last_page_num ? true : false; hasNextPage = page < last_page_num ? true : false;
} }
return { list, hasNextPage } return { list, hasNextPage };
} }
async filterList(year = "all", genre = "all", sort = "new", page = 1) { async filterList(year = "all", genre = "all", sort = "new", page = 1) {
year = year == "all" ? "" : `/year/${year}` year = year == "all" ? "" : `/year/${year}`;
genre = genre == "all" ? "" : `/cat/${genre}` genre = genre == "all" ? "" : `/cat/${genre}`;
sort = sort == "new" ? "" : `/sort/${sort}` sort = sort == "new" ? "" : `/sort/${sort}`;
var slug = `${sort}${year}${genre}` var slug = `${sort}${year}${genre}`;
var movieList = await this.formatList(`movielist${slug}`, page); var movieList = await this.formatList(`movielist${slug}`, page);
var seriesList = await this.formatList(`tvlist${slug}`, page); var seriesList = await this.formatList(`tvlist${slug}`, page);
@@ -88,7 +94,7 @@ class DefaultExtension extends MProvider {
var hasNextPage = seriesList.hasNextPage || movieList.hasNextPage; var hasNextPage = seriesList.hasNextPage || movieList.hasNextPage;
return { list, hasNextPage } return { list, hasNextPage };
} }
async getPopular(page) { async getPopular(page) {
@@ -102,15 +108,15 @@ class DefaultExtension extends MProvider {
} }
async search(query, page, filters) { async search(query, page, filters) {
var seriesList = [] var seriesList = [];
var movieList = [] var movieList = [];
var list = []; var list = [];
var res = await this.formatList(`search.html?keyword=${query}`, 1) var res = await this.formatList(`search.html?keyword=${query}`, 1);
var movies = res["list"] var movies = res["list"];
for (var movie of movies) { for (var movie of movies) {
var link = movie.link var link = movie.link;
if (link.indexOf("tv_") != -1) { if (link.indexOf("tv_") != -1) {
seriesList.push(movie); seriesList.push(movie);
} else { } else {
@@ -125,62 +131,66 @@ class DefaultExtension extends MProvider {
list = [...movieList, ...seriesList]; list = [...movieList, ...seriesList];
} }
return { list, hasNextPage: false } return { list, hasNextPage: false };
} }
async getDetail(url) { async getDetail(url) {
var doc = await this.request(url); const baseUrl = this.getPreference("soaper_override_base_url");
var slug = url.replace(`${baseUrl}/`,'')
var doc = await this.request(slug);
var name = doc
.selectFirst(".col-sm-12.col-lg-12.text-center")
.selectFirst("h4")
.text.trim();
var poster = doc
.selectFirst(".thumbnail.text-center")
.selectFirst("img").getSrc;
var imageUrl = `${baseUrl}${poster}`;
const baseUrl = this.getPreference("soaper_override_base_url") var description = doc.selectFirst("p#wrap").text.trim();
var name = doc.selectFirst(".col-sm-12.col-lg-12.text-center").selectFirst("h4").text.trim() var link = `${baseUrl}/${slug}`;
var poster = doc.selectFirst(".thumbnail.text-center").selectFirst("img").getSrc
var imageUrl = `${baseUrl}${poster}`
var description = doc.selectFirst("p#wrap").text.trim() var chapters = [];
var link = `${baseUrl}/${url}` if (slug.indexOf("tv_") != -1) {
var seasonList = doc.select(".alert.alert-info-ex.col-sm-12");
var chapters = [] var seasonCount = seasonList.length;
if (url.indexOf("tv_") != -1) {
var seasonList = doc.select(".alert.alert-info-ex.col-sm-12")
var seasonCount = seasonList.length
for (var season of seasonList) { for (var season of seasonList) {
var eps = season.select(".col-sm-12.col-md-6.col-lg-4.myp1") var eps = season.select(".col-sm-12.col-md-6.col-lg-4.myp1");
for (var ep of eps) { for (var ep of eps) {
var epLinkSection = ep.selectFirst("a") var epLinkSection = ep.selectFirst("a");
var epLink = epLinkSection.getHref.substring(1) var epLink = epLinkSection.getHref.substring(1);
var epName = epLinkSection.text var epName = epLinkSection.text;
chapters.push({ chapters.push({
name: `S${seasonCount}E${epName}`, name: `S${seasonCount}E${epName}`,
url: epLink, url: epLink,
}) });
} }
seasonCount--; seasonCount--;
} }
} else { } else {
chapters.push({ chapters.push({
name: "Movie", name: "Movie",
url: url, url: slug,
}) });
} }
return { name, imageUrl, description, link, chapters } return { name, imageUrl, description, link, chapters };
} }
// For anime episode video list // For anime episode video list
async getVideoList(url) { async getVideoList(url) {
var body = await this.request(url) var body = await this.request(url);
var baseUrl = this.getBasueUrl() var baseUrl = this.getBasueUrl();
var streams = [] var streams = [];
// Traditional servers // Traditional servers
var eId = body.selectFirst("#hId").attr('value') var eId = body.selectFirst("#hId").attr("value");
var hIsW = body.selectFirst("#hIsW").attr('value') var hIsW = body.selectFirst("#hIsW").attr("value");
var apiType = url[0].toUpperCase() var apiType = url[0].toUpperCase();
var servers = [0, 1] var servers = [0, 1];
for (var serverNum of servers) { for (var serverNum of servers) {
var serverName = body.selectFirst(`#server_button_${serverNum}`).text var serverName = body.selectFirst(`#server_button_${serverNum}`).text;
if (serverName.length < 1) continue; if (serverName.length < 1) continue;
var data = { var data = {
pass: eId, pass: eId,
@@ -188,40 +198,42 @@ class DefaultExtension extends MProvider {
extra: "1", extra: "1",
e2: hIsW, e2: hIsW,
server: "" + serverNum, server: "" + serverNum,
} };
var res = await this.requestJSON(`home/index/Get${apiType}InfoAjax`, data) var res = await this.requestJSON(
`home/index/Get${apiType}InfoAjax`,
data
);
var streamUrl = baseUrl + res.val var streamUrl = baseUrl + res.val;
var subs = [] var subs = [];
var vidSubs = res.subs var vidSubs = res.subs;
if (vidSubs != null && vidSubs.length > 0) { if (vidSubs != null && vidSubs.length > 0) {
for (var sub of vidSubs) { for (var sub of vidSubs) {
subs.push({ subs.push({
file: baseUrl + sub.path, file: baseUrl + sub.path,
label: sub.name label: sub.name,
}) });
} }
} }
streams.push({ streams.push({
url: streamUrl, url: streamUrl,
originalUrl: streamUrl, originalUrl: streamUrl,
quality: serverName, quality: serverName,
subtitles: subs subtitles: subs,
}); });
} }
// Download servers // Download servers
var modal_footer = body.select(".modal-footer > a") var modal_footer = body.select(".modal-footer > a");
if (modal_footer.length > 0) { if (modal_footer.length > 0) {
modal_footer.reverse();
modal_footer.reverse()
for (var item of modal_footer) { for (var item of modal_footer) {
var dSlug = item.getHref var dSlug = item.getHref;
var dBody = await this.request(dSlug) var dBody = await this.request(dSlug);
var res = dBody.selectFirst("#res").attr('value') var res = dBody.selectFirst("#res").attr("value");
var mb = dBody.selectFirst("#mb").attr('value') var mb = dBody.selectFirst("#mb").attr("value");
var streamLink = dBody.selectFirst("#link").attr('value') var streamLink = dBody.selectFirst("#link").attr("value");
streams.push({ streams.push({
url: streamLink, url: streamLink,
@@ -230,8 +242,7 @@ class DefaultExtension extends MProvider {
}); });
} }
} }
return streams return streams;
} }
// For manga chapter pages // For manga chapter pages
async getPageList() { async getPageList() {
@@ -242,24 +253,26 @@ class DefaultExtension extends MProvider {
} }
getSourcePreferences() { getSourcePreferences() {
return [{ return [
"key": "soaper_override_base_url", {
key: "soaper_override_base_url",
editTextPreference: { editTextPreference: {
title: "Override base url", title: "Override base url",
summary: "Default: https://soaper.cc", summary: "Default: https://soaper.cc",
value: "https://soaper.cc", value: "https://soaper.cc",
dialogTitle: "Override base url", dialogTitle: "Override base url",
dialogMessage: "", dialogMessage: "",
} },
}, { },
key: 'soaper_content_priority', {
key: "soaper_content_priority",
listPreference: { listPreference: {
title: 'Preferred content priority', title: "Preferred content priority",
summary: 'Choose which type of content to show first', summary: "Choose which type of content to show first",
valueIndex: 0, valueIndex: 0,
entries: ["Movies", "Series"], entries: ["Movies", "Series"],
entryValues: ["movies", "series"] entryValues: ["movies", "series"],
} },
}, },
]; ];
} }

View File

@@ -1,17 +1,20 @@
const mangayomiSources = [{ const mangayomiSources = [
{
"name": "AnimeGG", "name": "AnimeGG",
"lang": "en", "lang": "en",
"id": 209614032,
"baseUrl": "https://www.animegg.org", "baseUrl": "https://www.animegg.org",
"apiUrl": "", "apiUrl": "",
"iconUrl": "https://www.google.com/s2/favicons?sz=256&domain=https://www.animegg.org/", "iconUrl":
"https://www.google.com/s2/favicons?sz=256&domain=https://www.animegg.org/",
"typeSource": "single", "typeSource": "single",
"itemType": 1, "itemType": 1,
"version": "1.0.2", "version": "1.0.3",
"pkgPath": "anime/src/en/animegg.js" "pkgPath": "anime/src/en/animegg.js"
}]; }
];
class DefaultExtension extends MProvider { class DefaultExtension extends MProvider {
constructor() { constructor() {
super(); super();
this.client = new Client(); this.client = new Client();
@@ -19,9 +22,9 @@ class DefaultExtension extends MProvider {
getHeaders(url) { getHeaders(url) {
return { return {
"Referer": this.source.baseUrl, Referer: this.source.baseUrl,
"Origin": this.source.baseUrl Origin: this.source.baseUrl,
} };
} }
getPreference(key) { getPreference(key) {
@@ -29,7 +32,7 @@ class DefaultExtension extends MProvider {
} }
async requestText(slug) { async requestText(slug) {
var url = `${this.source.baseUrl}${slug}` var url = `${this.source.baseUrl}${slug}`;
var res = await this.client.get(url, this.getHeaders()); var res = await this.client.get(url, this.getHeaders());
return res.body; return res.body;
} }
@@ -38,36 +41,34 @@ class DefaultExtension extends MProvider {
} }
async fetchPopularnLatest(slug) { async fetchPopularnLatest(slug) {
var body = await this.request(slug) var body = await this.request(slug);
var items = body.select("li.fea") var items = body.select("li.fea");
var list = [] var list = [];
var hasNextPage = true var hasNextPage = true;
if (items.length > 0) { if (items.length > 0) {
for (var item of items) { for (var item of items) {
var imageUrl = item.selectFirst('img').getSrc var imageUrl = item.selectFirst("img").getSrc;
var linkSection = item.selectFirst('.rightpop').selectFirst('a') var linkSection = item.selectFirst(".rightpop").selectFirst("a");
var link = linkSection.getHref var link = linkSection.getHref;
var name = linkSection.text var name = linkSection.text;
list.push({ list.push({
name, name,
imageUrl, imageUrl,
link link,
}); });
} }
} else {
hasNextPage = false;
} }
else { return { list, hasNextPage };
hasNextPage = false
}
return { list, hasNextPage }
} }
async getPopular(page) { async getPopular(page) {
var start = (page - 1) * 25; var start = (page - 1) * 25;
var limit = start + 25; var limit = start + 25;
var category = "" var category = "";
var pop = this.getPreference("animegg_popular_category") var pop = this.getPreference("animegg_popular_category");
switch (pop) { switch (pop) {
case 1: { case 1: {
category = "sortBy=createdAt&sortDirection=DESC&"; category = "sortBy=createdAt&sortDirection=DESC&";
@@ -85,12 +86,9 @@ class DefaultExtension extends MProvider {
category = "sortBy=sortLetter&sortDirection=ASC&"; category = "sortBy=sortLetter&sortDirection=ASC&";
break; break;
} }
} }
var slug = `/popular-series?${category}start=${start}&limit=${limit}` var slug = `/popular-series?${category}start=${start}&limit=${limit}`;
return await this.fetchPopularnLatest(slug) return await this.fetchPopularnLatest(slug);
} }
get supportsLatest() { get supportsLatest() {
throw new Error("supportsLatest not implemented"); throw new Error("supportsLatest not implemented");
@@ -99,104 +97,99 @@ class DefaultExtension extends MProvider {
var start = (page - 1) * 25; var start = (page - 1) * 25;
var limit = start + 25; var limit = start + 25;
var slug = `/releases?start=${start}&limit=${limit}` var slug = `/releases?start=${start}&limit=${limit}`;
return await this.fetchPopularnLatest(slug) return await this.fetchPopularnLatest(slug);
} }
async search(query, page, filters) { async search(query, page, filters) {
var slug = `/search?q=${query}` var slug = `/search?q=${query}`;
var body = await this.request(slug) var body = await this.request(slug);
var items = body.select(".moose.page > a") var items = body.select(".moose.page > a");
var list = [] var list = [];
for (var item of items) { for (var item of items) {
var imageUrl = item.selectFirst('img').getSrc var imageUrl = item.selectFirst("img").getSrc;
var link = item.getHref var link = item.getHref;
var name = item.selectFirst("h2").text var name = item.selectFirst("h2").text;
list.push({ list.push({
name, name,
imageUrl, imageUrl,
link link,
}); });
} }
return { list, hasNextPage: false } return { list, hasNextPage: false };
} }
statusCode(status) { statusCode(status) {
return { return (
"Ongoing": 0, {
"Completed": 1, Ongoing: 0,
}[status] ?? 5; Completed: 1,
}[status] ?? 5
);
} }
async getDetail(url) { async getDetail(url) {
var link = this.source.baseUrl + url; var baseUrl = this.source.baseUrl;
var slug = url.replace(baseUrl, "");
var link = baseUrl + slug;
var body = await this.request(url) var body = await this.request(slug);
var media = body.selectFirst(".media") var media = body.selectFirst(".media");
var title = media.selectFirst("h1").text var title = media.selectFirst("h1").text;
var spans = media.selectFirst("p.infoami").select("span") var spans = media.selectFirst("p.infoami").select("span");
var statusText = spans[spans.length - 1].text.replace("Status: ", '') var statusText = spans[spans.length - 1].text.replace("Status: ", "");
var status = this.statusCode(statusText) var status = this.statusCode(statusText);
var tagscat = media.select(".tagscat > li");
var genre = [];
tagscat.forEach((tag) => genre.push(tag.text));
var description = body.selectFirst("p.ptext").text;
var chapters = [];
var tagscat = media.select(".tagscat > li") var episodesList = body.select(".newmanga > li");
var genre = [] episodesList.forEach((ep) => {
tagscat.forEach(tag => genre.push(tag.text)) var epTitle = ep.selectFirst("i.anititle").text;
var description = body.selectFirst("p.ptext").text var epNumber = ep.selectFirst("strong").text.replace(title, "Episode");
var chapters = [] var epName = epNumber == epTitle ? epNumber : `${epNumber} - ${epTitle}`;
var epUrl = ep.selectFirst("a").getHref;
var episodesList = body.select(".newmanga > li")
episodesList.forEach(ep => {
var epTitle = ep.selectFirst('i.anititle').text
var epNumber = ep.selectFirst('strong').text.replace(title, "Episode")
var epName = epNumber == epTitle ? epNumber : `${epNumber} - ${epTitle}`
var epUrl = ep.selectFirst("a").getHref
var scanlator = ""; var scanlator = "";
var type = ep.select("span.btn-xs") var type = ep.select("span.btn-xs");
type.forEach(t => { type.forEach((t) => {
scanlator += t.text + ", "; scanlator += t.text + ", ";
});
})
scanlator = scanlator.slice(0, -2); scanlator = scanlator.slice(0, -2);
chapters.push({ name: epName, url: epUrl, scanlator }) chapters.push({ name: epName, url: epUrl, scanlator });
}) });
return { description, status, genre, chapters, link }
return { description, status, genre, chapters, link };
} }
async exxtractStreams(div,audio){ async exxtractStreams(div, audio) {
var slug = div.selectFirst("iframe").getSrc;
var slug = div.selectFirst("iframe").getSrc var streams = [];
var streams = [] if (slug.length < 1) {
if(slug.length < 1){
return streams; return streams;
} }
var body = await this.requestText(slug) var body = await this.requestText(slug);
var sKey = "var videoSources = " var sKey = "var videoSources = ";
var eKey = "var httpProtocol" var eKey = "var httpProtocol";
var start = body.indexOf(sKey) + sKey.length var start = body.indexOf(sKey) + sKey.length;
var end = body.indexOf(eKey) - 8 var end = body.indexOf(eKey) - 8;
var videoSourcesStr = body.substring(start, end) var videoSourcesStr = body.substring(start, end);
let videoSources = eval("(" + videoSourcesStr + ")"); let videoSources = eval("(" + videoSourcesStr + ")");
var headers = this.getHeaders(); var headers = this.getHeaders();
videoSources.forEach(videoSource => { videoSources.forEach((videoSource) => {
var url = this.source.baseUrl +videoSource.file var url = this.source.baseUrl + videoSource.file;
var quality = `${videoSource.label} - ${audio}` var quality = `${videoSource.label} - ${audio}`;
streams.push({ streams.push({
url, url,
originalUrl: url, originalUrl: url,
quality, quality,
headers headers,
}); });
}); });
return streams.reverse(); return streams.reverse();
@@ -204,31 +197,28 @@ class DefaultExtension extends MProvider {
// For anime episode video list // For anime episode video list
async getVideoList(url) { async getVideoList(url) {
var body = await this.request(url) var body = await this.request(url);
var sub = body.selectFirst("#subbed-Animegg") var sub = body.selectFirst("#subbed-Animegg");
var subStreams = await this.exxtractStreams(sub,"Sub") var subStreams = await this.exxtractStreams(sub, "Sub");
var dub = body.selectFirst("#dubbed-Animegg") var dub = body.selectFirst("#dubbed-Animegg");
var dubStreams = await this.exxtractStreams(dub,"Dub") var dubStreams = await this.exxtractStreams(dub, "Dub");
var raw = body.selectFirst("#raw-Animegg") var raw = body.selectFirst("#raw-Animegg");
var rawStreams = await this.exxtractStreams(raw,"Raw") var rawStreams = await this.exxtractStreams(raw, "Raw");
var pref = this.getPreference("animegg_stream_type_1");
var pref = this.getPreference("animegg_stream_type_1")
var streams = []; var streams = [];
if(pref == 0){ if (pref == 0) {
streams = [...subStreams,...dubStreams, ...rawStreams] streams = [...subStreams, ...dubStreams, ...rawStreams];
}else if(pref == 1){ } else if (pref == 1) {
streams = [...dubStreams,...subStreams, ...rawStreams] streams = [...dubStreams, ...subStreams, ...rawStreams];
}else{ } else {
streams = [...rawStreams,...subStreams, ...dubStreams] streams = [...rawStreams, ...subStreams, ...dubStreams];
} }
return streams return streams;
} }
getSourcePreferences() { getSourcePreferences() {
@@ -236,23 +226,29 @@ class DefaultExtension extends MProvider {
{ {
key: "animegg_popular_category", key: "animegg_popular_category",
listPreference: { listPreference: {
title: 'Preferred popular category', title: "Preferred popular category",
summary: '', summary: "",
valueIndex: 0, valueIndex: 0,
entries: ["Popular", "Newest", "Ongoing", "Completed", "Alphabetical"], entries: [
entryValues: ["0", "1", "2", "3", "4"] "Popular",
} "Newest",
"Ongoing",
"Completed",
"Alphabetical",
],
entryValues: ["0", "1", "2", "3", "4"],
},
}, },
{ {
key: "animegg_stream_type_1", key: "animegg_stream_type_1",
listPreference: { listPreference: {
title: 'Preferred stream type', title: "Preferred stream type",
summary: '', summary: "",
valueIndex: 0, valueIndex: 0,
entries: ["Sub","Dub","Raw"], entries: ["Sub", "Dub", "Raw"],
entryValues: ["0", "1", "2"] entryValues: ["0", "1", "2"],
} },
} },
] ];
} }
} }

View File

@@ -6,7 +6,7 @@ const mangayomiSources = [{
"iconUrl": "https://www.google.com/s2/favicons?sz=256&domain=https://www.animeonsen.xyz", "iconUrl": "https://www.google.com/s2/favicons?sz=256&domain=https://www.animeonsen.xyz",
"typeSource": "single", "typeSource": "single",
"itemType": 1, "itemType": 1,
"version": "1.0.0", "version": "1.0.1",
"pkgPath": "anime/src/all/animeonsen.js" "pkgPath": "anime/src/all/animeonsen.js"
}]; }];
@@ -143,7 +143,9 @@ class DefaultExtension extends MProvider {
} }
async getDetail(url) { async getDetail(url) {
var link = `${this.source.baseUrl}/details/${url}` var linkSlug = `${this.source.baseUrl}/details/`
url = url.replace(linkSlug, "")
var link = `${linkSlug}${url}`
var detailsApiSlug = `/${url}/extensive` var detailsApiSlug = `/${url}/extensive`
var animeDetails = await this.request(detailsApiSlug); var animeDetails = await this.request(detailsApiSlug);

View File

@@ -1,17 +1,19 @@
const mangayomiSources = [{ const mangayomiSources = [
{
"name": "AnimeParadise", "name": "AnimeParadise",
"lang": "en", "lang": "en",
"baseUrl": "https://animeparadise.moe", "baseUrl": "https://animeparadise.moe",
"apiUrl": "https://api.animeparadise.moe", "apiUrl": "https://api.animeparadise.moe",
"iconUrl": "https://www.google.com/s2/favicons?sz=128&domain=https://animeparadise.moe", "iconUrl":
"https://www.google.com/s2/favicons?sz=128&domain=https://animeparadise.moe",
"typeSource": "single", "typeSource": "single",
"itemType": 1, "itemType": 1,
"version": "0.0.1", "version": "0.0.2",
"pkgPath": "anime/src/en/animeparadise.js" "pkgPath": "anime/src/en/animeparadise.js"
}]; }
];
class DefaultExtension extends MProvider { class DefaultExtension extends MProvider {
getPreference(key) { getPreference(key) {
const preferences = new SharedPreferences(); const preferences = new SharedPreferences();
return preferences.get(key); return preferences.get(key);
@@ -20,12 +22,12 @@ class DefaultExtension extends MProvider {
async extractFromUrl(url) { async extractFromUrl(url) {
var res = await new Client().get(this.source.baseUrl + url); var res = await new Client().get(this.source.baseUrl + url);
var doc = new Document(res.body); var doc = new Document(res.body);
var jsonData = doc.selectFirst("#__NEXT_DATA__").text var jsonData = doc.selectFirst("#__NEXT_DATA__").text;
return JSON.parse(jsonData).props.pageProps return JSON.parse(jsonData).props.pageProps;
} }
async requestAPI(slug) { async requestAPI(slug) {
var api = `${this.source.apiUrl}/${slug}` var api = `${this.source.apiUrl}/${slug}`;
var response = await new Client().get(api); var response = await new Client().get(api);
var body = JSON.parse(response.body); var body = JSON.parse(response.body);
return body; return body;
@@ -35,89 +37,91 @@ class DefaultExtension extends MProvider {
var jsonData = await this.requestAPI(slug); var jsonData = await this.requestAPI(slug);
var list = []; var list = [];
if ("episodes" in jsonData) { if ("episodes" in jsonData) {
jsonData.episodes.forEach(item => { jsonData.episodes.forEach((item) => {
list.push({ list.push({
"name": item.origin.title, "name": item.origin.title,
"link": item.origin.link, "link": item.origin.link,
"imageUrl": item.image "imageUrl": item.image,
});
}); });
})
} else { } else {
jsonData.data.forEach(item => { jsonData.data.forEach((item) => {
list.push({ list.push({
"name": item.title, "name": item.title,
"link": item.link, "link": item.link,
"imageUrl": item.posterImage.original "imageUrl": item.posterImage.original,
});
}); });
})
} }
return { return {
"list": list, "list": list,
"hasNextPage": false "hasNextPage": false,
} };
} }
async getPopular(page) { async getPopular(page) {
return await this.formList('?sort={"rate": -1 }') return await this.formList('?sort={"rate": -1 }');
} }
async getLatestUpdates(page) { async getLatestUpdates(page) {
var slug = '?sort={"postDate": -1 }'; var slug = '?sort={"postDate": -1 }';
var choice = this.getPreference("animeparadise_pref_latest_tab"); var choice = this.getPreference("animeparadise_pref_latest_tab");
if (choice === "recent_ep") slug = 'ep/recently-added'; if (choice === "recent_ep") slug = "ep/recently-added";
return await this.formList(slug) return await this.formList(slug);
} }
async search(query, page, filters) { async search(query, page, filters) {
var season = filters[0].values[filters[0].state].value var season = filters[0].values[filters[0].state].value;
var year = filters[1].values[filters[1].state].value var year = filters[1].values[filters[1].state].value;
var genre = "genre[]=" var genre = "genre[]=";
for (var filter of filters[2].state) { for (var filter of filters[2].state) {
if (filter.state == true) if (filter.state == true) genre += `${filter.value}&genre[]=`;
genre += `${filter.value}&genre[]=`
} }
var slug = `search?q=${query}&year=${year}&season=${season}&${genre}` var slug = `search?q=${query}&year=${year}&season=${season}&${genre}`;
return await this.formList(slug); return await this.formList(slug);
} }
statusCode(status) { statusCode(status) {
return { return (
{
"current": 0, "current": 0,
"finished": 1, "finished": 1,
}[status] ?? 5; }[status] ?? 5
);
} }
async getDetail(url) { async getDetail(url) {
var link = this.source.baseUrl + `/anime/${url}` var linkSlug = this.source.baseUrl + `/anime/`;
var jsonData = await this.extractFromUrl(`/anime/${url}`) if (url.includes(linkSlug)) url = url.replace(linkSlug, "");
jsonData = jsonData.data
var details = {} var jsonData = await this.extractFromUrl(`/anime/${url}`);
var chapters = [] jsonData = jsonData.data;
details.imageUrl = jsonData.posterImage.original var details = {};
details.description = jsonData.synopsys var chapters = [];
details.genre = jsonData.genres details.imageUrl = jsonData.posterImage.original;
details.status = this.statusCode(jsonData.status) details.description = jsonData.synopsys;
var id = jsonData._id details.genre = jsonData.genres;
var epAPI = await this.requestAPI(`anime/${id}/episode`) details.status = this.statusCode(jsonData.status);
epAPI.data.forEach(ep => { var id = jsonData._id;
var epAPI = await this.requestAPI(`anime/${id}/episode`);
epAPI.data.forEach((ep) => {
var epName = `E${ep.number}: ${ep.title}`; var epName = `E${ep.number}: ${ep.title}`;
var epUrl = `${ep.uid}?origin=${ep.origin}` var epUrl = `${ep.uid}?origin=${ep.origin}`;
chapters.push({ name: epName, url: epUrl }) chapters.push({ name: epName, url: epUrl });
}) });
details.link = `${linkSlug}${url}`;
details.chapters = chapters.reverse(); details.chapters = chapters.reverse();
return details; return details;
} }
// Sorts streams based on user preference. // Sorts streams based on user preference.
async sortStreams(streams) { async sortStreams(streams) {
var sortedStreams = []; var sortedStreams = [];
var copyStreams = streams.slice() var copyStreams = streams.slice();
var pref = await this.getPreference("animeparadise_pref_video_resolution"); var pref = await this.getPreference("animeparadise_pref_video_resolution");
for (var stream of streams) { for (var stream of streams) {
if (stream.quality.indexOf(pref) > -1) { if (stream.quality.indexOf(pref) > -1) {
sortedStreams.push(stream); sortedStreams.push(stream);
var index = copyStreams.indexOf(stream); var index = copyStreams.indexOf(stream);
@@ -127,115 +131,140 @@ class DefaultExtension extends MProvider {
break; break;
} }
} }
return [...sortedStreams, ...copyStreams] return [...sortedStreams, ...copyStreams];
} }
// Extracts the streams url for different resolutions from a hls stream. // Extracts the streams url for different resolutions from a hls stream.
async extractStreams(url) { async extractStreams(url) {
const response = await new Client().get(url); const response = await new Client().get(url);
const body = response.body; const body = response.body;
const lines = body.split('\n'); const lines = body.split("\n");
var streams = [{ var streams = [
{
url: url, url: url,
originalUrl: url, originalUrl: url,
quality: `Auto`, quality: `Auto`,
}]; },
];
for (let i = 0; i < lines.length; i++) { for (let i = 0; i < lines.length; i++) {
if (lines[i].startsWith('#EXT-X-STREAM-INF:')) { if (lines[i].startsWith("#EXT-X-STREAM-INF:")) {
var resolution = lines[i].match(/RESOLUTION=(\d+x\d+)/)[1]; var resolution = lines[i].match(/RESOLUTION=(\d+x\d+)/)[1];
var m3u8Url = lines[i + 1].trim(); var m3u8Url = lines[i + 1].trim();
m3u8Url = url.replace("master.m3u8", m3u8Url) m3u8Url = url.replace("master.m3u8", m3u8Url);
streams.push({ streams.push({
url: m3u8Url, url: m3u8Url,
originalUrl: m3u8Url, originalUrl: m3u8Url,
quality: resolution quality: resolution,
}); });
} }
} }
return streams return streams;
} }
// For anime episode video list // For anime episode video list
async getVideoList(url) { async getVideoList(url) {
var streams = [] var streams = [];
var jsonData = await this.extractFromUrl(`/watch/${url}`); var jsonData = await this.extractFromUrl(`/watch/${url}`);
var epData = jsonData.episode var epData = jsonData.episode;
streams = await this.extractStreams(epData.streamLink) streams = await this.extractStreams(epData.streamLink);
var subtitles = [] var subtitles = [];
epData.subData.forEach(sub => { epData.subData.forEach((sub) => {
subtitles.push({ subtitles.push({
"label": sub.label, "label": sub.label,
"file": `${this.source.apiUrl}/stream/file/${sub.src}`, "file": `${this.source.apiUrl}/stream/file/${sub.src}`,
}); });
}) });
streams[0].subtitles = subtitles streams[0].subtitles = subtitles;
return streams
return streams;
} }
addCatogory(arr, typ) { addCatogory(arr, typ) {
arr = arr.map(x => ({ type_name: typ, name: x, value: x })) arr = arr.map((x) => ({ type_name: typ, name: x, value: x }));
arr.unshift({ arr.unshift({
type_name: typ, type_name: typ,
name: 'All', name: "All",
value: '' value: "",
}) });
return arr return arr;
} }
getFilterList() { getFilterList() {
var seasons = ["Winter", "Spring", "Summer", "Fall"] var seasons = ["Winter", "Spring", "Summer", "Fall"];
const currentYear = new Date().getFullYear(); const currentYear = new Date().getFullYear();
var years = Array.from({ length: currentYear - 1939 }, (_, i) => (i + 1940).toString()).reverse() var years = Array.from({ length: currentYear - 1939 }, (_, i) =>
(i + 1940).toString()
).reverse();
var genres = ["Action", "Adventure", "Comedy", "Drama", "Ecchi", "Fantasy", "Horror", "Mahou Shojo", "Mecha", "Music", "Mystery", "Psychological", "Romance", "Sci-Fi", "Slice of Life", "Sports", "Supernatural", "Thriller"].map(x => ({ type_name: "CheckBox", name: x, value: x })) var genres = [
"Action",
"Adventure",
"Comedy",
"Drama",
"Ecchi",
"Fantasy",
"Horror",
"Mahou Shojo",
"Mecha",
"Music",
"Mystery",
"Psychological",
"Romance",
"Sci-Fi",
"Slice of Life",
"Sports",
"Supernatural",
"Thriller",
].map((x) => ({ type_name: "CheckBox", name: x, value: x }));
return [ return [
{ {
type_name: "SelectFilter", type_name: "SelectFilter",
name: "Season", name: "Season",
state: 0, state: 0,
values: this.addCatogory(seasons, "SelectOption") values: this.addCatogory(seasons, "SelectOption"),
}, { },
{
type_name: "SelectFilter", type_name: "SelectFilter",
name: "Year", name: "Year",
state: 0, state: 0,
values: this.addCatogory(years, "SelectOption") values: this.addCatogory(years, "SelectOption"),
}, { },
{
type_name: "GroupFilter", type_name: "GroupFilter",
name: "Genres", name: "Genres",
state: genres state: genres,
} },
] ];
} }
getSourcePreferences() { getSourcePreferences() {
return [{ return [
key: 'animeparadise_pref_latest_tab', {
key: "animeparadise_pref_latest_tab",
listPreference: { listPreference: {
title: 'Latest tab category', title: "Latest tab category",
summary: 'Anime list to be shown in latest tab', summary: "Anime list to be shown in latest tab",
valueIndex: 0, valueIndex: 0,
entries: ["Recently added anime", "Recently added episode"], entries: ["Recently added anime", "Recently added episode"],
entryValues: ["recent_ani", "recent_ep"] entryValues: ["recent_ani", "recent_ep"],
} },
}, { },
key: 'animeparadise_pref_video_resolution', {
key: "animeparadise_pref_video_resolution",
listPreference: { listPreference: {
title: 'Preferred video resolution', title: "Preferred video resolution",
summary: '', summary: "",
valueIndex: 0, valueIndex: 0,
entries: ["Auto", "1080p", "720p", "360p"], entries: ["Auto", "1080p", "720p", "360p"],
entryValues: ["auto", "1080", "720", "360"] entryValues: ["auto", "1080", "720", "360"],
}
}, },
] },
];
} }
} }

View File

@@ -1,14 +1,17 @@
const mangayomiSources = [{ const mangayomiSources = [
{
"name": "AnimeZ", "name": "AnimeZ",
"lang": "en", "lang": "en",
"baseUrl": "https://animez.org", "baseUrl": "https://animez.org",
"apiUrl": "", "apiUrl": "",
"iconUrl": "https://www.google.com/s2/favicons?sz=256&domain=https://animez.org/", "iconUrl":
"https://www.google.com/s2/favicons?sz=256&domain=https://animez.org/",
"typeSource": "multi", "typeSource": "multi",
"itemType": 1, "itemType": 1,
"version": "1.0.1", "version": "1.0.2",
"pkgPath": "anime/src/en/animez.js" "pkgPath": "anime/src/en/animez.js"
}]; }
];
class DefaultExtension extends MProvider { class DefaultExtension extends MProvider {
constructor() { constructor() {
@@ -19,7 +22,7 @@ class DefaultExtension extends MProvider {
getHeaders(url) { getHeaders(url) {
return { return {
"Referer": this.source.baseUrl, "Referer": this.source.baseUrl,
} };
} }
getPreference(key) { getPreference(key) {
@@ -27,161 +30,168 @@ class DefaultExtension extends MProvider {
} }
async request(slug) { async request(slug) {
var url = this.source.baseUrl + slug var url = this.source.baseUrl + slug;
var res = await this.client.get(url, this.getHeaders()); var res = await this.client.get(url, this.getHeaders());
return new Document(res.body); return new Document(res.body);
} }
async page(slug) { async page(slug) {
var body = await this.request(slug) var body = await this.request(slug);
var list = [] var list = [];
var hasNextPage = false; var hasNextPage = false;
var animes = body.select("li.TPostMv") var animes = body.select("li.TPostMv");
animes.forEach(anime => { animes.forEach((anime) => {
var link = anime.selectFirst("a").getHref var link = anime.selectFirst("a").getHref;
var name = anime.selectFirst('h2.Title').text; var name = anime.selectFirst("h2.Title").text;
var imageUrl = this.source.baseUrl +"/"+ anime.selectFirst('img').getSrc; var imageUrl =
this.source.baseUrl + "/" + anime.selectFirst("img").getSrc;
list.push({ name, link, imageUrl }); list.push({ name, link, imageUrl });
}); });
var paginations = body.select(".pagination > li") var paginations = body.select(".pagination > li");
hasNextPage = paginations[paginations.length - 1].text == "Last" ? true : false hasNextPage =
paginations[paginations.length - 1].text == "Last" ? true : false;
return { list, hasNextPage } return { list, hasNextPage };
} }
sortByPref(key) { sortByPref(key) {
var sort = parseInt(this.getPreference(key)) var sort = parseInt(this.getPreference(key));
var sortBy = "hot" var sortBy = "hot";
switch (sort) { switch (sort) {
case 1: { case 1: {
sortBy = "lastest-chap" sortBy = "lastest-chap";
break; break;
} case 2: { }
sortBy = "hot" case 2: {
sortBy = "hot";
break; break;
} }
case 3: { case 3: {
sortBy = "lastest-manga" sortBy = "lastest-manga";
break; break;
} }
case 4: { case 4: {
sortBy = "top-manga" sortBy = "top-manga";
break; break;
} }
case 5: { case 5: {
sortBy = "top-month" sortBy = "top-month";
break; break;
} }
case 6: { case 6: {
sortBy = "top-week" sortBy = "top-week";
break; break;
} }
case 7: { case 7: {
sortBy = "top-day" sortBy = "top-day";
break; break;
} }
case 8: { case 8: {
sortBy = "follow" sortBy = "follow";
break; break;
} }
case 9: { case 9: {
sortBy = "comment" sortBy = "comment";
break; break;
} }
case 10: { case 10: {
sortBy = "num-chap" sortBy = "num-chap";
break; break;
} }
} }
return sortBy; return sortBy;
} }
async getPopular(page) { async getPopular(page) {
var sortBy = this.sortByPref("animez_pref_popular_section") var sortBy = this.sortByPref("animez_pref_popular_section");
var slug = `/?act=search&f[status]=all&f[sortby]=${sortBy}&&pageNum=${page}` var slug = `/?act=search&f[status]=all&f[sortby]=${sortBy}&&pageNum=${page}`;
return await this.page(slug) return await this.page(slug);
} }
get supportsLatest() { get supportsLatest() {
throw new Error("supportsLatest not implemented"); throw new Error("supportsLatest not implemented");
} }
async getLatestUpdates(page) { async getLatestUpdates(page) {
var sortBy = this.sortByPref("animez_pref_latest_section") var sortBy = this.sortByPref("animez_pref_latest_section");
var slug = `/?act=search&f[status]=all&f[sortby]=${sortBy}&&pageNum=${page}` var slug = `/?act=search&f[status]=all&f[sortby]=${sortBy}&&pageNum=${page}`;
return await this.page(slug) return await this.page(slug);
} }
async search(query, page, filters) { async search(query, page, filters) {
var slug = `/?act=search&f[status]=all&f[keyword]=${query}&&pageNum=${page}` var slug = `/?act=search&f[status]=all&f[keyword]=${query}&&pageNum=${page}`;
return await this.page(slug) return await this.page(slug);
} }
async getDetail(url) { async getDetail(url) {
var link = this.source.baseUrl + url; var baseUrl = this.source.baseUrl;
if (url.includes(baseUrl)) url = url.replace(baseUrl, "");
var link = +url;
var body = await this.request(url); var body = await this.request(url);
var name = body.selectFirst("#title-detail-manga").text var name = body.selectFirst("#title-detail-manga").text;
var animeId = body.selectFirst("#title-detail-manga").attr("data-manga") var animeId = body.selectFirst("#title-detail-manga").attr("data-manga");
var genre = [] var genre = [];
body.select("li.AAIco-adjust")[3].select("a").forEach(g => genre.push(g.text)) body
var description = body.selectFirst("#summary_shortened").text .select("li.AAIco-adjust")[3]
.select("a")
.forEach((g) => genre.push(g.text));
var description = body.selectFirst("#summary_shortened").text;
var chapters = [];
var chapters = [] var chapLen = 0;
var chapLen = 0 var pageNum = 1;
var pageNum = 1
var hasNextPage = true; var hasNextPage = true;
while(hasNextPage) { while (hasNextPage) {
var pageSlug = `?act=ajax&code=load_list_chapter&manga_id=${animeId}&page_num=${pageNum}&chap_id=0&keyword=` var pageSlug = `?act=ajax&code=load_list_chapter&manga_id=${animeId}&page_num=${pageNum}&chap_id=0&keyword=`;
var pageBody = await this.request(pageSlug); var pageBody = await this.request(pageSlug);
var parsedBody = JSON.parse(pageBody.html); var parsedBody = JSON.parse(pageBody.html);
var nav = parsedBody.nav var nav = parsedBody.nav;
if(nav==null){ // if "nav" doesnt exists there is no next page if (nav == null) {
// if "nav" doesnt exists there is no next page
hasNextPage = false; hasNextPage = false;
} else {
}else{ var navLi = new Document(nav).select(".page-link.next").length;
var navLi = new Document(nav).select(".page-link.next").length if (navLi > 0) {
if(navLi>0){ // if "nav" exists and has li.next then there is next page // if "nav" exists and has li.next then there is next page
pageNum++; pageNum++;
}else{// if "nav" exists and doesn't have li.next then there is no next page } else {
// if "nav" exists and doesn't have li.next then there is no next page
hasNextPage = false; hasNextPage = false;
} }
} }
var list_chap = new Document(parsedBody.list_chap).select(
"li.wp-manga-chapter"
);
var list_chap = new Document(parsedBody.list_chap).select('li.wp-manga-chapter') list_chap.forEach((chapter) => {
var a = chapter.selectFirst("a");
list_chap.forEach(chapter => { var title = a.text;
var a = chapter.selectFirst("a") var epLink = a.getHref;
var title = a.text var scanlator = "Sub";
var epLink = a.getHref if (title.indexOf("Dub") > 0) {
var scanlator = "Sub" title = title.replace("-Dub", "");
if(title.indexOf("Dub")>0){ scanlator = "Dub";
title = title.replace("-Dub","")
scanlator = "Dub"
} }
title = title.indexOf("Movie") > -1? title : `Episode ${title}` title = title.indexOf("Movie") > -1 ? title : `Episode ${title}`;
var epData = { var epData = {
name:title, name: title,
url:epLink, url: epLink,
scanlator scanlator,
} };
if(chapLen>0){ if (chapLen > 0) {
var pos = chapLen -1 var pos = chapLen - 1;
var lastEntry = chapters[pos] var lastEntry = chapters[pos];
if(lastEntry.name == epData.name){ // if last entries name is same then append url and scanlator to last entry if (lastEntry.name == epData.name) {
chapters.pop() // remove the last entry // if last entries name is same then append url and scanlator to last entry
epData.url = `${epData.url}||${lastEntry.url}` chapters.pop(); // remove the last entry
epData.scanlator = `${lastEntry.scanlator}, ${epData.scanlator}` epData.url = `${epData.url}||${lastEntry.url}`;
epData.scanlator = `${lastEntry.scanlator}, ${epData.scanlator}`;
chapLen = pos; // since the last entry is removed the chapLen will decrease chapLen = pos; // since the last entry is removed the chapLen will decrease
} }
} }
chapters.push(epData) chapters.push(epData);
chapLen++; chapLen++;
}) });
} }
return { return {
@@ -196,7 +206,7 @@ class DefaultExtension extends MProvider {
sortStreams(streams) { sortStreams(streams) {
var sortedStreams = []; var sortedStreams = [];
var copyStreams = streams.slice() var copyStreams = streams.slice();
var pref = this.getPreference("animez_pref_stream_audio"); var pref = this.getPreference("animez_pref_stream_audio");
for (var stream of streams) { for (var stream of streams) {
if (stream.quality.indexOf(pref) > -1) { if (stream.quality.indexOf(pref) > -1) {
@@ -208,60 +218,83 @@ class DefaultExtension extends MProvider {
break; break;
} }
} }
return [...sortedStreams, ...copyStreams] return [...sortedStreams, ...copyStreams];
} }
// For anime episode video list // For anime episode video list
async getVideoList(url) { async getVideoList(url) {
var linkSlugs = url.split("||") var linkSlugs = url.split("||");
var streams = []; var streams = [];
for(var slug of linkSlugs){ for (var slug of linkSlugs) {
var body = await this.request(slug) var body = await this.request(slug);
var iframeSrc = body.selectFirst("iframe").getSrc var iframeSrc = body.selectFirst("iframe").getSrc;
var streamLink = iframeSrc.replace("/embed/","/anime/") var streamLink = iframeSrc.replace("/embed/", "/anime/");
var audio = slug.indexOf("dub-") > -1 ? "Dub" : "Sub" var audio = slug.indexOf("dub-") > -1 ? "Dub" : "Sub";
streams.push({ streams.push({
url: streamLink, url: streamLink,
originalUrl: streamLink, originalUrl: streamLink,
quality: audio, quality: audio,
}) });
} }
return sortStreams(streams); return sortStreams(streams);
} }
getSourcePreferences() { getSourcePreferences() {
return [{ return [
key: 'animez_pref_popular_section', {
key: "animez_pref_popular_section",
listPreference: { listPreference: {
title: 'Preferred popular content', title: "Preferred popular content",
summary: '', summary: "",
valueIndex: 1, valueIndex: 1,
entries: ["Latest update", "Hot", "New releases", "Top all", "Top month", "Top week", "Top day", "Top follow", "Top comments", "Number of episodes"], entries: [
entryValues: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"] "Latest update",
} "Hot",
}, { "New releases",
key: 'animez_pref_latest_section', "Top all",
"Top month",
"Top week",
"Top day",
"Top follow",
"Top comments",
"Number of episodes",
],
entryValues: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"],
},
},
{
key: "animez_pref_latest_section",
listPreference: { listPreference: {
title: 'Preferred latest content', title: "Preferred latest content",
summary: '', summary: "",
valueIndex: 0, valueIndex: 0,
entries: ["Latest update", "Hot", "New releases", "Top all", "Top month", "Top week", "Top day", "Top follow", "Top comments", "Number of episodes"], entries: [
entryValues: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"] "Latest update",
} "Hot",
}, { "New releases",
key: 'animez_pref_stream_audio', "Top all",
"Top month",
"Top week",
"Top day",
"Top follow",
"Top comments",
"Number of episodes",
],
entryValues: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"],
},
},
{
key: "animez_pref_stream_audio",
listPreference: { listPreference: {
title: 'Preferred stream audio', title: "Preferred stream audio",
summary: '', summary: "",
valueIndex: 0, valueIndex: 0,
entries: ["Sub","Dub"], entries: ["Sub", "Dub"],
entryValues: ["Sub","Dub"], entryValues: ["Sub", "Dub"],
} },
},] },
];
} }
} }

View File

@@ -6,7 +6,7 @@ const mangayomiSources = [{
"iconUrl": "https://www.google.com/s2/favicons?sz=128&domain=https://gojo.wtf/", "iconUrl": "https://www.google.com/s2/favicons?sz=128&domain=https://gojo.wtf/",
"typeSource": "multi", "typeSource": "multi",
"itemType": 1, "itemType": 1,
"version": "0.0.5", "version": "0.0.6",
"pkgPath": "anime/src/en/gojo.js" "pkgPath": "anime/src/en/gojo.js"
}]; }];
@@ -103,6 +103,9 @@ class DefaultExtension extends MProvider {
async getDetail(url) { async getDetail(url) {
var linkSlug = `${this.source.baseUrl}/watch/`
if (url.includes(linkSlug)) url = url.replace(linkSlug, "");
var anilistId = url var anilistId = url
var res = await this.gojoAPI(`/info/${anilistId}`) var res = await this.gojoAPI(`/info/${anilistId}`)
if (res == null) { if (res == null) {
@@ -111,7 +114,7 @@ class DefaultExtension extends MProvider {
var name = this.getTitle(res.title) var name = this.getTitle(res.title)
var imageUrl = res.coverImage.large var imageUrl = res.coverImage.large
var description = res.description; var description = res.description;
var link = `${this.source.baseUrl}/watch/${anilistId}` var link = `${linkSlug}${anilistId}`
var genres = res.genres var genres = res.genres
var status = (() => { var status = (() => {
switch (res.status) { switch (res.status) {

View File

@@ -5,7 +5,7 @@ const mangayomiSources = [{
"apiUrl": "", "apiUrl": "",
"iconUrl": "https://www.google.com/s2/favicons?sz=128&domain=https://sudatchi.com", "iconUrl": "https://www.google.com/s2/favicons?sz=128&domain=https://sudatchi.com",
"typeSource": "single", "typeSource": "single",
"version": "1.1.0", "version": "1.1.1",
"dateFormat": "", "dateFormat": "",
"dateFormatLocale": "", "dateFormatLocale": "",
"itemType": 1, "itemType": 1,
@@ -148,8 +148,11 @@ class DefaultExtension extends MProvider {
async getDetail(url) { async getDetail(url) {
var linkSlug = "https://sudatchi.com/anime/"
if (url.includes(linkSlug)) url = url.replace(linkSlug, "");
var lang = this.getPreference("sudatchi_pref_lang") var lang = this.getPreference("sudatchi_pref_lang")
var link = `https://sudatchi.com/anime/${url}` var link = `${linkSlug}${url}`
var details = await this.requestApi(`/anime/${url}`); var details = await this.requestApi(`/anime/${url}`);
var titles = details.title var titles = details.title
var name = titles.romaji var name = titles.romaji

View File

@@ -1,81 +1,89 @@
const mangayomiSources = [{ const mangayomiSources = [
{
"name": "Mangapill", "name": "Mangapill",
"lang": "en", "lang": "en",
"baseUrl": "https://mangapill.com", "baseUrl": "https://mangapill.com",
"apiUrl": "", "apiUrl": "",
"iconUrl": "https://www.google.com/s2/favicons?sz=64&domain=https://mangapill.com/", "iconUrl":
"https://www.google.com/s2/favicons?sz=64&domain=https://mangapill.com/",
"typeSource": "single", "typeSource": "single",
"isManga": true, "isManga": true,
"version": "1.0.2", "version": "1.0.3",
"dateFormat": "", "dateFormat": "",
"dateFormatLocale": "", "dateFormatLocale": "",
"pkgPath": "manga/src/en/mangapill.js" "pkgPath": "manga/src/en/mangapill.js"
}]; }
];
class DefaultExtension extends MProvider { class DefaultExtension extends MProvider {
getHeaders(url) { getHeaders(url) {
return { return {
"Referer": this.source.baseUrl "Referer": this.source.baseUrl,
} };
} }
statusCode(status) { statusCode(status) {
return { return (
{
"publishing": 0, "publishing": 0,
"finished": 1, "finished": 1,
"on hiatus": 2, "on hiatus": 2,
"discontinued": 3, "discontinued": 3,
"not yet published": 4, "not yet published": 4,
}[status] ?? 5; }[status] ?? 5
);
} }
async getPreference(key) { async getPreference(key) {
const preferences = new SharedPreferences(); const preferences = new SharedPreferences();
return parseInt(preferences.get(key)) return parseInt(preferences.get(key));
} }
async getMangaList(slug) { async getMangaList(slug) {
var lang = await this.getPreference("pref_title_lang"); var lang = await this.getPreference("pref_title_lang");
var url = `${this.source.baseUrl}/${slug}` var url = `${this.source.baseUrl}/${slug}`;
var res = await new Client().get(url, this.getHeaders()); var res = await new Client().get(url, this.getHeaders());
var doc = new Document(res.body); var doc = new Document(res.body);
var list = []; var list = [];
var mangaElements = doc.select("div.grid.gap-3.lg > div") var mangaElements = doc.select("div.grid.gap-3.lg > div");
for (var manga of mangaElements) { for (var manga of mangaElements) {
var details = manga.selectFirst('div').select('a'); var details = manga.selectFirst("div").select("a");
var detLen = details.length var detLen = details.length;
details = details[detLen - 1] details = details[detLen - 1];
var imageUrl = manga.selectFirst("img").getSrc; var imageUrl = manga.selectFirst("img").getSrc;
var link = details.getHref; var link = details.getHref;
var nameSection = details.select('div'); var nameSection = details.select("div");
var name = (nameSection[1] && lang == 2) ? nameSection[1].text : nameSection[0].text var name =
nameSection[1] && lang == 2 ? nameSection[1].text : nameSection[0].text;
list.push({ name, imageUrl, link }); list.push({ name, imageUrl, link });
} }
var hasNextPage = false; var hasNextPage = false;
if (slug.includes("search?q")) { if (slug.includes("search?q")) {
hasNextPage = doc.selectFirst(".container.py-3 a.btn.btn-sm").className ? true : false hasNextPage = doc.selectFirst(".container.py-3 a.btn.btn-sm").className
? true
: false;
} }
return { list, hasNextPage } return { list, hasNextPage };
} }
async getNavPage(prefKey) { async getNavPage(prefKey) {
var val = await this.getPreference(prefKey); var val = await this.getPreference(prefKey);
var slug = '' var slug = "";
switch (val) { switch (val) {
case 1: { case 1: {
slug = 'mangas/new' slug = "mangas/new";
break; break;
} }
case 2: { case 2: {
slug = 'chapters' slug = "chapters";
break; break;
} }
} }
return await this.getMangaList(slug) return await this.getMangaList(slug);
} }
async getPopular(page) { async getPopular(page) {
@@ -90,53 +98,68 @@ class DefaultExtension extends MProvider {
} }
async searchManga(query, status, type, genre, page) { async searchManga(query, status, type, genre, page) {
var slug = `search?q=${query}&status=${status}&type=${type}${genre}&page=${page}` var slug = `search?q=${query}&status=${status}&type=${type}${genre}&page=${page}`;
return await this.getMangaList(slug) return await this.getMangaList(slug);
} }
async search(query, page, filters) { async search(query, page, filters) {
var type = filters[0].values[filters[0].state].value var type = filters[0].values[filters[0].state].value;
var status = filters[1].values[filters[1].state].value var status = filters[1].values[filters[1].state].value;
var genre = "" var genre = "";
for (var filter of filters[2].state) { for (var filter of filters[2].state) {
if (filter.state == true) if (filter.state == true) genre += `&genre=${filter.value}`;
genre += `&genre=${filter.value}`
} }
return await this.searchManga(query, status, type, genre, page); return await this.searchManga(query, status, type, genre, page);
} }
async getMangaDetail(slug) { async getMangaDetail(slug) {
var lang = await this.getPreference("pref_title_lang"); var lang = await this.getPreference("pref_title_lang");
var baseUrl = this.source.baseUrl;
if (slug.includes(baseUrl)) slug = slug.replace(baseUrl, "");
var link = `${this.source.baseUrl}${slug}` var link = `${baseUrl}${slug}`;
var res = await new Client().get(link, this.getHeaders()); var res = await new Client().get(link, this.getHeaders());
var doc = new Document(res.body); var doc = new Document(res.body);
var mangaName = doc.selectFirst(".mb-3 .font-bold.text-lg").text var mangaName = doc.selectFirst(".mb-3 .font-bold.text-lg").text;
if (doc.selectFirst(".mb-3 .text-sm.text-secondary") && lang == 2) mangaName = doc.selectFirst(".mb-3 .text-sm.text-secondary").text if (doc.selectFirst(".mb-3 .text-sm.text-secondary") && lang == 2)
var description = doc.selectFirst("meta[name='description']").attr("content") mangaName = doc.selectFirst(".mb-3 .text-sm.text-secondary").text;
var imageUrl = doc.selectFirst(".w-full.h-full").getSrc var description = doc
var statusText = doc.select(".grid.grid-cols-1 > div")[1].selectFirst("div").text .selectFirst("meta[name='description']")
var status = this.statusCode(statusText) .attr("content");
var imageUrl = doc.selectFirst(".w-full.h-full").getSrc;
var statusText = doc
.select(".grid.grid-cols-1 > div")[1]
.selectFirst("div").text;
var status = this.statusCode(statusText);
var genre = [] var genre = [];
var genreList = doc.select("a.mr-1") var genreList = doc.select("a.mr-1");
for (var gen of genreList) { genre.push(gen.text) } for (var gen of genreList) {
genre.push(gen.text);
var chapters = []
var chapList = doc.select("div.my-3.grid > a")
for (var chap of chapList) {
var name = chap.text
var url = chap.getHref
chapters.push({ name, url })
} }
return { name: mangaName, description, link, imageUrl, status, genre, chapters }
var chapters = [];
var chapList = doc.select("div.my-3.grid > a");
for (var chap of chapList) {
var name = chap.text;
var url = chap.getHref;
chapters.push({ name, url });
}
return {
name: mangaName,
description,
link,
imageUrl,
status,
genre,
chapters,
};
} }
async getDetail(url) { async getDetail(url) {
return await this.getMangaDetail(url); return await this.getMangaDetail(url);
} }
// For anime episode video list // For anime episode video list
async getVideoList(url) { async getVideoList(url) {
@@ -145,20 +168,20 @@ class DefaultExtension extends MProvider {
// For manga chapter pages // For manga chapter pages
async getPageList(url) { async getPageList(url) {
var link = `${this.source.baseUrl}${url}` var link = `${this.source.baseUrl}${url}`;
var res = await new Client().get(link, this.getHeaders()); var res = await new Client().get(link, this.getHeaders());
var doc = new Document(res.body); var doc = new Document(res.body);
var urls = []; var urls = [];
var pages = doc.select("chapter-page") var pages = doc.select("chapter-page");
for (var page of pages) { for (var page of pages) {
var img = page.selectFirst("img").getSrc var img = page.selectFirst("img").getSrc;
if (img != null) urls.push(img); if (img != null) urls.push(img);
} }
return urls return urls;
} }
getFilterList() { getFilterList() {
@@ -175,8 +198,8 @@ class DefaultExtension extends MProvider {
["Doujinshi", "doujinshi"], ["Doujinshi", "doujinshi"],
["Manhwa", "manhwa"], ["Manhwa", "manhwa"],
["Manhua", "manhua"], ["Manhua", "manhua"],
["Oel", "oel"] ["Oel", "oel"],
].map(x => ({ type_name: 'SelectOption', name: x[0], value: x[1] })) ].map((x) => ({ type_name: "SelectOption", name: x[0], value: x[1] })),
}, },
{ {
type_name: "SelectFilter", type_name: "SelectFilter",
@@ -188,9 +211,10 @@ class DefaultExtension extends MProvider {
["Finished", "finished"], ["Finished", "finished"],
["On hiatus", "on hiatus"], ["On hiatus", "on hiatus"],
["Discontinued", "discontinued"], ["Discontinued", "discontinued"],
["Not yet published", "not yet published"] ["Not yet published", "not yet published"],
].map(x => ({ type_name: 'SelectOption', name: x[0], value: x[1] })) ].map((x) => ({ type_name: "SelectOption", name: x[0], value: x[1] })),
}, { },
{
type_name: "GroupFilter", type_name: "GroupFilter",
name: "Genre", name: "Genre",
state: [ state: [
@@ -239,42 +263,44 @@ class DefaultExtension extends MProvider {
["Tragedy", "Tragedy"], ["Tragedy", "Tragedy"],
["Vampire", "Vampire"], ["Vampire", "Vampire"],
["Yaoi", "Yaoi"], ["Yaoi", "Yaoi"],
["Yuri", "Yuri"] ["Yuri", "Yuri"],
].map(x => ({ type_name: 'CheckBox', name: x[0], value: x[1] })) ].map((x) => ({ type_name: "CheckBox", name: x[0], value: x[1] })),
} },
]; ];
} }
getSourcePreferences() { getSourcePreferences() {
return [{ return [
key: 'pref_popular_content', {
key: "pref_popular_content",
listPreference: { listPreference: {
title: 'Preferred popular content', title: "Preferred popular content",
summary: '', summary: "",
valueIndex: 0, valueIndex: 0,
entries: ["New Mangas", "Recent Chapters"], entries: ["New Mangas", "Recent Chapters"],
entryValues: ["1", "2"] entryValues: ["1", "2"],
} },
}, { },
key: 'pref_latest_content', {
key: "pref_latest_content",
listPreference: { listPreference: {
title: 'Preferred latest content', title: "Preferred latest content",
summary: '', summary: "",
valueIndex: 1, valueIndex: 1,
entries: ["New Mangas", "Recent Chapters"], entries: ["New Mangas", "Recent Chapters"],
entryValues: ["1", "2"] entryValues: ["1", "2"],
} },
}, { },
key: 'pref_title_lang', {
key: "pref_title_lang",
listPreference: { listPreference: {
title: 'Preferred title language', title: "Preferred title language",
summary: '', summary: "",
valueIndex: 0, valueIndex: 0,
entries: ["Romaji", "English"], entries: ["Romaji", "English"],
entryValues: ["1", "2"] entryValues: ["1", "2"],
} },
} },
]; ];
} }
} }

View File

@@ -1,14 +1,17 @@
const mangayomiSources = [{ const mangayomiSources = [
{
"name": "ReadComicOnline", "name": "ReadComicOnline",
"lang": "en", "lang": "en",
"baseUrl": "https://readcomiconline.li", "baseUrl": "https://readcomiconline.li",
"apiUrl": "", "apiUrl": "",
"iconUrl": "https://www.google.com/s2/favicons?sz=256&domain=https://readcomiconline.li/", "iconUrl":
"https://www.google.com/s2/favicons?sz=256&domain=https://readcomiconline.li/",
"typeSource": "single", "typeSource": "single",
"itemType": 0, "itemType": 0,
"version": "0.1.2", "version": "0.1.3",
"pkgPath": "manga/src/en/readcomiconline.js" "pkgPath": "manga/src/en/readcomiconline.js"
}]; }
];
class DefaultExtension extends MProvider { class DefaultExtension extends MProvider {
constructor() { constructor() {
@@ -22,165 +25,192 @@ class DefaultExtension extends MProvider {
getHeaders() { getHeaders() {
return { return {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6832.64 Safari/537.36", "User-Agent":
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.6832.64 Safari/537.36",
"Referer": this.source.baseUrl, "Referer": this.source.baseUrl,
"Origin": this.source.baseUrl, "Origin": this.source.baseUrl,
} };
} }
async request(slug) { async request(slug) {
var url = slug var url = slug;
var baseUrl = this.source.baseUrl var baseUrl = this.source.baseUrl;
if (!slug.includes(baseUrl)) url = baseUrl + slug; if (!slug.includes(baseUrl)) url = baseUrl + slug;
var res = await this.client.get(url, this.getHeaders()); var res = await this.client.get(url, this.getHeaders());
return new Document(res.body); return new Document(res.body);
} }
async getListPage(slug, page) { async getListPage(slug, page) {
var url = `${slug}page=${page}` var url = `${slug}page=${page}`;
var doc = await this.request(url); var doc = await this.request(url);
var baseUrl = this.source.baseUrl var baseUrl = this.source.baseUrl;
var list = [] var list = [];
var comicList = doc.select(".list-comic > .item") var comicList = doc.select(".list-comic > .item");
comicList.forEach(item => { comicList.forEach((item) => {
var name = item.selectFirst(".title").text; var name = item.selectFirst(".title").text;
var link = item.selectFirst("a").getHref var link = item.selectFirst("a").getHref;
var imageSlug = item.selectFirst("img").getSrc var imageSlug = item.selectFirst("img").getSrc;
var imageUrl = imageSlug.includes("http") ? imageSlug : `${baseUrl}${imageSlug}`; var imageUrl = imageSlug.includes("http")
? imageSlug
: `${baseUrl}${imageSlug}`;
list.push({ name, link, imageUrl }); list.push({ name, link, imageUrl });
}); });
var pager = doc.select("ul.pager > li") var pager = doc.select("ul.pager > li");
var hasNextPage = false var hasNextPage = false;
if (pager.length > 0) hasNextPage = pager[pager.length - 1].text.includes("Last") ? true : false; if (pager.length > 0)
hasNextPage = pager[pager.length - 1].text.includes("Last")
return { list, hasNextPage } ? true
: false;
return { list, hasNextPage };
} }
async getPopular(page) { async getPopular(page) {
return await this.getListPage("/ComicList/MostPopular?", page) return await this.getListPage("/ComicList/MostPopular?", page);
} }
get supportsLatest() { get supportsLatest() {
throw new Error("supportsLatest not implemented"); throw new Error("supportsLatest not implemented");
} }
async getLatestUpdates(page) { async getLatestUpdates(page) {
return await this.getListPage("/ComicList/LatestUpdate?", page) return await this.getListPage("/ComicList/LatestUpdate?", page);
} }
async search(query, page, filters) { async search(query, page, filters) {
function getFilter(state) { function getFilter(state) {
var rd = "" var rd = "";
state.forEach(item => { state.forEach((item) => {
if (item.state) { if (item.state) {
rd += `${item.value},` rd += `${item.value},`;
} }
}) });
return rd.slice(0, -1) return rd.slice(0, -1);
} }
var isFiltersAvailable = !filters || filters.length != 0 var isFiltersAvailable = !filters || filters.length != 0;
var genre = isFiltersAvailable ? getFilter(filters[0].state): [] var genre = isFiltersAvailable ? getFilter(filters[0].state) : [];
var status = isFiltersAvailable ? filters[1].values[filters[1].state].value: "" var status = isFiltersAvailable
var year = isFiltersAvailable ? filters[2].values[filters[2].state].value: "" ? filters[1].values[filters[1].state].value
: "";
var year = isFiltersAvailable
? filters[2].values[filters[2].state].value
: "";
var slug = `/AdvanceSearch?comicName=${query}&ig=${encodeURIComponent(
genre
)}&status=${status}&pubDate=${year}&`;
var slug = `/AdvanceSearch?comicName=${query}&ig=${encodeURIComponent(genre)}&status=${status}&pubDate=${year}&` return await this.getListPage(slug, page);
return await this.getListPage(slug, page)
} }
async getDetail(url) { async getDetail(url) {
function statusCode(status) { function statusCode(status) {
return { return (
{
"Ongoing": 0, "Ongoing": 0,
"Completed": 1, "Completed": 1,
}[status] ?? 5; }[status] ?? 5
);
} }
var link = this.source.baseUrl + url
var doc = await this.request(url)
var detailsSection = doc.selectFirst(".barContent") var baseUrl = this.source.baseUrl;
var name = detailsSection.selectFirst("a").text if (url.includes(baseUrl)) url = url.replace(baseUrl, "");
var imageSlug = doc.selectFirst(".rightBox").selectFirst("img").getSrc
var imageUrl = imageSlug.includes("http") ? imageSlug : `${this.source.baseUrl}${imageSlug}`;
var pTag = detailsSection.select("p")
var description = pTag[pTag.length - 2].text var doc = await this.request(url);
var status = 5 var detailsSection = doc.selectFirst(".barContent");
var genre = [] var name = detailsSection.selectFirst("a").text;
var author = "" var imageSlug = doc.selectFirst(".rightBox").selectFirst("img").getSrc;
var artist = "" var imageUrl = imageSlug.includes("http")
? imageSlug
: `${this.source.baseUrl}${imageSlug}`;
var pTag = detailsSection.select("p");
pTag.forEach(p => { var description = pTag[pTag.length - 2].text;
var itemText = p.text.trim()
var status = 5;
var genre = [];
var author = "";
var artist = "";
pTag.forEach((p) => {
var itemText = p.text.trim();
if (itemText.includes("Genres")) { if (itemText.includes("Genres")) {
genre = itemText.replace("Genres:", "").trim().split(", ") genre = itemText.replace("Genres:", "").trim().split(", ");
} else if (itemText.includes("Status")) { } else if (itemText.includes("Status")) {
var sts = itemText.replace("Status: ", "").trim().split("\n")[0] var sts = itemText.replace("Status: ", "").trim().split("\n")[0];
status = statusCode(sts) status = statusCode(sts);
} else if (itemText.includes("Writer")) { } else if (itemText.includes("Writer")) {
author = itemText.replace("Writer: ", "") author = itemText.replace("Writer: ", "");
} else if (itemText.includes("Artist")) { } else if (itemText.includes("Artist")) {
artist = itemText.replace("Artist: ", "") artist = itemText.replace("Artist: ", "");
} }
});
var chapters = [];
var tr = doc.selectFirst("table").select("tr");
tr.splice(0, 2); // 1st item in the table is headers & 2nd item is a line break
tr.forEach((item) => {
var tds = item.select("td");
var aTag = tds[0].selectFirst("a");
var chapLink = aTag.getHref;
}) var chapTitle = aTag.text.trim().replace(`${name} `, "");
var chapters = [] chapTitle = chapTitle[0] == "_" ? chapTitle.substring(1) : chapTitle;
var tr = doc.selectFirst("table").select("tr")
tr.splice(0, 2) // 1st item in the table is headers & 2nd item is a line break
tr.forEach(item => {
var tds = item.select("td")
var aTag = tds[0].selectFirst("a")
var chapLink = aTag.getHref
var chapTitle = aTag.text.trim().replace(`${name} `, "") var uploadDate = tds[1].text.trim();
chapTitle = chapTitle[0] == "_" ? chapTitle.substring(1,) : chapTitle
var uploadDate = tds[1].text.trim()
var date = new Date(uploadDate); var date = new Date(uploadDate);
var dateUpload = date.getTime().toString(); var dateUpload = date.getTime().toString();
chapters.push({ url: chapLink, name: chapTitle, dateUpload });
});
var link = baseUrl + url;
chapters.push({ url: chapLink, name: chapTitle, dateUpload }) return {
name,
}) link,
imageUrl,
return { name, link, imageUrl, description, genre, status, author, artist, chapters } description,
genre,
status,
author,
artist,
chapters,
};
} }
// For manga chapter pages // For manga chapter pages
async getPageList(url) { async getPageList(url) {
var pages = []; var pages = [];
var hdr = this.getHeaders() var hdr = this.getHeaders();
let match; let match;
var imageQuality = this.getPreference("readcomiconline_page_quality"); var imageQuality = this.getPreference("readcomiconline_page_quality");
var doc = await this.request(url) var doc = await this.request(url);
var html = doc.html var html = doc.html;
// Find host url for images // Find host url for images
var baseUrlOverride = "" var baseUrlOverride = "";
const hostRegex = /return\s+baeu\s*\(\s*l\s*,\s*'([^']+?)'\s*\);?/g; const hostRegex = /return\s+baeu\s*\(\s*l\s*,\s*'([^']+?)'\s*\);?/g;
match = hostRegex.exec(html) match = hostRegex.exec(html);
if (match.length > 0) { if (match.length > 0) {
baseUrlOverride = match[1] baseUrlOverride = match[1];
if (baseUrlOverride.slice(-1) != "/") baseUrlOverride += "/" if (baseUrlOverride.slice(-1) != "/") baseUrlOverride += "/";
} }
const pageRegex = /pht\s*=\s*'([^']+?)';?/g; const pageRegex = /pht\s*=\s*'([^']+?)';?/g;
while ((match = pageRegex.exec(html)) !== null) { while ((match = pageRegex.exec(html)) !== null) {
var encodedImageUrl = match[1] var encodedImageUrl = match[1];
var decodedImageUrl = this.decodeImageUrl(encodedImageUrl, imageQuality, baseUrlOverride) var decodedImageUrl = this.decodeImageUrl(
encodedImageUrl,
imageQuality,
baseUrlOverride
);
pages.push({ pages.push({
url: decodedImageUrl, url: decodedImageUrl,
headers: hdr, headers: hdr,
}); });
} }
return pages; return pages;
} }
@@ -188,7 +218,7 @@ class DefaultExtension extends MProvider {
function formateState(type_name, items, values) { function formateState(type_name, items, values) {
var state = []; var state = [];
for (var i = 0; i < items.length; i++) { for (var i = 0; i < items.length; i++) {
state.push({ type_name: type_name, name: items[i], value: values[i] }) state.push({ type_name: type_name, name: items[i], value: values[i] });
} }
return state; return state;
} }
@@ -197,29 +227,115 @@ class DefaultExtension extends MProvider {
// Genre // Genre
var items = [ var items = [
"Action", "Adventure", "Anthology", "Anthropomorphic", "Biography", "Children", "Comedy", "Action",
"Crime", "Drama", "Family", "Fantasy", "Fighting", "Graphic Novels", "Historical", "Horror", "Adventure",
"Leading Ladies", "LGBTQ", "Literature", "Manga", "Martial Arts", "Mature", "Military", "Anthology",
"Mini-Series", "Movies & TV", "Music", "Mystery", "Mythology", "Personal", "Political", "Anthropomorphic",
"Post-Apocalyptic", "Psychological", "Pulp", "Religious", "Robots", "Romance", "School Life", "Biography",
"Sci-Fi", "Slice of Life", "Sport", "Spy", "Superhero", "Supernatural", "Suspense", "Teen", "Children",
"Thriller", "Vampires", "Video Games", "War", "Western", "Zombies" "Comedy",
"Crime",
"Drama",
"Family",
"Fantasy",
"Fighting",
"Graphic Novels",
"Historical",
"Horror",
"Leading Ladies",
"LGBTQ",
"Literature",
"Manga",
"Martial Arts",
"Mature",
"Military",
"Mini-Series",
"Movies & TV",
"Music",
"Mystery",
"Mythology",
"Personal",
"Political",
"Post-Apocalyptic",
"Psychological",
"Pulp",
"Religious",
"Robots",
"Romance",
"School Life",
"Sci-Fi",
"Slice of Life",
"Sport",
"Spy",
"Superhero",
"Supernatural",
"Suspense",
"Teen",
"Thriller",
"Vampires",
"Video Games",
"War",
"Western",
"Zombies",
]; ];
var values = [ var values = [
"1", "2", "38", "46", "41", "49", "3", "1",
"17", "19", "25", "20", "31", "5", "28", "15", "2",
"35", "51", "44", "40", "4", "8", "33", "38",
"56", "47", "55", "23", "21", "48", "42", "46",
"43", "27", "39", "53", "9", "32", "52", "41",
"16", "50", "54", "30", "22", "24", "29", "57", "49",
"18", "34", "37", "26", "45", "36" "3",
"17",
"19",
"25",
"20",
"31",
"5",
"28",
"15",
"35",
"51",
"44",
"40",
"4",
"8",
"33",
"56",
"47",
"55",
"23",
"21",
"48",
"42",
"43",
"27",
"39",
"53",
"9",
"32",
"52",
"16",
"50",
"54",
"30",
"22",
"24",
"29",
"57",
"18",
"34",
"37",
"26",
"45",
"36",
]; ];
filters.push({ filters.push({
type_name: "GroupFilter", type_name: "GroupFilter",
name: "Genres", name: "Genres",
state: formateState("CheckBox", items, values) state: formateState("CheckBox", items, values),
}) });
// Status // Status
items = ["Any", "Ongoing", "Completed"]; items = ["Any", "Ongoing", "Completed"];
@@ -228,20 +344,22 @@ class DefaultExtension extends MProvider {
type_name: "SelectFilter", type_name: "SelectFilter",
name: "Status", name: "Status",
state: 0, state: 0,
values: formateState("SelectOption", items, values) values: formateState("SelectOption", items, values),
}) });
// Years // Years
const currentYear = new Date().getFullYear(); const currentYear = new Date().getFullYear();
items = Array.from({ length: currentYear - 1919 }, (_, i) => (1920 + i).toString()).reverse() items = Array.from({ length: currentYear - 1919 }, (_, i) =>
items = ["All", ...items] (1920 + i).toString()
values = ["", ...items] ).reverse();
items = ["All", ...items];
values = ["", ...items];
filters.push({ filters.push({
type_name: "SelectFilter", type_name: "SelectFilter",
name: "Year", name: "Year",
state: 0, state: 0,
values: formateState("SelectOption", items, values) values: formateState("SelectOption", items, values),
}) });
return filters; return filters;
} }
@@ -250,29 +368,28 @@ class DefaultExtension extends MProvider {
{ {
key: "readcomiconline_page_quality", key: "readcomiconline_page_quality",
listPreference: { listPreference: {
title: 'Preferred image quality', title: "Preferred image quality",
summary: '', summary: "",
valueIndex: 2, valueIndex: 2,
entries: ["Low", "Medium", "High", "Highest"], entries: ["Low", "Medium", "High", "Highest"],
entryValues: ["l", "m", "h", "vh"] entryValues: ["l", "m", "h", "vh"],
}
}, },
] },
];
} }
// -------- ReadComicOnline Image Decoder -------- // -------- ReadComicOnline Image Decoder --------
// Source:- https://readcomiconline.li/Scripts/rguard.min.js // Source:- https://readcomiconline.li/Scripts/rguard.min.js
base64UrlDecode(input) { base64UrlDecode(input) {
let base64 = input let base64 = input.replace(/-/g, "+").replace(/_/g, "/");
.replace(/-/g, "+")
.replace(/_/g, "/");
while (base64.length % 4 !== 0) { while (base64.length % 4 !== 0) {
base64 += "="; base64 += "=";
} }
const base64abc = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; const base64abc =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
const outputBytes = []; const outputBytes = [];
for (let i = 0; i < base64.length; i += 4) { for (let i = 0; i < base64.length; i += 4) {
@@ -283,23 +400,25 @@ class DefaultExtension extends MProvider {
const triplet = (c1 << 18) | (c2 << 12) | ((c3 & 63) << 6) | (c4 & 63); const triplet = (c1 << 18) | (c2 << 12) | ((c3 & 63) << 6) | (c4 & 63);
outputBytes.push((triplet >> 16) & 0xFF); outputBytes.push((triplet >> 16) & 0xff);
if (base64[i + 2] !== "=") outputBytes.push((triplet >> 8) & 0xFF); if (base64[i + 2] !== "=") outputBytes.push((triplet >> 8) & 0xff);
if (base64[i + 3] !== "=") outputBytes.push(triplet & 0xFF); if (base64[i + 3] !== "=") outputBytes.push(triplet & 0xff);
} }
// Convert bytes to ISO-8859-1 string // Convert bytes to ISO-8859-1 string
return String.fromCharCode(...outputBytes); return String.fromCharCode(...outputBytes);
} }
extractBeforeDecode(url) { extractBeforeDecode(url) {
return url.substring(15, 33) + url.substring(50); return url.substring(15, 33) + url.substring(50);
} }
finalizeDecodedString(decoded) { finalizeDecodedString(decoded) {
return decoded.substring(0, decoded.length - 11) + decoded[decoded.length - 2] + decoded[decoded.length - 1]; return (
decoded.substring(0, decoded.length - 11) +
decoded[decoded.length - 2] +
decoded[decoded.length - 1]
);
} }
decoderFunction(encodedUrl) { decoderFunction(encodedUrl) {
@@ -307,34 +426,34 @@ class DefaultExtension extends MProvider {
decodedUrl = this.finalizeDecodedString(decodedUrl); decodedUrl = this.finalizeDecodedString(decodedUrl);
decodedUrl = decodeURIComponent(this.base64UrlDecode(decodedUrl)); decodedUrl = decodeURIComponent(this.base64UrlDecode(decodedUrl));
decodedUrl = decodedUrl.substring(0, 13) + decodedUrl.substring(17); decodedUrl = decodedUrl.substring(0, 13) + decodedUrl.substring(17);
return decodedUrl.slice(0, -2) + "=s1600" return decodedUrl.slice(0, -2) + "=s1600";
} }
decodeImageUrl(encodedImageUrl, imageQuality, baseUrlOverride) { decodeImageUrl(encodedImageUrl, imageQuality, baseUrlOverride) {
// Default image qualities // Default image qualities
var IMAGEQUALITY = [ var IMAGEQUALITY = [
{ "l": "900", "m": "0", "h": "1600", "vh": "2041" }, { "l": "900", "m": "0", "h": "1600", "vh": "2041" },
{ "l": "900", "m": "1600", "h": "2041", "vh": "0" } { "l": "900", "m": "1600", "h": "2041", "vh": "0" },
] ];
let finalUrl; let finalUrl;
var qType = 0 var qType = 0;
// Check if the url starts with https, if not then decode the url // Check if the url starts with https, if not then decode the url
if (!encodedImageUrl.startsWith("https")) { if (!encodedImageUrl.startsWith("https")) {
encodedImageUrl = encodedImageUrl encodedImageUrl = encodedImageUrl
.replace(/6UUQS__ACd__/g, 'b') .replace(/6UUQS__ACd__/g, "b")
.replace(/pw_.g28x/g, "b") .replace(/pw_.g28x/g, "b");
var encodedUrl = encodedImageUrl.split("=s")[0] var encodedUrl = encodedImageUrl.split("=s")[0];
var decodedUrl = this.decoderFunction(encodedUrl); var decodedUrl = this.decoderFunction(encodedUrl);
var queryParams = encodedImageUrl.substring(encodedImageUrl.indexOf("?")); var queryParams = encodedImageUrl.substring(encodedImageUrl.indexOf("?"));
finalUrl = baseUrlOverride + decodedUrl + queryParams finalUrl = baseUrlOverride + decodedUrl + queryParams;
} else { } else {
// If the url starts with https, then just override the base url // If the url starts with https, then just override the base url
qType = 1 qType = 1;
finalUrl = baseUrlOverride + encodedImageUrl.split(".com/")[1] finalUrl = baseUrlOverride + encodedImageUrl.split(".com/")[1];
} }
return finalUrl.replace("s1600", `s${IMAGEQUALITY[qType][imageQuality]}`) return finalUrl.replace("s1600", `s${IMAGEQUALITY[qType][imageQuality]}`);
} }
} }