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/", "typeSource": "multi", "isManga": false, "itemType": 1, "version": "1.1.4", "dateFormat": "", "dateFormatLocale": "", "pkgPath": "anime/src/all/autoembed.js" }]; class DefaultExtension extends MProvider { getHeaders() { return { Referer: this.source.apiUrl } } async getPreference(key) { const preferences = new SharedPreferences(); return preferences.get(key); } async tmdbRequest(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; } async getSearchItems(body) { var items = []; var results = body.metas; for (let i in results) { var result = results[i]; 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 }); } 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); 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; return { list: fullList, hasNextPage }; } async getPopular(page) { var skip = (page - 1) * 20; return await this.getSearchInfo(`tmdb.popular/skip=${skip}.json`); } get supportsLatest() { throw new Error("supportsLatest not implemented"); } async getLatestUpdates(page) { var trend_window = await this.getPreference("pref_latest_time_window"); var skip = (page - 1) * 20; 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 parts = url.split("||"); var media_type = parts[0]; var id = parts[1]; var body = await this.tmdbRequest(`meta/${media_type}/${id}.json`) var result = body.meta; 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 chaps = []; var item = { name: result.name, imageUrl: result.poster, link: `${this.source.baseUrl}/${media_type}/${tmdb_id}`, description: result.description, genre: result.genre, }; var link = `${media_type}||${tmdb_id}` if (media_type == "tv") { 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 if (release < dateNow) { 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 { if (release < dateNow) { chaps.push({ name: "Movie", url: link, dateUpload: release.toString(), }) } } item.chapters = chaps; chaps.reverse(); return item; } // 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 = [{ url: url, originalUrl: url, quality: "auto", }]; for (let i = 0; i < lines.length; i++) { if (lines[i].startsWith('#EXT-X-STREAM-INF:')) { 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 = []; var copyStreams = streams.slice() var pref = await this.getPreference("pref_video_resolution"); for (var i in streams) { var stream = streams[i]; if (stream.quality.indexOf(pref) > -1) { sortedStreams.push(stream); var index = copyStreams.indexOf(stream); if (index > -1) { copyStreams.splice(index, 1); } break; } } 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 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 != 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; } } if (streams.length < 1) { throw new Error("Video unavailable"); } if (subtitles.length < 1) { subtitles = await this.getSubtitleList(tmdb, s, e) } streams[0].subtitles = subtitles return await this.sortStreams(streams) } // For manga chapter pages async getPageList() { throw new Error("getPageList not implemented"); } getFilterList() { throw new Error("getFilterList not implemented"); } getSourcePreferences() { return [{ key: 'pref_latest_time_window', listPreference: { title: 'Preferred latest trend time window', summary: '', valueIndex: 0, entries: ["Day", "Week"], entryValues: ["day", "week"] } }, { key: 'pref_video_resolution', listPreference: { title: 'Preferred video resolution', summary: '', valueIndex: 0, entries: ["Auto", "1080p", "720p", "360p"], 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"] } }, ]; } }