AniWorld, AnimeFenix, JKAnime and TioAnime: New host, fixes and helper lib update

AniWorld
- New Host: Speedfiles
- New Host: Filemoon
- Better Sorting for preferences

AnimeFenix:
- Added possible status string

JKAnime
- Get Studios

TioAnime
- Fix hasNextPage

Helper library
- Add new host: Speedfiles
- Add new String function: reverse, swapcase
This commit is contained in:
RndDev123
2024-11-26 08:05:50 +01:00
parent 8973f5b076
commit 40af589d38
4 changed files with 1014 additions and 258 deletions

View File

@@ -6,7 +6,7 @@ const mangayomiSources = [{
"iconUrl": "https://www3.animefenix.tv/themes/fenix-neo/images/AveFenix.png",
"typeSource": "single",
"isManga": false,
"version": "0.1.0",
"version": "0.1.1",
"dateFormat": "",
"dateFormatLocale": "",
"pkgPath": "anime/src/es/animefenix.js"
@@ -37,10 +37,11 @@ class DefaultExtension extends MProvider {
}
statusFromString(status) {
return {
"En emision": 0, // releasing
"Finalizado": 1, // finished
"Proximamente": 4, // unreleased
}[status] ?? 5;
"en emision": 0, // releasing
"emisión": 0, // releasing
"finalizado": 1, // finished
"proximamente": 4, // unreleased
}[status.toLowerCase()] ?? 5;
}
async getPopular(page) {
return this.parseAnimeList(`${this.source.baseUrl}/animes?order=visits&page=${page}`);
@@ -80,7 +81,7 @@ class DefaultExtension extends MProvider {
const info = doc.selectFirst('main div.flex');
detail.name = info.selectFirst("h1").text;
detail.status = this.statusFromString(info.selectFirst("a").text);
detail.status = this.statusFromString(info.selectFirst("a").text.trim());
detail.imageUrl = info.selectFirst("img").getSrc;
detail.description = info.selectFirst("h2 + p").text.trim();
detail.genre = info.select("h2 + div a").map(e => e.text.trim());
@@ -114,13 +115,8 @@ class DefaultExtension extends MProvider {
};
// extract remote video links
const renameLUT = {
'amazones': 'amazon',
'burst': 'burstcloud',
'hide': 'vidhide',
'ru': 'okru',
'stream2': 'vidhide',
};
const renameLUT = { 'amazones': 'amazon', 'burst': 'burstcloud', 'hide': 'vidhide',
'ru': 'okru', 'stream2': 'vidhide', };
for (let i = 0; i < hosts.length; i++) {
const host = hosts[i].trim();
const lhost = host.toLowerCase();
@@ -509,6 +505,11 @@ class DefaultExtension extends MProvider {
];
}
getSourcePreferences() {
const languages = ['Español'];
const types = ['Sub'];
const resolutions = ['1080p', '720p', '480p'];
const hosts = ['Amazon', 'AmazonEs', 'Burst', 'Mp4Upload', 'RU', 'Sendvid', 'STREAM2', 'HIDE', 'YourUpload'];
return [
{
key: 'lang',
@@ -516,12 +517,8 @@ class DefaultExtension extends MProvider {
title: 'Preferred Language',
summary: 'If available, this language will be chosen by default. Priority = 0 (lower is better)',
valueIndex: 0,
entries: [
'Español'
],
entryValues: [
'Español'
]
entries: languages,
entryValues: languages
}
},
{
@@ -530,12 +527,8 @@ class DefaultExtension extends MProvider {
title: 'Preferred Type',
summary: 'If available, this type will be chosen by default. Priority = 1 (lower is better)',
valueIndex: 0,
entries: [
'Sub'
],
entryValues: [
'Sub'
]
entries: types,
entryValues: types
}
},
{
@@ -544,44 +537,18 @@ class DefaultExtension extends MProvider {
title: 'Preferred Resolution',
summary: 'If available, this resolution will be chosen by default. Priority = 2 (lower is better)',
valueIndex: 0,
entries: [
'1080p',
'720p',
'480p'
],
entryValues: [
'1080p',
'720p',
'480p'
]
entries: resolutions,
entryValues: resolutions
}
},
{
key: 'host',
listPreference: {
title: 'Preferred Hoster',
title: 'Preferred Host',
summary: 'If available, this hoster will be chosen by default. Priority = 3 (lower is better)',
valueIndex: 0,
entries: [
'Amazon',
'Burst',
'Mp4Upload',
'RU',
'Sendvid',
'STREAM2',
'HIDE',
'YourUpload',
],
entryValues: [
'Amazon',
'Burst',
'Mp4Upload',
'RU',
'Sendvid',
'STREAM2',
'HIDE',
'YourUpload',
]
entries: hosts,
entryValues: hosts
}
}
];
@@ -590,7 +557,7 @@ class DefaultExtension extends MProvider {
/***************************************************************************************************
*
* mangayomi-js-helpers v1.0
* mangayomi-js-helpers v1.1
*
* # Video Extractors
* - vidGuardExtractor
@@ -601,9 +568,10 @@ class DefaultExtension extends MProvider {
* - vidHideExtractor
* - filemoonExtractor
* - mixdropExtractor
* - speedfilesExtractor
* - burstcloudExtractor (not working, see description)
*
* # Video Extractor Format Wrappers
* # Video Extractor Wrappers
* - streamWishExtractor
* - voeExtractor
* - mp4UploadExtractor
@@ -618,24 +586,34 @@ class DefaultExtension extends MProvider {
* - m3u8Extractor
* - jwplayerExtractor
*
* # Extension
* # Extension Helpers
* - sortVideos()
*
* # Encoding/Decoding
* - Uint8Array.fromBase64()
* - Uint8Array.prototype.toBase64()
* - Uint8Array.prototype.decode()
* # Uint8Array
* - Uint8Array.fromBase64()
* - Uint8Array.prototype.toBase64()
* - Uint8Array.prototype.decode()
*
* # String
* - String.prototype.encode()
* - String.prototype.decode()
*
* # Random string
* - String.decode()
* - String.prototype.reverse()
* - String.prototype.swapcase()
* - getRandomString()
*
* # Encode/Decode Functions
* - decodeUTF8
* - encodeUTF8
*
* # URL
* # Url
* - absUrl()
*
***************************************************************************************************/
//--------------------------------------------------------------------------------------------------
// Video Extractors
//--------------------------------------------------------------------------------------------------
async function vidGuardExtractor(url) {
// get html
const res = await new Client().get(url);
@@ -716,8 +694,8 @@ async function vidHideExtractor(url) {
return await jwplayerExtractor(res.body);
}
async function filemoonExtractor(url) {
let res = await new Client().get(url);
async function filemoonExtractor(url, headers) {
let res = await new Client().get(url, headers);
const src = res.body.match(/iframe src="(.*?)"/)?.[1];
if (src) {
res = await new Client().get(src, {
@@ -749,6 +727,37 @@ async function mixdropExtractor(url) {
return [{url: videoUrl, originalUrl: videoUrl, quality: '', headers: headers}];
}
async function speedfilesExtractor(url) {
let res = await new Client().get(url);
let doc = new Document(res.body);
const code = doc.selectFirst('script:contains(var)').text;
let b64;
// Get b64
for (const match of code.matchAll(/(?:var|let|const)\s*\w+\s*=\s*["']([^"']+)/g)) {
if (match[1].match(/[g-zG-Z]/)) {
b64 = match[1];
break;
}
}
// decode b64 => b64
const step1 = Uint8Array.fromBase64(b64).reverse().decode().swapcase();
// decode b64 => hex
const step2 = Uint8Array.fromBase64(step1).reverse().decode();
// decode hex => b64
let step3 = [];
for (let i = 0; i < step2.length; i += 2) {
step3.push(parseInt(step2.slice(i, i + 2), 16) - 3);
}
step3 = String.fromCharCode(...step3.reverse()).swapcase();
// decode b64 => url
const videoUrl = Uint8Array.fromBase64(step3).decode();
return [{url: videoUrl, originalUrl: videoUrl, quality: '', headers: null}];
}
/** Does not work: Client always sets 'charset=utf-8' in Content-Type. */
async function burstcloudExtractor(url) {
let client = new Client();
@@ -773,6 +782,10 @@ async function burstcloudExtractor(url) {
}];
}
//--------------------------------------------------------------------------------------------------
// Video Extractor Wrappers
//--------------------------------------------------------------------------------------------------
_streamWishExtractor = streamWishExtractor;
streamWishExtractor = async (url) => {
return (await _streamWishExtractor(url, '')).map(v => {
@@ -829,9 +842,13 @@ sendVidExtractor = async (url) => {
return [{url: videoUrl, originalUrl: videoUrl, quality: quality, headers: null}];
}
async function extractAny(url, method, lang, type, host) {
//--------------------------------------------------------------------------------------------------
// Video Extractor Helpers
//--------------------------------------------------------------------------------------------------
async function extractAny(url, method, lang, type, host, headers = null) {
const m = extractAny.methods[method];
return (!m) ? [] : (await m(url)).map(v => {
return (!m) ? [] : (await m(url, headers)).map(v => {
v.quality = v.quality ? `${lang} ${type} ${v.quality} ${host}` : `${lang} ${type} ${host}`;
return v;
});
@@ -846,6 +863,7 @@ extractAny.methods = {
'mp4upload': mp4UploadExtractor,
'okru': okruExtractor,
'sendvid': sendVidExtractor,
'speedfiles': speedfilesExtractor,
'streamtape': streamTapeExtractor,
'streamwish': vidHideExtractor,
'vidguard': vidGuardExtractor,
@@ -855,6 +873,10 @@ extractAny.methods = {
'yourupload': yourUploadExtractor
};
//--------------------------------------------------------------------------------------------------
// Playlist Extractors
//--------------------------------------------------------------------------------------------------
async function m3u8Extractor(url, headers = null) {
// https://developer.apple.com/documentation/http-live-streaming/creating-a-multivariant-playlist
// https://developer.apple.com/documentation/http-live-streaming/adding-alternate-media-to-a-playlist
@@ -992,6 +1014,10 @@ async function jwplayerExtractor(text, headers) {
});
}
//--------------------------------------------------------------------------------------------------
// Extension Helpers
//--------------------------------------------------------------------------------------------------
function sortVideos(videos) {
const pref = new SharedPreferences();
const getres = RegExp('(\\d+)p?', 'i');
@@ -1023,6 +1049,10 @@ function sortVideos(videos) {
});
}
//--------------------------------------------------------------------------------------------------
// Uint8Array
//--------------------------------------------------------------------------------------------------
Uint8Array.fromBase64 = function (b64) {
// [00,01,02,03,04,05,06,07,08,\t,\n,0b,0c,\r,0e,0f,10,11,12,13,14,15,16,17,18,19,1a,1b,1c,1d,1e,1f,' ', !, ", #, $, %, &, ', (, ), *, +,',', -, ., /, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, :, ;, <,'=', >, ?, @,A,B,C,D,E,F,G,H,I,J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, [, \, ], ^, _, `, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, {, |, }, ~,7f]
const m = [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, 62, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1]
@@ -1062,6 +1092,10 @@ Uint8Array.prototype.decode = function (encoding = 'utf-8') {
return null;
}
//--------------------------------------------------------------------------------------------------
// String
//--------------------------------------------------------------------------------------------------
String.prototype.encode = function (encoding = 'utf-8') {
encoding = encoding.toLowerCase();
if (encoding == 'utf-8') {
@@ -1078,6 +1112,32 @@ String.decode = function (data, encoding = 'utf-8') {
return null;
}
String.prototype.reverse = function () {
return this.split('').reverse().join('');
}
String.prototype.swapcase = function () {
const isAsciiLetter = /[A-z]/;
const result = [];
for (const l of this)
result.push(isAsciiLetter.test(l) ? String.fromCharCode(l.charCodeAt() ^ 32) : l);
return result.join('');
}
function getRandomString(length) {
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890";
let result = "";
for (let i = 0; i < length; i++) {
const random = Math.floor(Math.random() * 61);
result += chars[random];
}
return result;
}
//--------------------------------------------------------------------------------------------------
// Encode/Decode Functions
//--------------------------------------------------------------------------------------------------
function decodeUTF8(data) {
const codes = [];
for (let i = 0; i < data.length;) {
@@ -1107,15 +1167,9 @@ function encodeUTF8(string) {
return new Uint8Array(data);
}
function getRandomString(length) {
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890";
let result = "";
for (let i = 0; i < length; i++) {
const random = Math.floor(Math.random() * 61);
result += chars[random];
}
return result;
}
//--------------------------------------------------------------------------------------------------
// Url
//--------------------------------------------------------------------------------------------------
function absUrl(url, base) {
if (url.search(/^\w+:\/\//) == 0) {

View File

@@ -6,7 +6,7 @@ const mangayomiSources = [{
"iconUrl": "https://cdn.jkdesu.com/assets2/css/img/favicon.ico",
"typeSource": "single",
"isManga": false,
"version": "0.1.0",
"version": "0.1.1",
"dateFormat": "",
"dateFormatLocale": "",
"pkgPath": "anime/src/es/jkanime.js"
@@ -102,6 +102,7 @@ class DefaultExtension extends MProvider {
detail.description = info.selectFirst("p.sinopsis").text.trim();
detail.status = this.statusFromString(extInfo.selectFirst("span:contains(Estado) + span").text);
detail.genre = extInfo.select("li:contains(Genero) a").map(e => e.text);
detail.author = extInfo.select("li:contains(Studios) a").map(e => e.text).join(', ');
// get episodes
detail.episodes = [];
@@ -779,6 +780,11 @@ class DefaultExtension extends MProvider {
];
}
getSourcePreferences() {
const languages = ['Español'];
const types = ['Sub'];
const resolutions = ['1080p', '720p', '480p'];
const hosts = ['Desu', 'Filemoon', 'Mixdrop', 'Mp4upload', 'Streamtape', 'Streamwish', 'Vidhide', 'VOE'];
return [
{
key: 'lang',
@@ -786,12 +792,8 @@ class DefaultExtension extends MProvider {
title: 'Preferred Language',
summary: 'If available, this language will be chosen by default. Priority = 0 (lower is better)',
valueIndex: 0,
entries: [
'Español'
],
entryValues: [
'Español'
]
entries: languages,
entryValues: languages
}
},
{
@@ -800,12 +802,8 @@ class DefaultExtension extends MProvider {
title: 'Preferred Type',
summary: 'If available, this type will be chosen by default. Priority = 1 (lower is better)',
valueIndex: 0,
entries: [
'Sub'
],
entryValues: [
'Sub'
]
entries: types,
entryValues: types
}
},
{
@@ -814,44 +812,18 @@ class DefaultExtension extends MProvider {
title: 'Preferred Resolution',
summary: 'If available, this resolution will be chosen by default. Priority = 2 (lower is better)',
valueIndex: 0,
entries: [
'1080p',
'720p',
'480p'
],
entryValues: [
'1080p',
'720p',
'480p'
]
entries: resolutions,
entryValues: resolutions
}
},
{
key: 'host',
listPreference: {
title: 'Preferred Hoster',
title: 'Preferred Host',
summary: 'If available, this hoster will be chosen by default. Priority = 3 (lower is better)',
valueIndex: 0,
entries: [
'Desu',
'Filemoon',
'Mixdrop',
'Mp4Upload',
'Streamtape',
'Streamwish',
'Vidhide',
'Voe'
],
entryValues: [
'Desu',
'Filemoon',
'Mixdrop',
'Mp4Upload',
'Streamtape',
'Streamwish',
'Vidhide',
'Voe'
]
entries: hosts,
entryValues: hosts
}
}
];
@@ -860,7 +832,7 @@ class DefaultExtension extends MProvider {
/***************************************************************************************************
*
* mangayomi-js-helpers v1.0
* mangayomi-js-helpers v1.1
*
* # Video Extractors
* - vidGuardExtractor
@@ -871,9 +843,10 @@ class DefaultExtension extends MProvider {
* - vidHideExtractor
* - filemoonExtractor
* - mixdropExtractor
* - speedfilesExtractor
* - burstcloudExtractor (not working, see description)
*
* # Video Extractor Format Wrappers
* # Video Extractor Wrappers
* - streamWishExtractor
* - voeExtractor
* - mp4UploadExtractor
@@ -888,24 +861,34 @@ class DefaultExtension extends MProvider {
* - m3u8Extractor
* - jwplayerExtractor
*
* # Extension
* # Extension Helpers
* - sortVideos()
*
* # Encoding/Decoding
* - Uint8Array.fromBase64()
* - Uint8Array.prototype.toBase64()
* - Uint8Array.prototype.decode()
* # Uint8Array
* - Uint8Array.fromBase64()
* - Uint8Array.prototype.toBase64()
* - Uint8Array.prototype.decode()
*
* # String
* - String.prototype.encode()
* - String.prototype.decode()
*
* # Random string
* - String.decode()
* - String.prototype.reverse()
* - String.prototype.swapcase()
* - getRandomString()
*
* # Encode/Decode Functions
* - decodeUTF8
* - encodeUTF8
*
* # URL
* # Url
* - absUrl()
*
***************************************************************************************************/
//--------------------------------------------------------------------------------------------------
// Video Extractors
//--------------------------------------------------------------------------------------------------
async function vidGuardExtractor(url) {
// get html
const res = await new Client().get(url);
@@ -986,8 +969,8 @@ async function vidHideExtractor(url) {
return await jwplayerExtractor(res.body);
}
async function filemoonExtractor(url) {
let res = await new Client().get(url);
async function filemoonExtractor(url, headers) {
let res = await new Client().get(url, headers);
const src = res.body.match(/iframe src="(.*?)"/)?.[1];
if (src) {
res = await new Client().get(src, {
@@ -1019,6 +1002,37 @@ async function mixdropExtractor(url) {
return [{url: videoUrl, originalUrl: videoUrl, quality: '', headers: headers}];
}
async function speedfilesExtractor(url) {
let res = await new Client().get(url);
let doc = new Document(res.body);
const code = doc.selectFirst('script:contains(var)').text;
let b64;
// Get b64
for (const match of code.matchAll(/(?:var|let|const)\s*\w+\s*=\s*["']([^"']+)/g)) {
if (match[1].match(/[g-zG-Z]/)) {
b64 = match[1];
break;
}
}
// decode b64 => b64
const step1 = Uint8Array.fromBase64(b64).reverse().decode().swapcase();
// decode b64 => hex
const step2 = Uint8Array.fromBase64(step1).reverse().decode();
// decode hex => b64
let step3 = [];
for (let i = 0; i < step2.length; i += 2) {
step3.push(parseInt(step2.slice(i, i + 2), 16) - 3);
}
step3 = String.fromCharCode(...step3.reverse()).swapcase();
// decode b64 => url
const videoUrl = Uint8Array.fromBase64(step3).decode();
return [{url: videoUrl, originalUrl: videoUrl, quality: '', headers: null}];
}
/** Does not work: Client always sets 'charset=utf-8' in Content-Type. */
async function burstcloudExtractor(url) {
let client = new Client();
@@ -1043,6 +1057,10 @@ async function burstcloudExtractor(url) {
}];
}
//--------------------------------------------------------------------------------------------------
// Video Extractor Wrappers
//--------------------------------------------------------------------------------------------------
_streamWishExtractor = streamWishExtractor;
streamWishExtractor = async (url) => {
return (await _streamWishExtractor(url, '')).map(v => {
@@ -1099,9 +1117,13 @@ sendVidExtractor = async (url) => {
return [{url: videoUrl, originalUrl: videoUrl, quality: quality, headers: null}];
}
async function extractAny(url, method, lang, type, host) {
//--------------------------------------------------------------------------------------------------
// Video Extractor Helpers
//--------------------------------------------------------------------------------------------------
async function extractAny(url, method, lang, type, host, headers = null) {
const m = extractAny.methods[method];
return (!m) ? [] : (await m(url)).map(v => {
return (!m) ? [] : (await m(url, headers)).map(v => {
v.quality = v.quality ? `${lang} ${type} ${v.quality} ${host}` : `${lang} ${type} ${host}`;
return v;
});
@@ -1116,6 +1138,7 @@ extractAny.methods = {
'mp4upload': mp4UploadExtractor,
'okru': okruExtractor,
'sendvid': sendVidExtractor,
'speedfiles': speedfilesExtractor,
'streamtape': streamTapeExtractor,
'streamwish': vidHideExtractor,
'vidguard': vidGuardExtractor,
@@ -1125,6 +1148,10 @@ extractAny.methods = {
'yourupload': yourUploadExtractor
};
//--------------------------------------------------------------------------------------------------
// Playlist Extractors
//--------------------------------------------------------------------------------------------------
async function m3u8Extractor(url, headers = null) {
// https://developer.apple.com/documentation/http-live-streaming/creating-a-multivariant-playlist
// https://developer.apple.com/documentation/http-live-streaming/adding-alternate-media-to-a-playlist
@@ -1262,6 +1289,10 @@ async function jwplayerExtractor(text, headers) {
});
}
//--------------------------------------------------------------------------------------------------
// Extension Helpers
//--------------------------------------------------------------------------------------------------
function sortVideos(videos) {
const pref = new SharedPreferences();
const getres = RegExp('(\\d+)p?', 'i');
@@ -1293,6 +1324,10 @@ function sortVideos(videos) {
});
}
//--------------------------------------------------------------------------------------------------
// Uint8Array
//--------------------------------------------------------------------------------------------------
Uint8Array.fromBase64 = function (b64) {
// [00,01,02,03,04,05,06,07,08,\t,\n,0b,0c,\r,0e,0f,10,11,12,13,14,15,16,17,18,19,1a,1b,1c,1d,1e,1f,' ', !, ", #, $, %, &, ', (, ), *, +,',', -, ., /, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, :, ;, <,'=', >, ?, @,A,B,C,D,E,F,G,H,I,J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, [, \, ], ^, _, `, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, {, |, }, ~,7f]
const m = [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, 62, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1]
@@ -1332,6 +1367,10 @@ Uint8Array.prototype.decode = function (encoding = 'utf-8') {
return null;
}
//--------------------------------------------------------------------------------------------------
// String
//--------------------------------------------------------------------------------------------------
String.prototype.encode = function (encoding = 'utf-8') {
encoding = encoding.toLowerCase();
if (encoding == 'utf-8') {
@@ -1348,6 +1387,32 @@ String.decode = function (data, encoding = 'utf-8') {
return null;
}
String.prototype.reverse = function () {
return this.split('').reverse().join('');
}
String.prototype.swapcase = function () {
const isAsciiLetter = /[A-z]/;
const result = [];
for (const l of this)
result.push(isAsciiLetter.test(l) ? String.fromCharCode(l.charCodeAt() ^ 32) : l);
return result.join('');
}
function getRandomString(length) {
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890";
let result = "";
for (let i = 0; i < length; i++) {
const random = Math.floor(Math.random() * 61);
result += chars[random];
}
return result;
}
//--------------------------------------------------------------------------------------------------
// Encode/Decode Functions
//--------------------------------------------------------------------------------------------------
function decodeUTF8(data) {
const codes = [];
for (let i = 0; i < data.length;) {
@@ -1377,15 +1442,9 @@ function encodeUTF8(string) {
return new Uint8Array(data);
}
function getRandomString(length) {
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890";
let result = "";
for (let i = 0; i < length; i++) {
const random = Math.floor(Math.random() * 61);
result += chars[random];
}
return result;
}
//--------------------------------------------------------------------------------------------------
// Url
//--------------------------------------------------------------------------------------------------
function absUrl(url, base) {
if (url.search(/^\w+:\/\//) == 0) {

View File

@@ -6,7 +6,7 @@ const mangayomiSources = [{
"iconUrl": "https://tioanime.com/assets/img/tio_fb.jpg",
"typeSource": "single",
"isManga": false,
"version": "0.1.0",
"version": "0.1.1",
"dateFormat": "",
"dateFormatLocale": "",
"pkgPath": "anime/src/es/tioanime.js"
@@ -32,8 +32,8 @@ class DefaultExtension extends MProvider {
const link = element.selectFirst("a").getHref;
list.push({ name, imageUrl, link });
}
const hasNextPage = doc.selectFirst("li.page-item.active + li").text == "»";
return { "list": list, "hasNextPage": hasNextPage };
const isLastPage = doc.selectFirst("li.page-item.active + li").text == "»";
return { "list": list, "hasNextPage": !isLastPage };
}
statusFromString(status) {
return {
@@ -106,6 +106,11 @@ class DefaultExtension extends MProvider {
throw new Error("getFilterList not implemented");
}
getSourcePreferences() {
const languages = ['Español'];
const types = ['Sub'];
const resolutions = ['1080p', '720p', '480p'];
const hosts = ['Okru', 'VidGuard', 'Voe', 'YourUpload'];
return [
{
key: 'lang',
@@ -113,12 +118,8 @@ class DefaultExtension extends MProvider {
title: 'Preferred Language',
summary: 'If available, this language will be chosen by default. Priority = 0 (lower is better)',
valueIndex: 0,
entries: [
'Español'
],
entryValues: [
'Español'
]
entries: languages,
entryValues: languages
}
},
{
@@ -127,12 +128,8 @@ class DefaultExtension extends MProvider {
title: 'Preferred Type',
summary: 'If available, this type will be chosen by default. Priority = 1 (lower is better)',
valueIndex: 0,
entries: [
'Sub'
],
entryValues: [
'Sub'
]
entries: types,
entryValues: types
}
},
{
@@ -141,36 +138,18 @@ class DefaultExtension extends MProvider {
title: 'Preferred Resolution',
summary: 'If available, this resolution will be chosen by default. Priority = 2 (lower is better)',
valueIndex: 0,
entries: [
'1080p',
'720p',
'480p'
],
entryValues: [
'1080p',
'720p',
'480p'
]
entries: resolutions,
entryValues: resolutions
}
},
{
key: 'host',
listPreference: {
title: 'Preferred Hoster',
title: 'Preferred Host',
summary: 'If available, this hoster will be chosen by default. Priority = 3 (lower is better)',
valueIndex: 0,
entries: [
'Okru',
'VidGuard',
'Voe',
'YourUpload'
],
entryValues: [
'Okru',
'VidGuard',
'Voe',
'YourUpload'
]
entries: hosts,
entryValues: hosts
}
}
];
@@ -179,7 +158,7 @@ class DefaultExtension extends MProvider {
/***************************************************************************************************
*
* mangayomi-js-helpers v1.0
* mangayomi-js-helpers v1.1
*
* # Video Extractors
* - vidGuardExtractor
@@ -190,9 +169,10 @@ class DefaultExtension extends MProvider {
* - vidHideExtractor
* - filemoonExtractor
* - mixdropExtractor
* - speedfilesExtractor
* - burstcloudExtractor (not working, see description)
*
* # Video Extractor Format Wrappers
* # Video Extractor Wrappers
* - streamWishExtractor
* - voeExtractor
* - mp4UploadExtractor
@@ -207,24 +187,34 @@ class DefaultExtension extends MProvider {
* - m3u8Extractor
* - jwplayerExtractor
*
* # Extension
* # Extension Helpers
* - sortVideos()
*
* # Encoding/Decoding
* - Uint8Array.fromBase64()
* - Uint8Array.prototype.toBase64()
* - Uint8Array.prototype.decode()
* # Uint8Array
* - Uint8Array.fromBase64()
* - Uint8Array.prototype.toBase64()
* - Uint8Array.prototype.decode()
*
* # String
* - String.prototype.encode()
* - String.prototype.decode()
*
* # Random string
* - String.decode()
* - String.prototype.reverse()
* - String.prototype.swapcase()
* - getRandomString()
*
* # Encode/Decode Functions
* - decodeUTF8
* - encodeUTF8
*
* # URL
* # Url
* - absUrl()
*
***************************************************************************************************/
//--------------------------------------------------------------------------------------------------
// Video Extractors
//--------------------------------------------------------------------------------------------------
async function vidGuardExtractor(url) {
// get html
const res = await new Client().get(url);
@@ -305,8 +295,8 @@ async function vidHideExtractor(url) {
return await jwplayerExtractor(res.body);
}
async function filemoonExtractor(url) {
let res = await new Client().get(url);
async function filemoonExtractor(url, headers) {
let res = await new Client().get(url, headers);
const src = res.body.match(/iframe src="(.*?)"/)?.[1];
if (src) {
res = await new Client().get(src, {
@@ -338,6 +328,37 @@ async function mixdropExtractor(url) {
return [{url: videoUrl, originalUrl: videoUrl, quality: '', headers: headers}];
}
async function speedfilesExtractor(url) {
let res = await new Client().get(url);
let doc = new Document(res.body);
const code = doc.selectFirst('script:contains(var)').text;
let b64;
// Get b64
for (const match of code.matchAll(/(?:var|let|const)\s*\w+\s*=\s*["']([^"']+)/g)) {
if (match[1].match(/[g-zG-Z]/)) {
b64 = match[1];
break;
}
}
// decode b64 => b64
const step1 = Uint8Array.fromBase64(b64).reverse().decode().swapcase();
// decode b64 => hex
const step2 = Uint8Array.fromBase64(step1).reverse().decode();
// decode hex => b64
let step3 = [];
for (let i = 0; i < step2.length; i += 2) {
step3.push(parseInt(step2.slice(i, i + 2), 16) - 3);
}
step3 = String.fromCharCode(...step3.reverse()).swapcase();
// decode b64 => url
const videoUrl = Uint8Array.fromBase64(step3).decode();
return [{url: videoUrl, originalUrl: videoUrl, quality: '', headers: null}];
}
/** Does not work: Client always sets 'charset=utf-8' in Content-Type. */
async function burstcloudExtractor(url) {
let client = new Client();
@@ -362,6 +383,10 @@ async function burstcloudExtractor(url) {
}];
}
//--------------------------------------------------------------------------------------------------
// Video Extractor Wrappers
//--------------------------------------------------------------------------------------------------
_streamWishExtractor = streamWishExtractor;
streamWishExtractor = async (url) => {
return (await _streamWishExtractor(url, '')).map(v => {
@@ -418,9 +443,13 @@ sendVidExtractor = async (url) => {
return [{url: videoUrl, originalUrl: videoUrl, quality: quality, headers: null}];
}
async function extractAny(url, method, lang, type, host) {
//--------------------------------------------------------------------------------------------------
// Video Extractor Helpers
//--------------------------------------------------------------------------------------------------
async function extractAny(url, method, lang, type, host, headers = null) {
const m = extractAny.methods[method];
return (!m) ? [] : (await m(url)).map(v => {
return (!m) ? [] : (await m(url, headers)).map(v => {
v.quality = v.quality ? `${lang} ${type} ${v.quality} ${host}` : `${lang} ${type} ${host}`;
return v;
});
@@ -435,6 +464,7 @@ extractAny.methods = {
'mp4upload': mp4UploadExtractor,
'okru': okruExtractor,
'sendvid': sendVidExtractor,
'speedfiles': speedfilesExtractor,
'streamtape': streamTapeExtractor,
'streamwish': vidHideExtractor,
'vidguard': vidGuardExtractor,
@@ -444,6 +474,10 @@ extractAny.methods = {
'yourupload': yourUploadExtractor
};
//--------------------------------------------------------------------------------------------------
// Playlist Extractors
//--------------------------------------------------------------------------------------------------
async function m3u8Extractor(url, headers = null) {
// https://developer.apple.com/documentation/http-live-streaming/creating-a-multivariant-playlist
// https://developer.apple.com/documentation/http-live-streaming/adding-alternate-media-to-a-playlist
@@ -581,6 +615,10 @@ async function jwplayerExtractor(text, headers) {
});
}
//--------------------------------------------------------------------------------------------------
// Extension Helpers
//--------------------------------------------------------------------------------------------------
function sortVideos(videos) {
const pref = new SharedPreferences();
const getres = RegExp('(\\d+)p?', 'i');
@@ -612,6 +650,10 @@ function sortVideos(videos) {
});
}
//--------------------------------------------------------------------------------------------------
// Uint8Array
//--------------------------------------------------------------------------------------------------
Uint8Array.fromBase64 = function (b64) {
// [00,01,02,03,04,05,06,07,08,\t,\n,0b,0c,\r,0e,0f,10,11,12,13,14,15,16,17,18,19,1a,1b,1c,1d,1e,1f,' ', !, ", #, $, %, &, ', (, ), *, +,',', -, ., /, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, :, ;, <,'=', >, ?, @,A,B,C,D,E,F,G,H,I,J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, [, \, ], ^, _, `, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, {, |, }, ~,7f]
const m = [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, 62, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1]
@@ -651,6 +693,10 @@ Uint8Array.prototype.decode = function (encoding = 'utf-8') {
return null;
}
//--------------------------------------------------------------------------------------------------
// String
//--------------------------------------------------------------------------------------------------
String.prototype.encode = function (encoding = 'utf-8') {
encoding = encoding.toLowerCase();
if (encoding == 'utf-8') {
@@ -667,6 +713,32 @@ String.decode = function (data, encoding = 'utf-8') {
return null;
}
String.prototype.reverse = function () {
return this.split('').reverse().join('');
}
String.prototype.swapcase = function () {
const isAsciiLetter = /[A-z]/;
const result = [];
for (const l of this)
result.push(isAsciiLetter.test(l) ? String.fromCharCode(l.charCodeAt() ^ 32) : l);
return result.join('');
}
function getRandomString(length) {
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890";
let result = "";
for (let i = 0; i < length; i++) {
const random = Math.floor(Math.random() * 61);
result += chars[random];
}
return result;
}
//--------------------------------------------------------------------------------------------------
// Encode/Decode Functions
//--------------------------------------------------------------------------------------------------
function decodeUTF8(data) {
const codes = [];
for (let i = 0; i < data.length;) {
@@ -696,15 +768,9 @@ function encodeUTF8(string) {
return new Uint8Array(data);
}
function getRandomString(length) {
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890";
let result = "";
for (let i = 0; i < length; i++) {
const random = Math.floor(Math.random() * 61);
result += chars[random];
}
return result;
}
//--------------------------------------------------------------------------------------------------
// Url
//--------------------------------------------------------------------------------------------------
function absUrl(url, base) {
if (url.search(/^\w+:\/\//) == 0) {