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

View File

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

View File

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

View File

@@ -1,17 +1,19 @@
const mangayomiSources = [{
const mangayomiSources = [
{
"name": "AnimeParadise",
"lang": "en",
"baseUrl": "https://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",
"itemType": 1,
"version": "0.0.1",
"version": "0.0.2",
"pkgPath": "anime/src/en/animeparadise.js"
}];
}
];
class DefaultExtension extends MProvider {
getPreference(key) {
const preferences = new SharedPreferences();
return preferences.get(key);
@@ -20,12 +22,12 @@ class DefaultExtension extends MProvider {
async extractFromUrl(url) {
var res = await new Client().get(this.source.baseUrl + url);
var doc = new Document(res.body);
var jsonData = doc.selectFirst("#__NEXT_DATA__").text
return JSON.parse(jsonData).props.pageProps
var jsonData = doc.selectFirst("#__NEXT_DATA__").text;
return JSON.parse(jsonData).props.pageProps;
}
async requestAPI(slug) {
var api = `${this.source.apiUrl}/${slug}`
var api = `${this.source.apiUrl}/${slug}`;
var response = await new Client().get(api);
var body = JSON.parse(response.body);
return body;
@@ -35,89 +37,91 @@ class DefaultExtension extends MProvider {
var jsonData = await this.requestAPI(slug);
var list = [];
if ("episodes" in jsonData) {
jsonData.episodes.forEach(item => {
jsonData.episodes.forEach((item) => {
list.push({
"name": item.origin.title,
"link": item.origin.link,
"imageUrl": item.image
"imageUrl": item.image,
});
});
})
} else {
jsonData.data.forEach(item => {
jsonData.data.forEach((item) => {
list.push({
"name": item.title,
"link": item.link,
"imageUrl": item.posterImage.original
"imageUrl": item.posterImage.original,
});
});
})
}
return {
"list": list,
"hasNextPage": false
}
"hasNextPage": false,
};
}
async getPopular(page) {
return await this.formList('?sort={"rate": -1 }')
return await this.formList('?sort={"rate": -1 }');
}
async getLatestUpdates(page) {
var slug = '?sort={"postDate": -1 }';
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) {
var season = filters[0].values[filters[0].state].value
var year = filters[1].values[filters[1].state].value
var season = filters[0].values[filters[0].state].value;
var year = filters[1].values[filters[1].state].value;
var genre = "genre[]="
var genre = "genre[]=";
for (var filter of filters[2].state) {
if (filter.state == true)
genre += `${filter.value}&genre[]=`
if (filter.state == true) 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);
}
statusCode(status) {
return {
return (
{
"current": 0,
"finished": 1,
}[status] ?? 5;
}[status] ?? 5
);
}
async getDetail(url) {
var link = this.source.baseUrl + `/anime/${url}`
var jsonData = await this.extractFromUrl(`/anime/${url}`)
jsonData = jsonData.data
var details = {}
var chapters = []
details.imageUrl = jsonData.posterImage.original
details.description = jsonData.synopsys
details.genre = jsonData.genres
details.status = this.statusCode(jsonData.status)
var id = jsonData._id
var epAPI = await this.requestAPI(`anime/${id}/episode`)
epAPI.data.forEach(ep => {
var linkSlug = this.source.baseUrl + `/anime/`;
if (url.includes(linkSlug)) url = url.replace(linkSlug, "");
var jsonData = await this.extractFromUrl(`/anime/${url}`);
jsonData = jsonData.data;
var details = {};
var chapters = [];
details.imageUrl = jsonData.posterImage.original;
details.description = jsonData.synopsys;
details.genre = jsonData.genres;
details.status = this.statusCode(jsonData.status);
var id = jsonData._id;
var epAPI = await this.requestAPI(`anime/${id}/episode`);
epAPI.data.forEach((ep) => {
var epName = `E${ep.number}: ${ep.title}`;
var epUrl = `${ep.uid}?origin=${ep.origin}`
chapters.push({ name: epName, url: epUrl })
})
var epUrl = `${ep.uid}?origin=${ep.origin}`;
chapters.push({ name: epName, url: epUrl });
});
details.link = `${linkSlug}${url}`;
details.chapters = chapters.reverse();
return details;
}
// Sorts streams based on user preference.
async sortStreams(streams) {
var sortedStreams = [];
var copyStreams = streams.slice()
var copyStreams = streams.slice();
var pref = await this.getPreference("animeparadise_pref_video_resolution");
for (var stream of streams) {
if (stream.quality.indexOf(pref) > -1) {
sortedStreams.push(stream);
var index = copyStreams.indexOf(stream);
@@ -127,115 +131,140 @@ class DefaultExtension extends MProvider {
break;
}
}
return [...sortedStreams, ...copyStreams]
return [...sortedStreams, ...copyStreams];
}
// Extracts the streams url for different resolutions from a hls stream.
async extractStreams(url) {
const response = await new Client().get(url);
const body = response.body;
const lines = body.split('\n');
var streams = [{
const lines = body.split("\n");
var streams = [
{
url: url,
originalUrl: url,
quality: `Auto`,
}];
},
];
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 m3u8Url = lines[i + 1].trim();
m3u8Url = url.replace("master.m3u8", m3u8Url)
m3u8Url = url.replace("master.m3u8", m3u8Url);
streams.push({
url: m3u8Url,
originalUrl: m3u8Url,
quality: resolution
quality: resolution,
});
}
}
return streams
return streams;
}
// For anime episode video list
async getVideoList(url) {
var streams = []
var streams = [];
var jsonData = await this.extractFromUrl(`/watch/${url}`);
var epData = jsonData.episode
streams = await this.extractStreams(epData.streamLink)
var epData = jsonData.episode;
streams = await this.extractStreams(epData.streamLink);
var subtitles = []
epData.subData.forEach(sub => {
var subtitles = [];
epData.subData.forEach((sub) => {
subtitles.push({
"label": sub.label,
"file": `${this.source.apiUrl}/stream/file/${sub.src}`,
});
})
});
streams[0].subtitles = subtitles
return streams
streams[0].subtitles = subtitles;
return streams;
}
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({
type_name: typ,
name: 'All',
value: ''
})
return arr
name: "All",
value: "",
});
return arr;
}
getFilterList() {
var seasons = ["Winter", "Spring", "Summer", "Fall"]
var seasons = ["Winter", "Spring", "Summer", "Fall"];
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 [
{
type_name: "SelectFilter",
name: "Season",
state: 0,
values: this.addCatogory(seasons, "SelectOption")
}, {
values: this.addCatogory(seasons, "SelectOption"),
},
{
type_name: "SelectFilter",
name: "Year",
state: 0,
values: this.addCatogory(years, "SelectOption")
}, {
values: this.addCatogory(years, "SelectOption"),
},
{
type_name: "GroupFilter",
name: "Genres",
state: genres
}
]
state: genres,
},
];
}
getSourcePreferences() {
return [{
key: 'animeparadise_pref_latest_tab',
return [
{
key: "animeparadise_pref_latest_tab",
listPreference: {
title: 'Latest tab category',
summary: 'Anime list to be shown in latest tab',
title: "Latest tab category",
summary: "Anime list to be shown in latest tab",
valueIndex: 0,
entries: ["Recently added anime", "Recently added episode"],
entryValues: ["recent_ani", "recent_ep"]
}
}, {
key: 'animeparadise_pref_video_resolution',
entryValues: ["recent_ani", "recent_ep"],
},
},
{
key: "animeparadise_pref_video_resolution",
listPreference: {
title: 'Preferred video resolution',
summary: '',
title: "Preferred video resolution",
summary: "",
valueIndex: 0,
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",
"lang": "en",
"baseUrl": "https://animez.org",
"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",
"itemType": 1,
"version": "1.0.1",
"version": "1.0.2",
"pkgPath": "anime/src/en/animez.js"
}];
}
];
class DefaultExtension extends MProvider {
constructor() {
@@ -19,7 +22,7 @@ class DefaultExtension extends MProvider {
getHeaders(url) {
return {
"Referer": this.source.baseUrl,
}
};
}
getPreference(key) {
@@ -27,161 +30,168 @@ class DefaultExtension extends MProvider {
}
async request(slug) {
var url = this.source.baseUrl + slug
var url = this.source.baseUrl + slug;
var res = await this.client.get(url, this.getHeaders());
return new Document(res.body);
}
async page(slug) {
var body = await this.request(slug)
var list = []
var body = await this.request(slug);
var list = [];
var hasNextPage = false;
var animes = body.select("li.TPostMv")
animes.forEach(anime => {
var link = anime.selectFirst("a").getHref
var name = anime.selectFirst('h2.Title').text;
var imageUrl = this.source.baseUrl +"/"+ anime.selectFirst('img').getSrc;
var animes = body.select("li.TPostMv");
animes.forEach((anime) => {
var link = anime.selectFirst("a").getHref;
var name = anime.selectFirst("h2.Title").text;
var imageUrl =
this.source.baseUrl + "/" + anime.selectFirst("img").getSrc;
list.push({ name, link, imageUrl });
});
var paginations = body.select(".pagination > li")
hasNextPage = paginations[paginations.length - 1].text == "Last" ? true : false
var paginations = body.select(".pagination > li");
hasNextPage =
paginations[paginations.length - 1].text == "Last" ? true : false;
return { list, hasNextPage }
return { list, hasNextPage };
}
sortByPref(key) {
var sort = parseInt(this.getPreference(key))
var sortBy = "hot"
var sort = parseInt(this.getPreference(key));
var sortBy = "hot";
switch (sort) {
case 1: {
sortBy = "lastest-chap"
sortBy = "lastest-chap";
break;
} case 2: {
sortBy = "hot"
}
case 2: {
sortBy = "hot";
break;
}
case 3: {
sortBy = "lastest-manga"
sortBy = "lastest-manga";
break;
}
case 4: {
sortBy = "top-manga"
sortBy = "top-manga";
break;
}
case 5: {
sortBy = "top-month"
sortBy = "top-month";
break;
}
case 6: {
sortBy = "top-week"
sortBy = "top-week";
break;
}
case 7: {
sortBy = "top-day"
sortBy = "top-day";
break;
}
case 8: {
sortBy = "follow"
sortBy = "follow";
break;
}
case 9: {
sortBy = "comment"
sortBy = "comment";
break;
}
case 10: {
sortBy = "num-chap"
sortBy = "num-chap";
break;
}
}
return sortBy;
}
async getPopular(page) {
var sortBy = this.sortByPref("animez_pref_popular_section")
var slug = `/?act=search&f[status]=all&f[sortby]=${sortBy}&&pageNum=${page}`
return await this.page(slug)
var sortBy = this.sortByPref("animez_pref_popular_section");
var slug = `/?act=search&f[status]=all&f[sortby]=${sortBy}&&pageNum=${page}`;
return await this.page(slug);
}
get supportsLatest() {
throw new Error("supportsLatest not implemented");
}
async getLatestUpdates(page) {
var sortBy = this.sortByPref("animez_pref_latest_section")
var slug = `/?act=search&f[status]=all&f[sortby]=${sortBy}&&pageNum=${page}`
return await this.page(slug)
var sortBy = this.sortByPref("animez_pref_latest_section");
var slug = `/?act=search&f[status]=all&f[sortby]=${sortBy}&&pageNum=${page}`;
return await this.page(slug);
}
async search(query, page, filters) {
var slug = `/?act=search&f[status]=all&f[keyword]=${query}&&pageNum=${page}`
return await this.page(slug)
var slug = `/?act=search&f[status]=all&f[keyword]=${query}&&pageNum=${page}`;
return await this.page(slug);
}
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 name = body.selectFirst("#title-detail-manga").text
var animeId = body.selectFirst("#title-detail-manga").attr("data-manga")
var genre = []
body.select("li.AAIco-adjust")[3].select("a").forEach(g => genre.push(g.text))
var description = body.selectFirst("#summary_shortened").text
var name = body.selectFirst("#title-detail-manga").text;
var animeId = body.selectFirst("#title-detail-manga").attr("data-manga");
var genre = [];
body
.select("li.AAIco-adjust")[3]
.select("a")
.forEach((g) => genre.push(g.text));
var description = body.selectFirst("#summary_shortened").text;
var chapters = []
var chapLen = 0
var pageNum = 1
var chapters = [];
var chapLen = 0;
var pageNum = 1;
var hasNextPage = true;
while(hasNextPage) {
var pageSlug = `?act=ajax&code=load_list_chapter&manga_id=${animeId}&page_num=${pageNum}&chap_id=0&keyword=`
while (hasNextPage) {
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 parsedBody = JSON.parse(pageBody.html);
var nav = parsedBody.nav
if(nav==null){ // if "nav" doesnt exists there is no next page
var nav = parsedBody.nav;
if (nav == null) {
// if "nav" doesnt exists there is no next page
hasNextPage = false;
}else{
var navLi = new Document(nav).select(".page-link.next").length
if(navLi>0){ // if "nav" exists and has li.next then there is next page
} else {
var navLi = new Document(nav).select(".page-link.next").length;
if (navLi > 0) {
// if "nav" exists and has li.next then there is next page
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;
}
}
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")
var title = a.text
var epLink = a.getHref
var scanlator = "Sub"
if(title.indexOf("Dub")>0){
title = title.replace("-Dub","")
scanlator = "Dub"
list_chap.forEach((chapter) => {
var a = chapter.selectFirst("a");
var title = a.text;
var epLink = a.getHref;
var scanlator = "Sub";
if (title.indexOf("Dub") > 0) {
title = title.replace("-Dub", "");
scanlator = "Dub";
}
title = title.indexOf("Movie") > -1? title : `Episode ${title}`
title = title.indexOf("Movie") > -1 ? title : `Episode ${title}`;
var epData = {
name:title,
url:epLink,
scanlator
}
if(chapLen>0){
var pos = chapLen -1
var lastEntry = chapters[pos]
if(lastEntry.name == epData.name){ // if last entries name is same then append url and scanlator to last entry
chapters.pop() // remove the last entry
epData.url = `${epData.url}||${lastEntry.url}`
epData.scanlator = `${lastEntry.scanlator}, ${epData.scanlator}`
name: title,
url: epLink,
scanlator,
};
if (chapLen > 0) {
var pos = chapLen - 1;
var lastEntry = chapters[pos];
if (lastEntry.name == epData.name) {
// if last entries name is same then append url and scanlator to last entry
chapters.pop(); // remove the last entry
epData.url = `${epData.url}||${lastEntry.url}`;
epData.scanlator = `${lastEntry.scanlator}, ${epData.scanlator}`;
chapLen = pos; // since the last entry is removed the chapLen will decrease
}
}
chapters.push(epData)
chapters.push(epData);
chapLen++;
})
});
}
return {
@@ -196,7 +206,7 @@ class DefaultExtension extends MProvider {
sortStreams(streams) {
var sortedStreams = [];
var copyStreams = streams.slice()
var copyStreams = streams.slice();
var pref = this.getPreference("animez_pref_stream_audio");
for (var stream of streams) {
if (stream.quality.indexOf(pref) > -1) {
@@ -208,60 +218,83 @@ class DefaultExtension extends MProvider {
break;
}
}
return [...sortedStreams, ...copyStreams]
return [...sortedStreams, ...copyStreams];
}
// For anime episode video list
async getVideoList(url) {
var linkSlugs = url.split("||")
var linkSlugs = url.split("||");
var streams = [];
for(var slug of linkSlugs){
var body = await this.request(slug)
var iframeSrc = body.selectFirst("iframe").getSrc
var streamLink = iframeSrc.replace("/embed/","/anime/")
var audio = slug.indexOf("dub-") > -1 ? "Dub" : "Sub"
for (var slug of linkSlugs) {
var body = await this.request(slug);
var iframeSrc = body.selectFirst("iframe").getSrc;
var streamLink = iframeSrc.replace("/embed/", "/anime/");
var audio = slug.indexOf("dub-") > -1 ? "Dub" : "Sub";
streams.push({
url: streamLink,
originalUrl: streamLink,
quality: audio,
})
});
}
return sortStreams(streams);
}
getSourcePreferences() {
return [{
key: 'animez_pref_popular_section',
return [
{
key: "animez_pref_popular_section",
listPreference: {
title: 'Preferred popular content',
summary: '',
title: "Preferred popular content",
summary: "",
valueIndex: 1,
entries: ["Latest update", "Hot", "New releases", "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',
entries: [
"Latest update",
"Hot",
"New releases",
"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: {
title: 'Preferred latest content',
summary: '',
title: "Preferred latest content",
summary: "",
valueIndex: 0,
entries: ["Latest update", "Hot", "New releases", "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',
entries: [
"Latest update",
"Hot",
"New releases",
"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: {
title: 'Preferred stream audio',
summary: '',
title: "Preferred stream audio",
summary: "",
valueIndex: 0,
entries: ["Sub","Dub"],
entryValues: ["Sub","Dub"],
}
},]
entries: ["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/",
"typeSource": "multi",
"itemType": 1,
"version": "0.0.5",
"version": "0.0.6",
"pkgPath": "anime/src/en/gojo.js"
}];
@@ -103,6 +103,9 @@ class DefaultExtension extends MProvider {
async getDetail(url) {
var linkSlug = `${this.source.baseUrl}/watch/`
if (url.includes(linkSlug)) url = url.replace(linkSlug, "");
var anilistId = url
var res = await this.gojoAPI(`/info/${anilistId}`)
if (res == null) {
@@ -111,7 +114,7 @@ class DefaultExtension extends MProvider {
var name = this.getTitle(res.title)
var imageUrl = res.coverImage.large
var description = res.description;
var link = `${this.source.baseUrl}/watch/${anilistId}`
var link = `${linkSlug}${anilistId}`
var genres = res.genres
var status = (() => {
switch (res.status) {

View File

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

View File

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

View File

@@ -1,14 +1,17 @@
const mangayomiSources = [{
const mangayomiSources = [
{
"name": "ReadComicOnline",
"lang": "en",
"baseUrl": "https://readcomiconline.li",
"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",
"itemType": 0,
"version": "0.1.2",
"version": "0.1.3",
"pkgPath": "manga/src/en/readcomiconline.js"
}];
}
];
class DefaultExtension extends MProvider {
constructor() {
@@ -22,165 +25,192 @@ class DefaultExtension extends MProvider {
getHeaders() {
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,
"Origin": this.source.baseUrl,
}
};
}
async request(slug) {
var url = slug
var baseUrl = this.source.baseUrl
var url = slug;
var baseUrl = this.source.baseUrl;
if (!slug.includes(baseUrl)) url = baseUrl + slug;
var res = await this.client.get(url, this.getHeaders());
return new Document(res.body);
}
async getListPage(slug, page) {
var url = `${slug}page=${page}`
var url = `${slug}page=${page}`;
var doc = await this.request(url);
var baseUrl = this.source.baseUrl
var list = []
var baseUrl = this.source.baseUrl;
var list = [];
var comicList = doc.select(".list-comic > .item")
comicList.forEach(item => {
var comicList = doc.select(".list-comic > .item");
comicList.forEach((item) => {
var name = item.selectFirst(".title").text;
var link = item.selectFirst("a").getHref
var imageSlug = item.selectFirst("img").getSrc
var imageUrl = imageSlug.includes("http") ? imageSlug : `${baseUrl}${imageSlug}`;
var link = item.selectFirst("a").getHref;
var imageSlug = item.selectFirst("img").getSrc;
var imageUrl = imageSlug.includes("http")
? imageSlug
: `${baseUrl}${imageSlug}`;
list.push({ name, link, imageUrl });
});
var pager = doc.select("ul.pager > li")
var pager = doc.select("ul.pager > li");
var hasNextPage = false
if (pager.length > 0) hasNextPage = pager[pager.length - 1].text.includes("Last") ? true : false;
return { list, hasNextPage }
var hasNextPage = false;
if (pager.length > 0)
hasNextPage = pager[pager.length - 1].text.includes("Last")
? true
: false;
return { list, hasNextPage };
}
async getPopular(page) {
return await this.getListPage("/ComicList/MostPopular?", page)
return await this.getListPage("/ComicList/MostPopular?", page);
}
get supportsLatest() {
throw new Error("supportsLatest not implemented");
}
async getLatestUpdates(page) {
return await this.getListPage("/ComicList/LatestUpdate?", page)
return await this.getListPage("/ComicList/LatestUpdate?", page);
}
async search(query, page, filters) {
function getFilter(state) {
var rd = ""
state.forEach(item => {
var rd = "";
state.forEach((item) => {
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 genre = isFiltersAvailable ? getFilter(filters[0].state): []
var status = isFiltersAvailable ? filters[1].values[filters[1].state].value: ""
var year = isFiltersAvailable ? filters[2].values[filters[2].state].value: ""
var isFiltersAvailable = !filters || filters.length != 0;
var genre = isFiltersAvailable ? getFilter(filters[0].state) : [];
var status = isFiltersAvailable
? 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) {
function statusCode(status) {
return {
return (
{
"Ongoing": 0,
"Completed": 1,
}[status] ?? 5;
}[status] ?? 5
);
}
var link = this.source.baseUrl + url
var doc = await this.request(url)
var detailsSection = doc.selectFirst(".barContent")
var name = detailsSection.selectFirst("a").text
var imageSlug = doc.selectFirst(".rightBox").selectFirst("img").getSrc
var imageUrl = imageSlug.includes("http") ? imageSlug : `${this.source.baseUrl}${imageSlug}`;
var pTag = detailsSection.select("p")
var baseUrl = this.source.baseUrl;
if (url.includes(baseUrl)) url = url.replace(baseUrl, "");
var description = pTag[pTag.length - 2].text
var doc = await this.request(url);
var status = 5
var genre = []
var author = ""
var artist = ""
var detailsSection = doc.selectFirst(".barContent");
var name = detailsSection.selectFirst("a").text;
var imageSlug = doc.selectFirst(".rightBox").selectFirst("img").getSrc;
var imageUrl = imageSlug.includes("http")
? imageSlug
: `${this.source.baseUrl}${imageSlug}`;
var pTag = detailsSection.select("p");
pTag.forEach(p => {
var itemText = p.text.trim()
var description = pTag[pTag.length - 2].text;
var status = 5;
var genre = [];
var author = "";
var artist = "";
pTag.forEach((p) => {
var itemText = p.text.trim();
if (itemText.includes("Genres")) {
genre = itemText.replace("Genres:", "").trim().split(", ")
genre = itemText.replace("Genres:", "").trim().split(", ");
} else if (itemText.includes("Status")) {
var sts = itemText.replace("Status: ", "").trim().split("\n")[0]
status = statusCode(sts)
var sts = itemText.replace("Status: ", "").trim().split("\n")[0];
status = statusCode(sts);
} else if (itemText.includes("Writer")) {
author = itemText.replace("Writer: ", "")
author = itemText.replace("Writer: ", "");
} 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 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} `, "");
chapTitle = chapTitle[0] == "_" ? chapTitle.substring(1) : chapTitle;
var chapTitle = aTag.text.trim().replace(`${name} `, "")
chapTitle = chapTitle[0] == "_" ? chapTitle.substring(1,) : chapTitle
var uploadDate = tds[1].text.trim()
var uploadDate = tds[1].text.trim();
var date = new Date(uploadDate);
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, description, genre, status, author, artist, chapters }
return {
name,
link,
imageUrl,
description,
genre,
status,
author,
artist,
chapters,
};
}
// For manga chapter pages
async getPageList(url) {
var pages = [];
var hdr = this.getHeaders()
var hdr = this.getHeaders();
let match;
var imageQuality = this.getPreference("readcomiconline_page_quality");
var doc = await this.request(url)
var html = doc.html
var doc = await this.request(url);
var html = doc.html;
// Find host url for images
var baseUrlOverride = ""
var baseUrlOverride = "";
const hostRegex = /return\s+baeu\s*\(\s*l\s*,\s*'([^']+?)'\s*\);?/g;
match = hostRegex.exec(html)
match = hostRegex.exec(html);
if (match.length > 0) {
baseUrlOverride = match[1]
if (baseUrlOverride.slice(-1) != "/") baseUrlOverride += "/"
baseUrlOverride = match[1];
if (baseUrlOverride.slice(-1) != "/") baseUrlOverride += "/";
}
const pageRegex = /pht\s*=\s*'([^']+?)';?/g;
while ((match = pageRegex.exec(html)) !== null) {
var encodedImageUrl = match[1]
var decodedImageUrl = this.decodeImageUrl(encodedImageUrl, imageQuality, baseUrlOverride)
var encodedImageUrl = match[1];
var decodedImageUrl = this.decodeImageUrl(
encodedImageUrl,
imageQuality,
baseUrlOverride
);
pages.push({
url: decodedImageUrl,
headers: hdr,
});
}
return pages;
}
@@ -188,7 +218,7 @@ class DefaultExtension extends MProvider {
function formateState(type_name, items, values) {
var state = [];
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;
}
@@ -197,29 +227,115 @@ class DefaultExtension extends MProvider {
// Genre
var items = [
"Action", "Adventure", "Anthology", "Anthropomorphic", "Biography", "Children", "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"
"Action",
"Adventure",
"Anthology",
"Anthropomorphic",
"Biography",
"Children",
"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 = [
"1", "2", "38", "46", "41", "49", "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"
"1",
"2",
"38",
"46",
"41",
"49",
"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({
type_name: "GroupFilter",
name: "Genres",
state: formateState("CheckBox", items, values)
})
state: formateState("CheckBox", items, values),
});
// Status
items = ["Any", "Ongoing", "Completed"];
@@ -228,20 +344,22 @@ class DefaultExtension extends MProvider {
type_name: "SelectFilter",
name: "Status",
state: 0,
values: formateState("SelectOption", items, values)
})
values: formateState("SelectOption", items, values),
});
// Years
const currentYear = new Date().getFullYear();
items = Array.from({ length: currentYear - 1919 }, (_, i) => (1920 + i).toString()).reverse()
items = ["All", ...items]
values = ["", ...items]
items = Array.from({ length: currentYear - 1919 }, (_, i) =>
(1920 + i).toString()
).reverse();
items = ["All", ...items];
values = ["", ...items];
filters.push({
type_name: "SelectFilter",
name: "Year",
state: 0,
values: formateState("SelectOption", items, values)
})
values: formateState("SelectOption", items, values),
});
return filters;
}
@@ -250,29 +368,28 @@ class DefaultExtension extends MProvider {
{
key: "readcomiconline_page_quality",
listPreference: {
title: 'Preferred image quality',
summary: '',
title: "Preferred image quality",
summary: "",
valueIndex: 2,
entries: ["Low", "Medium", "High", "Highest"],
entryValues: ["l", "m", "h", "vh"]
}
entryValues: ["l", "m", "h", "vh"],
},
]
},
];
}
// -------- ReadComicOnline Image Decoder --------
// Source:- https://readcomiconline.li/Scripts/rguard.min.js
base64UrlDecode(input) {
let base64 = input
.replace(/-/g, "+")
.replace(/_/g, "/");
let base64 = input.replace(/-/g, "+").replace(/_/g, "/");
while (base64.length % 4 !== 0) {
base64 += "=";
}
const base64abc = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
const base64abc =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
const outputBytes = [];
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);
outputBytes.push((triplet >> 16) & 0xFF);
if (base64[i + 2] !== "=") outputBytes.push((triplet >> 8) & 0xFF);
if (base64[i + 3] !== "=") outputBytes.push(triplet & 0xFF);
outputBytes.push((triplet >> 16) & 0xff);
if (base64[i + 2] !== "=") outputBytes.push((triplet >> 8) & 0xff);
if (base64[i + 3] !== "=") outputBytes.push(triplet & 0xff);
}
// Convert bytes to ISO-8859-1 string
return String.fromCharCode(...outputBytes);
}
extractBeforeDecode(url) {
return url.substring(15, 33) + url.substring(50);
}
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) {
@@ -307,34 +426,34 @@ class DefaultExtension extends MProvider {
decodedUrl = this.finalizeDecodedString(decodedUrl);
decodedUrl = decodeURIComponent(this.base64UrlDecode(decodedUrl));
decodedUrl = decodedUrl.substring(0, 13) + decodedUrl.substring(17);
return decodedUrl.slice(0, -2) + "=s1600"
return decodedUrl.slice(0, -2) + "=s1600";
}
decodeImageUrl(encodedImageUrl, imageQuality, baseUrlOverride) {
// Default image qualities
var IMAGEQUALITY = [
{ "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;
var qType = 0
var qType = 0;
// Check if the url starts with https, if not then decode the url
if (!encodedImageUrl.startsWith("https")) {
encodedImageUrl = encodedImageUrl
.replace(/6UUQS__ACd__/g, 'b')
.replace(/pw_.g28x/g, "b")
.replace(/6UUQS__ACd__/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 queryParams = encodedImageUrl.substring(encodedImageUrl.indexOf("?"));
finalUrl = baseUrlOverride + decodedUrl + queryParams
finalUrl = baseUrlOverride + decodedUrl + queryParams;
} else {
// If the url starts with https, then just override the base url
qType = 1
finalUrl = baseUrlOverride + encodedImageUrl.split(".com/")[1]
qType = 1;
finalUrl = baseUrlOverride + encodedImageUrl.split(".com/")[1];
}
return finalUrl.replace("s1600", `s${IMAGEQUALITY[qType][imageQuality]}`)
return finalUrl.replace("s1600", `s${IMAGEQUALITY[qType][imageQuality]}`);
}
}