diff --git a/javascript/anime/src/all/autoembed.js b/javascript/anime/src/all/autoembed.js index 6713f666..b9a92841 100644 --- a/javascript/anime/src/all/autoembed.js +++ b/javascript/anime/src/all/autoembed.js @@ -6,7 +6,7 @@ const mangayomiSources = [{ "iconUrl": "https://www.google.com/s2/favicons?sz=64&domain=https://autoembed.cc/", "typeSource": "multi", "isManga": false, - "version": "1.0.3", + "version": "1.1.4", "dateFormat": "", "dateFormatLocale": "", "pkgPath": "anime/src/all/autoembed.js" @@ -129,7 +129,7 @@ class DefaultExtension extends MProvider { if (release < dateNow) { var episodeNum = video.episode var name = `S${seasonNum}:E${episodeNum} - ${video.name}` - var eplink = `${link}/${seasonNum}/${episodeNum}` + var eplink = `${link}||${seasonNum}||${episodeNum}` chaps.push({ name: name, @@ -152,27 +152,57 @@ class DefaultExtension extends MProvider { chaps.reverse(); 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 body = response.body; const lines = body.split('\n'); - var streams = []; + var streams = [{ + url: url, + originalUrl: url, + quality: "auto", + }]; for (let i = 0; i < lines.length; i++) { if (lines[i].startsWith('#EXT-X-STREAM-INF:')) { - const resolution = lines[i].match(/RESOLUTION=(\d+x\d+)/)[1]; - const m3u8Url = lines[i + 1].trim(); - + var resolution = lines[i].match(/RESOLUTION=(\d+x\d+)/)[1]; + resolution = `${lang} ${resolution}` + var m3u8Url = lines[i + 1].trim(); + m3u8Url = m3u8Url.replace("./", `${url}/`) streams.push({ url: m3u8Url, originalUrl: m3u8Url, 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; } + // Sorts streams based on user preference. async sortStreams(streams) { var sortedStreams = []; @@ -192,31 +222,177 @@ class DefaultExtension extends MProvider { 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 async getVideoList(url) { + var streamAPI = parseInt(await this.getPreference("pref_stream_source")) + var parts = url.split("||"); var media_type = parts[0]; var id = parts[1]; - var api = `${this.source.apiUrl}/api/getVideoSource?type=${media_type}&id=${id}` - const response = await new Client().get(api, this.getHeaders()); - const body = JSON.parse(response.body); + var tmdb = id + var streams = [] + 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) { - throw new Error("Video unavailable"); + if (response.statusCode != 200) { + 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 - var streams = await this.extractStreams(link); - streams.push({ - url: link, - originalUrl: link, - quality: "auto", - subtitles: subtitles, - }); + if (subtitles.length < 1) { + subtitles = await this.getSubtitleList(tmdb, s, e) + } + streams[0].subtitles = subtitles - return await this.sortStreams(streams); + return await this.sortStreams(streams) } // For manga chapter pages async getPageList() { @@ -255,8 +431,16 @@ class DefaultExtension extends MProvider { 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"] + } + }, ]; }