mirror of
https://github.com/kodjodevf/mangayomi-extensions.git
synced 2026-02-14 10:51:17 +00:00
Merge branch 'main' into feature/light-novel
This commit is contained in:
@@ -2,7 +2,7 @@ import '../../../../model/source.dart';
|
|||||||
import 'src/hianime/hianime.dart';
|
import 'src/hianime/hianime.dart';
|
||||||
import 'src/kaido/kaido.dart';
|
import 'src/kaido/kaido.dart';
|
||||||
|
|
||||||
const _zorothemeVersion = "0.0.95";
|
const _zorothemeVersion = "0.1.0";
|
||||||
const _zorothemeSourceCodeUrl =
|
const _zorothemeSourceCodeUrl =
|
||||||
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/multisrc/zorotheme/zorotheme.dart";
|
"https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/$branchName/dart/anime/multisrc/zorotheme/zorotheme.dart";
|
||||||
|
|
||||||
@@ -15,5 +15,6 @@ List<Source> _zorothemeSourcesList = [
|
|||||||
]
|
]
|
||||||
.map((e) => e
|
.map((e) => e
|
||||||
..sourceCodeUrl = _zorothemeSourceCodeUrl
|
..sourceCodeUrl = _zorothemeSourceCodeUrl
|
||||||
|
..appMinVerReq = "0.4.0"
|
||||||
..version = _zorothemeVersion)
|
..version = _zorothemeVersion)
|
||||||
.toList();
|
.toList();
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,12 +1,12 @@
|
|||||||
const mangayomiSources = [{
|
const mangayomiSources = [{
|
||||||
"name": "Autoembed",
|
"name": "Autoembed",
|
||||||
"lang": "all",
|
"lang": "all",
|
||||||
"baseUrl": "https://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,
|
||||||
"version": "1.0.1",
|
"version": "1.1.4",
|
||||||
"dateFormat": "",
|
"dateFormat": "",
|
||||||
"dateFormatLocale": "",
|
"dateFormatLocale": "",
|
||||||
"pkgPath": "anime/src/all/autoembed.js"
|
"pkgPath": "anime/src/all/autoembed.js"
|
||||||
@@ -14,7 +14,7 @@ const mangayomiSources = [{
|
|||||||
|
|
||||||
class DefaultExtension extends MProvider {
|
class DefaultExtension extends MProvider {
|
||||||
|
|
||||||
getHeaders(url) {
|
getHeaders() {
|
||||||
return {
|
return {
|
||||||
Referer: this.source.apiUrl
|
Referer: this.source.apiUrl
|
||||||
}
|
}
|
||||||
@@ -59,9 +59,17 @@ class DefaultExtension extends MProvider {
|
|||||||
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);
|
||||||
|
|
||||||
|
var fullList = [];
|
||||||
|
|
||||||
|
var priority = await this.getPreference("pref_content_priority");
|
||||||
|
if (priority === "series") {
|
||||||
|
fullList = [...popSeries, ...popMovie];
|
||||||
|
} else {
|
||||||
|
fullList = [...popMovie, ...popSeries]
|
||||||
|
}
|
||||||
var hasNextPage = slug.indexOf("search=") > -1 ? false : true;
|
var hasNextPage = slug.indexOf("search=") > -1 ? false : true;
|
||||||
return {
|
return {
|
||||||
list: [...popMovie, ...popSeries],
|
list: fullList,
|
||||||
hasNextPage
|
hasNextPage
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -90,8 +98,9 @@ class DefaultExtension extends MProvider {
|
|||||||
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,)
|
||||||
var imdb_id = result.imdb_id
|
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 = [];
|
||||||
@@ -99,12 +108,14 @@ class DefaultExtension extends MProvider {
|
|||||||
var item = {
|
var item = {
|
||||||
name: result.name,
|
name: result.name,
|
||||||
imageUrl: result.poster,
|
imageUrl: result.poster,
|
||||||
link: `https://imdb.com/title/${imdb_id}`,
|
link: `${this.source.baseUrl}/${media_type}/${tmdb_id}`,
|
||||||
description: result.description,
|
description: result.description,
|
||||||
genre: result.genre,
|
genre: result.genre,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (media_type == "series") {
|
var link = `${media_type}||${tmdb_id}`
|
||||||
|
|
||||||
|
if (media_type == "tv") {
|
||||||
|
|
||||||
var videos = result.videos
|
var videos = result.videos
|
||||||
for (var i in videos) {
|
for (var i in videos) {
|
||||||
@@ -118,7 +129,7 @@ class DefaultExtension extends MProvider {
|
|||||||
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 = `tv||${tmdb_id}/${seasonNum}/${episodeNum}`
|
var eplink = `${link}||${seasonNum}||${episodeNum}`
|
||||||
|
|
||||||
chaps.push({
|
chaps.push({
|
||||||
name: name,
|
name: name,
|
||||||
@@ -131,7 +142,7 @@ class DefaultExtension extends MProvider {
|
|||||||
if (release < dateNow) {
|
if (release < dateNow) {
|
||||||
chaps.push({
|
chaps.push({
|
||||||
name: "Movie",
|
name: "Movie",
|
||||||
url: `${media_type}||${tmdb_id}`,
|
url: link,
|
||||||
dateUpload: release.toString(),
|
dateUpload: release.toString(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -141,27 +152,57 @@ class DefaultExtension extends MProvider {
|
|||||||
chaps.reverse();
|
chaps.reverse();
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
async extractStreams(url) {
|
|
||||||
|
// Extracts the streams url for different resolutions from a hls stream.
|
||||||
|
async extractStreams(url, lang = "", hdr = {}) {
|
||||||
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,
|
||||||
|
originalUrl: url,
|
||||||
|
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:')) {
|
||||||
const resolution = lines[i].match(/RESOLUTION=(\d+x\d+)/)[1];
|
var resolution = lines[i].match(/RESOLUTION=(\d+x\d+)/)[1];
|
||||||
const m3u8Url = lines[i + 1].trim();
|
resolution = `${lang} ${resolution}`
|
||||||
|
var m3u8Url = lines[i + 1].trim();
|
||||||
|
m3u8Url = m3u8Url.replace("./", `${url}/`)
|
||||||
streams.push({
|
streams.push({
|
||||||
url: m3u8Url,
|
url: m3u8Url,
|
||||||
originalUrl: m3u8Url,
|
originalUrl: m3u8Url,
|
||||||
quality: resolution,
|
quality: resolution,
|
||||||
|
headers: hdr
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return streams
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// For some streams, we can form stream url using a default template.
|
||||||
|
async splitStreams(url, lang = "", hdr = {}) {
|
||||||
|
var streams = [];
|
||||||
|
var quality = ["auto", "360", "480", "720", "1080"]
|
||||||
|
for (var q of quality) {
|
||||||
|
var link = url
|
||||||
|
if (q != "auto") {
|
||||||
|
link = link.replace("index.m3u8", `${q}/index.m3u8`)
|
||||||
|
q = `${q}p`
|
||||||
|
}
|
||||||
|
streams.push({
|
||||||
|
url: link,
|
||||||
|
originalUrl: link,
|
||||||
|
quality: `${lang} - ${q}`,
|
||||||
|
headers: hdr
|
||||||
|
});
|
||||||
|
}
|
||||||
return streams;
|
return streams;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sorts streams based on user preference.
|
||||||
async sortStreams(streams) {
|
async sortStreams(streams) {
|
||||||
var sortedStreams = [];
|
var sortedStreams = [];
|
||||||
|
|
||||||
@@ -181,31 +222,177 @@ class DefaultExtension extends MProvider {
|
|||||||
return [...sortedStreams, ...copyStreams]
|
return [...sortedStreams, ...copyStreams]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Gets subtitles based on TMDB id.
|
||||||
|
async getSubtitleList(id, s, e) {
|
||||||
|
var api = `https://sub.wyzie.ru/search?id=${id}`
|
||||||
|
if (s != "0") api = `${api}&season=${s}&episode=${e}`
|
||||||
|
var response = await new Client().get(api);
|
||||||
|
var body = JSON.parse(response.body);
|
||||||
|
|
||||||
|
var subs = []
|
||||||
|
for (var sub of body) {
|
||||||
|
subs.push({
|
||||||
|
file: sub.url,
|
||||||
|
label: sub.display
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return subs
|
||||||
|
}
|
||||||
|
|
||||||
// For anime episode video list
|
// For anime episode video list
|
||||||
async getVideoList(url) {
|
async getVideoList(url) {
|
||||||
|
var streamAPI = parseInt(await this.getPreference("pref_stream_source"))
|
||||||
|
|
||||||
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 api = `${this.source.apiUrl}/api/getVideoSource?type=${media_type}&id=${id}`
|
var tmdb = id
|
||||||
const response = await new Client().get(api, this.getHeaders());
|
var streams = []
|
||||||
const body = JSON.parse(response.body);
|
var subtitles = []
|
||||||
|
switch (streamAPI) {
|
||||||
|
case 2: {
|
||||||
|
var s = "0"
|
||||||
|
var e = "0"
|
||||||
|
if (media_type == "tv") {
|
||||||
|
s = parts[2]
|
||||||
|
e = parts[3]
|
||||||
|
id = `${id}/${s}/${e}`
|
||||||
|
}
|
||||||
|
var api = `https://play2.123embed.net/server/3?path=/${media_type}/${id}`
|
||||||
|
var response = await new Client().get(api);
|
||||||
|
|
||||||
if (response.statusCode == 404) {
|
if (response.statusCode != 200) {
|
||||||
throw new Error("Video unavailable");
|
throw new Error("Video unavailable");
|
||||||
|
}
|
||||||
|
|
||||||
|
var body = JSON.parse(response.body);
|
||||||
|
var link = body.playlist[0].file
|
||||||
|
streams.push({
|
||||||
|
url: link,
|
||||||
|
originalUrl: link,
|
||||||
|
quality: "auto",
|
||||||
|
headers: { "Origin": "https://play2.123embed.net" },
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 3: {
|
||||||
|
var s = "0"
|
||||||
|
var e = "0"
|
||||||
|
if (media_type == "tv") {
|
||||||
|
s = parts[2]
|
||||||
|
e = parts[3]
|
||||||
|
id = `${id}&s=${s}&e=${e}`
|
||||||
|
}
|
||||||
|
var api = `https://autoembed.cc/embed/player.php?id=${id}`
|
||||||
|
|
||||||
|
var response = await new Client().get(api);
|
||||||
|
|
||||||
|
if (response.statusCode != 200) {
|
||||||
|
throw new Error("Video unavailable");
|
||||||
|
}
|
||||||
|
var body = response.body
|
||||||
|
var sKey = '"file": '
|
||||||
|
var eKey = "]});"
|
||||||
|
var start = body.indexOf(sKey)
|
||||||
|
if (start < 0) {
|
||||||
|
throw new Error("Video unavailable");
|
||||||
|
}
|
||||||
|
start += sKey.length
|
||||||
|
|
||||||
|
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 streamSplit = await this.splitStreams(link, lang);
|
||||||
|
streams = [...streams, ...streamSplit]
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 4: {
|
||||||
|
var s = "0"
|
||||||
|
var e = "0"
|
||||||
|
if (media_type == "tv") {
|
||||||
|
s = parts[2]
|
||||||
|
e = parts[3]
|
||||||
|
id = `${id}&season=${s}&episode=${e}`
|
||||||
|
}
|
||||||
|
var api = `https://player.flicky.host/Server-main.php?id=${id}`
|
||||||
|
var response = await new Client().get(api, { "Referer": "https://flicky.host/" });
|
||||||
|
|
||||||
|
if (response.statusCode != 200) {
|
||||||
|
throw new Error("Video unavailable");
|
||||||
|
}
|
||||||
|
var body = response.body
|
||||||
|
var sKey = 'streams = '
|
||||||
|
var eKey = "];"
|
||||||
|
var start = body.indexOf(sKey)
|
||||||
|
if (start < 0) {
|
||||||
|
throw new Error("Video unavailable");
|
||||||
|
}
|
||||||
|
start += sKey.length
|
||||||
|
|
||||||
|
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 streamSplit = await this.splitStreams(link, lang);
|
||||||
|
streams = [...streams, ...streamSplit]
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 5: {
|
||||||
|
if (media_type == "tv") {
|
||||||
|
id = `${id}/${parts[2]}/${parts[3]}`
|
||||||
|
}
|
||||||
|
var api = `https://vidapi.click/api/video/${media_type}/${id}`
|
||||||
|
var response = await new Client().get(api);
|
||||||
|
|
||||||
|
if (response.statusCode != 200) {
|
||||||
|
throw new Error("Video unavailable");
|
||||||
|
}
|
||||||
|
|
||||||
|
var body = JSON.parse(response.body);
|
||||||
|
var link = body.sources[0].file
|
||||||
|
subtitles = body.tracks
|
||||||
|
streams = await this.extractStreams(link);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default: {
|
||||||
|
if (media_type == "tv") {
|
||||||
|
id = `${id}/${parts[2]}/${parts[3]}`
|
||||||
|
}
|
||||||
|
var api = `${this.source.apiUrl}/api/getVideoSource?type=${media_type}&id=${id}`
|
||||||
|
var response = await new Client().get(api, this.getHeaders());
|
||||||
|
|
||||||
|
if (response.statusCode != 200) {
|
||||||
|
throw new Error("Video unavailable");
|
||||||
|
}
|
||||||
|
|
||||||
|
var body = JSON.parse(response.body);
|
||||||
|
var link = body.videoSource
|
||||||
|
subtitles = body.subtitles
|
||||||
|
streams = await this.extractStreams(link);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
var link = body.videoSource
|
if (streams.length < 1) {
|
||||||
|
throw new Error("Video unavailable");
|
||||||
|
}
|
||||||
|
|
||||||
var subtitles = body.subtitles
|
if (subtitles.length < 1) {
|
||||||
var streams = await this.extractStreams(link);
|
subtitles = await this.getSubtitleList(tmdb, s, e)
|
||||||
streams.push({
|
}
|
||||||
url: link,
|
streams[0].subtitles = subtitles
|
||||||
originalUrl: link,
|
|
||||||
quality: "auto",
|
|
||||||
subtitles: subtitles,
|
|
||||||
});
|
|
||||||
|
|
||||||
return await this.sortStreams(streams);
|
return await this.sortStreams(streams)
|
||||||
}
|
}
|
||||||
// For manga chapter pages
|
// For manga chapter pages
|
||||||
async getPageList() {
|
async getPageList() {
|
||||||
@@ -217,27 +404,44 @@ 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',
|
||||||
|
listPreference: {
|
||||||
|
title: 'Preferred content priority',
|
||||||
|
summary: 'Choose which type of content to show first',
|
||||||
|
valueIndex: 0,
|
||||||
|
entries: ["Movies", "Series"],
|
||||||
|
entryValues: ["movies", "series"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'pref_stream_source',
|
||||||
|
listPreference: {
|
||||||
|
title: 'Preferred stream source',
|
||||||
|
summary: '',
|
||||||
|
valueIndex: 0,
|
||||||
|
entries: ["tom.autoembed.cc", "123embed.net", "autoembed.cc - Indian languages", "flicky.host - Indian languages", "vidapi.click"],
|
||||||
|
entryValues: ["1", "2", "3", "4", "5"]
|
||||||
|
}
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,8 +5,8 @@ const mangayomiSources = [{
|
|||||||
"apiUrl": "",
|
"apiUrl": "",
|
||||||
"iconUrl": "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/javascript/icon/all.netflixmirror.png",
|
"iconUrl": "https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/main/javascript/icon/all.netflixmirror.png",
|
||||||
"typeSource": "single",
|
"typeSource": "single",
|
||||||
"itemType": 1,
|
"isManga": false,
|
||||||
"version": "0.0.55",
|
"version": "0.0.6",
|
||||||
"dateFormat": "",
|
"dateFormat": "",
|
||||||
"dateFormatLocale": "",
|
"dateFormatLocale": "",
|
||||||
"pkgPath": "anime/src/all/netflixmirror.js"
|
"pkgPath": "anime/src/all/netflixmirror.js"
|
||||||
@@ -22,8 +22,10 @@ class DefaultExtension extends MProvider {
|
|||||||
if (elements && elements.length > 0) {
|
if (elements && elements.length > 0) {
|
||||||
return cookie;
|
return cookie;
|
||||||
}
|
}
|
||||||
const addhash = new Document((await new Client().get(`${this.source.baseUrl}/home`, { "cookie": "" })).body).selectFirst("body").attr("data-addhash");
|
const hDoc = new Document((await new Client().get(`${this.source.baseUrl}/home`, { "cookie": "" })).body);
|
||||||
await new Client().get(`https://userverify.netmirror.app/verify?vhfd=${addhash}&a=y`);
|
const addhash = hDoc.selectFirst("body").attr("data-addhash");
|
||||||
|
const time = hDoc.selectFirst("body").attr("data-time");
|
||||||
|
await new Client().get(`https://userverify.netmirror.app/?fr3=${addhash}&a=y&t=${time}`);
|
||||||
let body;
|
let body;
|
||||||
let res;
|
let res;
|
||||||
do {
|
do {
|
||||||
@@ -209,4 +211,4 @@ class DefaultExtension extends MProvider {
|
|||||||
return videoList;
|
return videoList;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
280
javascript/manga/src/en/mangapill.js
Normal file
280
javascript/manga/src/en/mangapill.js
Normal file
@@ -0,0 +1,280 @@
|
|||||||
|
const mangayomiSources = [{
|
||||||
|
"name": "Mangapill",
|
||||||
|
"lang": "en",
|
||||||
|
"baseUrl": "https://mangapill.com",
|
||||||
|
"apiUrl": "",
|
||||||
|
"iconUrl": "https://www.google.com/s2/favicons?sz=64&domain=https://mangapill.com/",
|
||||||
|
"typeSource": "single",
|
||||||
|
"isManga": true,
|
||||||
|
"version": "1.0.2",
|
||||||
|
"dateFormat": "",
|
||||||
|
"dateFormatLocale": "",
|
||||||
|
"pkgPath": "manga/src/en/mangapill.js"
|
||||||
|
}];
|
||||||
|
|
||||||
|
class DefaultExtension extends MProvider {
|
||||||
|
getHeaders(url) {
|
||||||
|
return {
|
||||||
|
"Referer": this.source.baseUrl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
statusCode(status) {
|
||||||
|
return {
|
||||||
|
"publishing": 0,
|
||||||
|
"finished": 1,
|
||||||
|
"on hiatus": 2,
|
||||||
|
"discontinued": 3,
|
||||||
|
"not yet published": 4,
|
||||||
|
}[status] ?? 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getPreference(key) {
|
||||||
|
const preferences = new SharedPreferences();
|
||||||
|
return parseInt(preferences.get(key))
|
||||||
|
}
|
||||||
|
|
||||||
|
async getMangaList(slug) {
|
||||||
|
var lang = await this.getPreference("pref_title_lang");
|
||||||
|
|
||||||
|
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")
|
||||||
|
for (var manga of mangaElements) {
|
||||||
|
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 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
|
||||||
|
}
|
||||||
|
return { list, hasNextPage }
|
||||||
|
}
|
||||||
|
|
||||||
|
async getNavPage(prefKey) {
|
||||||
|
var val = await this.getPreference(prefKey);
|
||||||
|
var slug = ''
|
||||||
|
switch (val) {
|
||||||
|
case 1: {
|
||||||
|
slug = 'mangas/new'
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 2: {
|
||||||
|
slug = 'chapters'
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return await this.getMangaList(slug)
|
||||||
|
}
|
||||||
|
|
||||||
|
async getPopular(page) {
|
||||||
|
return await this.getNavPage("pref_popular_content");
|
||||||
|
}
|
||||||
|
get supportsLatest() {
|
||||||
|
throw new Error("supportsLatest not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
async getLatestUpdates(page) {
|
||||||
|
return await this.getNavPage("pref_latest_content");
|
||||||
|
}
|
||||||
|
|
||||||
|
async searchManga(query, status, type, genre, page) {
|
||||||
|
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 genre = ""
|
||||||
|
for (var filter of filters[2].state) {
|
||||||
|
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 link = `${this.source.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 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 })
|
||||||
|
}
|
||||||
|
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) {
|
||||||
|
throw new Error("getVideoList not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
// For manga chapter pages
|
||||||
|
async getPageList(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")
|
||||||
|
for (var page of pages) {
|
||||||
|
var img = page.selectFirst("img").getSrc
|
||||||
|
if (img != null) urls.push(img);
|
||||||
|
}
|
||||||
|
|
||||||
|
return urls
|
||||||
|
}
|
||||||
|
|
||||||
|
getFilterList() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
type_name: "SelectFilter",
|
||||||
|
name: "Type",
|
||||||
|
state: 0,
|
||||||
|
values: [
|
||||||
|
["All", ""],
|
||||||
|
["Manga", "manga"],
|
||||||
|
["Novel", "novel"],
|
||||||
|
["One-Shot", "one-shot"],
|
||||||
|
["Doujinshi", "doujinshi"],
|
||||||
|
["Manhwa", "manhwa"],
|
||||||
|
["Manhua", "manhua"],
|
||||||
|
["Oel", "oel"]
|
||||||
|
].map(x => ({ type_name: 'SelectOption', name: x[0], value: x[1] }))
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type_name: "SelectFilter",
|
||||||
|
name: "Status",
|
||||||
|
state: 0,
|
||||||
|
values: [
|
||||||
|
["All", ""],
|
||||||
|
["Publishing", "publishing"],
|
||||||
|
["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] }))
|
||||||
|
}, {
|
||||||
|
type_name: "GroupFilter",
|
||||||
|
name: "Genre",
|
||||||
|
state: [
|
||||||
|
["Action", "Action"],
|
||||||
|
["Adventure", "Adventure"],
|
||||||
|
["Cars", "Cars"],
|
||||||
|
["Comedy", "Comedy"],
|
||||||
|
["Dementia", "Dementia"],
|
||||||
|
["Demons", "Demons"],
|
||||||
|
["Doujinshi", "Doujinshi"],
|
||||||
|
["Drama", "Drama"],
|
||||||
|
["Ecchi", "Ecchi"],
|
||||||
|
["Fantasy", "Fantasy"],
|
||||||
|
["Game", "Game"],
|
||||||
|
["Gender Bender", "Gender Bender"],
|
||||||
|
["Harem", "Harem"],
|
||||||
|
["Historical", "Historical"],
|
||||||
|
["Horror", "Horror"],
|
||||||
|
["Isekai", "Isekai"],
|
||||||
|
["Josei", "Josei"],
|
||||||
|
["Kids", "Kids"],
|
||||||
|
["Magic", "Magic"],
|
||||||
|
["Martial Arts", "Martial Arts"],
|
||||||
|
["Mecha", "Mecha"],
|
||||||
|
["Military", "Military"],
|
||||||
|
["Music", "Music"],
|
||||||
|
["Mystery", "Mystery"],
|
||||||
|
["Parody", "Parody"],
|
||||||
|
["Police", "Police"],
|
||||||
|
["Psychological", "Psychological"],
|
||||||
|
["Romance", "Romance"],
|
||||||
|
["Samurai", "Samurai"],
|
||||||
|
["School", "School"],
|
||||||
|
["Sci-Fi", "Sci-Fi"],
|
||||||
|
["Seinen", "Seinen"],
|
||||||
|
["Shoujo", "Shoujo"],
|
||||||
|
["Shoujo Ai", "Shoujo Ai"],
|
||||||
|
["Shounen", "Shounen"],
|
||||||
|
["Shounen Ai", "Shounen Ai"],
|
||||||
|
["Slice of Life", "Slice of Life"],
|
||||||
|
["Space", "Space"],
|
||||||
|
["Sports", "Sports"],
|
||||||
|
["Super Power", "Super Power"],
|
||||||
|
["Supernatural", "Supernatural"],
|
||||||
|
["Thriller", "Thriller"],
|
||||||
|
["Tragedy", "Tragedy"],
|
||||||
|
["Vampire", "Vampire"],
|
||||||
|
["Yaoi", "Yaoi"],
|
||||||
|
["Yuri", "Yuri"]
|
||||||
|
].map(x => ({ type_name: 'CheckBox', name: x[0], value: x[1] }))
|
||||||
|
}
|
||||||
|
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getSourcePreferences() {
|
||||||
|
return [{
|
||||||
|
key: 'pref_popular_content',
|
||||||
|
listPreference: {
|
||||||
|
title: 'Preferred popular content',
|
||||||
|
summary: '',
|
||||||
|
valueIndex: 0,
|
||||||
|
entries: ["New Mangas", "Recent Chapters"],
|
||||||
|
entryValues: ["1", "2"]
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
key: 'pref_latest_content',
|
||||||
|
listPreference: {
|
||||||
|
title: 'Preferred latest content',
|
||||||
|
summary: '',
|
||||||
|
valueIndex: 1,
|
||||||
|
entries: ["New Mangas", "Recent Chapters"],
|
||||||
|
entryValues: ["1", "2"]
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
key: 'pref_title_lang',
|
||||||
|
listPreference: {
|
||||||
|
title: 'Preferred title language',
|
||||||
|
summary: '',
|
||||||
|
valueIndex: 0,
|
||||||
|
entries: ["Romaji", "English"],
|
||||||
|
entryValues: ["1", "2"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user