From d4ddab83e46035eb585200fc0ee8f8405abc88be Mon Sep 17 00:00:00 2001 From: Stormunblessed <86633626+Stormunblessed@users.noreply.github.com> Date: Sun, 10 Apr 2022 23:06:17 +0000 Subject: [PATCH 01/11] Vidstream fix (#934) --- .../java/com/lagradost/cloudstream3/extractors/WcoStream.kt | 6 +++++- .../java/com/lagradost/cloudstream3/utils/ExtractorApi.kt | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/WcoStream.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/WcoStream.kt index 64d02eed..5f388559 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/WcoStream.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/WcoStream.kt @@ -30,6 +30,10 @@ class VizcloudLive : WcoStream() { override var mainUrl = "https://vizcloud.live" } +class VizcloudInfo : WcoStream() { + override var mainUrl = "https://vizcloud.info" +} + open class WcoStream : ExtractorApi() { override var name = "VidStream" //Cause works for animekisa and wco override var mainUrl = "https://vidstream.pro" @@ -103,7 +107,7 @@ open class WcoStream : ExtractorApi() { } } if (mainUrl == "https://vidstream.pro" || mainUrl == "https://vidstreamz.online" || mainUrl == "https://vizcloud2.online" - || mainUrl == "https://vizcloud.xyz" || mainUrl == "https://vizcloud.live") { + || mainUrl == "https://vizcloud.xyz" || mainUrl == "https://vizcloud.live" || mainUrl == "https://vizcloud.info") { if (it.file.contains("m3u8")) { hlsHelper.m3u8Generation(M3u8Helper.M3u8Stream(it.file.replace("#.mp4",""), null, headers = mapOf("Referer" to url)), true) diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt index 895ba3c1..f46e3b4e 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt @@ -100,6 +100,7 @@ val extractorApis: Array = arrayOf( VizcloudOnline(), VizcloudXyz(), VizcloudLive(), + VizcloudInfo(), Mp4Upload(), StreamTape(), MixDrop(), From a975e5cf4b0119254dad12081f474201a02d34eb Mon Sep 17 00:00:00 2001 From: Zaw <42999156+ImZaw@users.noreply.github.com> Date: Mon, 11 Apr 2022 17:16:22 +0300 Subject: [PATCH 02/11] Fix egybest (#939) * fixes, added quality to SearchResponse * more qualities --- .../java/com/lagradost/cloudstream3/MainAPI.kt | 6 ++++++ .../movieproviders/EgyBestProvider.kt | 17 +++++++++++------ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt index c5c31c60..72091157 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt @@ -587,17 +587,22 @@ fun getQualityFromString(string: String?): SearchQuality? { "cam" -> SearchQuality.Cam "camrip" -> SearchQuality.CamRip "hdcam" -> SearchQuality.HdCam + "hdtc" -> SearchQuality.HdCam + "hdts" -> SearchQuality.HdCam "highquality" -> SearchQuality.HQ "hq" -> SearchQuality.HQ "highdefinition" -> SearchQuality.HD "hdrip" -> SearchQuality.HD "hd" -> SearchQuality.HD + "hdtv" -> SearchQuality.HD "rip" -> SearchQuality.CamRip "telecine" -> SearchQuality.Telecine "tc" -> SearchQuality.Telecine "telesync" -> SearchQuality.Telesync "ts" -> SearchQuality.Telesync "dvd" -> SearchQuality.DVD + "dvdrip" -> SearchQuality.DVD + "dvdscr" -> SearchQuality.DVD "blueray" -> SearchQuality.BlueRay "bluray" -> SearchQuality.BlueRay "br" -> SearchQuality.BlueRay @@ -609,6 +614,7 @@ fun getQualityFromString(string: String?): SearchQuality? { "wp" -> SearchQuality.WorkPrint "workprint" -> SearchQuality.WorkPrint "webrip" -> SearchQuality.WebRip + "webdl" -> SearchQuality.WebRip "web" -> SearchQuality.WebRip "hdr" -> SearchQuality.HDR "sdr" -> SearchQuality.SDR diff --git a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/EgyBestProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/EgyBestProvider.kt index 2130eaf9..fb98857b 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/EgyBestProvider.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/EgyBestProvider.kt @@ -8,7 +8,7 @@ import org.jsoup.nodes.Element class EgyBestProvider : MainAPI() { override val lang = "ar" - override var mainUrl = "https://egy.best" + override var mainUrl = "https://www.egy.best" override var name = "EgyBest" override val usesWebView = false override val hasMainPage = true @@ -26,6 +26,7 @@ class EgyBestProvider : MainAPI() { val isMovie = Regex(".*/movie/.*|.*/masrahiya/.*").matches(url) val tvType = if (isMovie) TvType.Movie else TvType.TvSeries title = if (year !== null) title else title.split(" (")[0].trim() + val quality = select("span.ribbon span").text().replace("-", "") // If you need to differentiate use the url. return MovieSearchResponse( title, @@ -35,18 +36,22 @@ class EgyBestProvider : MainAPI() { posterUrl, year, null, + quality = getQualityFromString(quality) ) } override suspend fun getMainPage(): HomePageResponse { // url, title val doc = app.get(mainUrl).document - val pages = doc.select("#mainLoad div.mbox").apmap { + val pages = arrayListOf() + doc.select("#mainLoad div.mbox").apmap { val name = it.select(".bdb.pda > strong").text() - val list = it.select(".movie").mapNotNull { element -> - element.toSearchResponse() + if (it.select(".movie").first().attr("href").contains("season-(.....)|ep-(.....)".toRegex())) return@apmap + val list = arrayListOf() + it.select(".movie").map { element -> + list.add(element.toSearchResponse()!!) } - HomePageList(name, list) + pages.add(HomePageList(name, list)) } return HomePageResponse(pages) } @@ -72,7 +77,7 @@ class EgyBestProvider : MainAPI() { val isMovie = Regex(".*/movie/.*|.*/masrahiya/.*").matches(url) val posterUrl = doc.select("div.movie_img a img")?.attr("src") val year = doc.select("div.movie_title h1 a")?.text()?.toIntOrNull() - val title = doc.select("div.movie_title h1 span[itemprop=\"name\"]").text() + val title = doc.select("div.movie_title h1 span").text() val synopsis = doc.select("div.mbox").firstOrNull { it.text().contains("القصة") From bf81258f779a427f7d1b52cd744b1bb73caeaa81 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Mon, 11 Apr 2022 14:16:39 +0000 Subject: [PATCH 03/11] chore(docs): update list of sites --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 97375c8a..1bdb95cb 100644 --- a/README.md +++ b/README.md @@ -90,7 +90,7 @@ https://lagradost.github.io/CloudStream-3/ - [kdramahood.com](https://kdramahood.com) - [akwam.to](https://akwam.to) - [mycima.tv](https://mycima.tv) -- [egy.best](https://egy.best) +- [egy.best](https://www.egy.best) - [hdm.to](https://hdm.to) - [theflix.to](https://theflix.to) - [v2.apimdb.net](https://v2.apimdb.net) From 06b778eac29f60ac8882638f450354d47d8f4795 Mon Sep 17 00:00:00 2001 From: Sarlay <60151189+Sarlay@users.noreply.github.com> Date: Mon, 11 Apr 2022 22:17:34 +0200 Subject: [PATCH 04/11] Added Nginx Provider and Nginx settings (#932) * Added Nginx Provider and Nginx settings * fixed some stuff * updated NginxProvider to use latest newEpisode method * hide nginx provider when no url specified * fixed some stuff * removed useless credentials variable * added url to my website * fixed tabulation Co-authored-by: sarlay --- .../com/lagradost/cloudstream3/MainAPI.kt | 5 + .../lagradost/cloudstream3/MainActivity.kt | 78 ++++- .../movieproviders/NginxProvider.kt | 268 ++++++++++++++++++ .../ui/settings/SettingsFragment.kt | 27 ++ .../utils/SingleSelectionHelper.kt | 65 ++++- .../main/res/layout/bottom_input_dialog.xml | 62 ++++ app/src/main/res/values/strings.xml | 11 + app/src/main/res/xml/settings.xml | 25 ++ 8 files changed, 534 insertions(+), 7 deletions(-) create mode 100644 app/src/main/java/com/lagradost/cloudstream3/movieproviders/NginxProvider.kt create mode 100644 app/src/main/res/layout/bottom_input_dialog.xml diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt index 72091157..d9cfc62b 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt @@ -19,6 +19,7 @@ import com.lagradost.cloudstream3.syncproviders.OAuth2API.Companion.malApi import com.lagradost.cloudstream3.ui.player.SubtitleData import com.lagradost.cloudstream3.utils.AppUtils.toJson import com.lagradost.cloudstream3.utils.ExtractorLink +import okhttp3.Headers import okhttp3.Interceptor import java.text.SimpleDateFormat import java.util.* @@ -107,6 +108,7 @@ object APIHolder { MonoschinosProvider(), KawaiifuProvider(), // disabled due to cloudflare //MultiAnimeProvider(), + NginxProvider(), ) } @@ -303,6 +305,7 @@ const val PROVIDER_STATUS_DOWN = 0 data class ProvidersInfoJson( @JsonProperty("name") var name: String, @JsonProperty("url") var url: String, + @JsonProperty("credentials") var credentials: String? = null, @JsonProperty("status") var status: Int, ) @@ -315,6 +318,7 @@ abstract class MainAPI { fun overrideWithNewData(data: ProvidersInfoJson) { this.name = data.name this.mainUrl = data.url + this.storedCredentials = data.credentials } init { @@ -325,6 +329,7 @@ abstract class MainAPI { open var name = "NONE" open var mainUrl = "NONE" + open var storedCredentials: String? = null //open val uniqueId : Int by lazy { this.name.hashCode() } // in case of duplicate providers you can have a shared id diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt index a43d4741..015a5004 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt @@ -64,12 +64,14 @@ import com.lagradost.cloudstream3.utils.UIHelper.getResourceColor import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard import com.lagradost.cloudstream3.utils.UIHelper.navigate import com.lagradost.cloudstream3.utils.UIHelper.requestRW +import com.lagradost.cloudstream3.movieproviders.NginxProvider import kotlinx.android.synthetic.main.activity_main.* import kotlinx.android.synthetic.main.fragment_result_swipe.* import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext import java.io.File +import kotlin.collections.HashMap import kotlin.concurrent.thread @@ -360,6 +362,57 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { e.printStackTrace() false } + fun addNginxToJson(data: java.util.HashMap): java.util.HashMap? { + try { + val settingsManager = PreferenceManager.getDefaultSharedPreferences(this) + val nginxUrl = + settingsManager.getString(getString(R.string.nginx_url_key), "nginx_url_key").toString() + val nginxCredentials = + settingsManager.getString(getString(R.string.nginx_credentials), "nginx_credentials") + .toString() + val StoredNginxProvider = NginxProvider() + if (nginxUrl == "nginx_url_key" || nginxUrl == "") { // if key is default value, or empty: + data[StoredNginxProvider.javaClass.simpleName] = ProvidersInfoJson( + url = nginxUrl, + name = StoredNginxProvider.name, + status = PROVIDER_STATUS_DOWN, // the provider will not be display + credentials = nginxCredentials + ) + } else { // valid url + data[StoredNginxProvider.javaClass.simpleName] = ProvidersInfoJson( + url = nginxUrl, + name = StoredNginxProvider.name, + status = PROVIDER_STATUS_OK, + credentials = nginxCredentials + ) + } + + return data + } catch (e: Exception) { + logError(e) + return data + } + } + fun createNginxJson() : ProvidersInfoJson? { //java.util.HashMap + return try { + val settingsManager = PreferenceManager.getDefaultSharedPreferences(this) + val nginxUrl = settingsManager.getString(getString(R.string.nginx_url_key), "nginx_url_key").toString() + val nginxCredentials = settingsManager.getString(getString(R.string.nginx_credentials), "nginx_credentials").toString() + if (nginxUrl == "nginx_url_key" || nginxUrl == "") { // if key is default value or empty: + null // don't overwrite anything + } else { + ProvidersInfoJson( + url = nginxUrl, + name = NginxProvider().name, + status = PROVIDER_STATUS_OK, + credentials = nginxCredentials + ) + } + } catch (e: Exception) { + logError(e) + null + } + } // this pulls the latest data so ppl don't have to update to simply change provider url if (downloadFromGithub) { @@ -379,8 +432,11 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { tryParseJson>(txt) setKey(PROVIDER_STATUS_KEY, txt) MainAPI.overrideData = newCache // update all new providers - for (api in apis) { // update current providers - newCache?.get(api.javaClass.simpleName)?.let { data -> + + val newUpdatedCache = newCache?.let { addNginxToJson(it) ?: it } + + for (api in apis) { // update current providers + newUpdatedCache?.get(api.javaClass.simpleName)?.let { data -> api.overrideWithNewData(data) } } @@ -397,12 +453,13 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { newCache }?.let { providersJsonMap -> MainAPI.overrideData = providersJsonMap + val providersJsonMapUpdated = addNginxToJson(providersJsonMap)?: providersJsonMap // if return null, use unchanged one val acceptableProviders = - providersJsonMap.filter { it.value.status == PROVIDER_STATUS_OK || it.value.status == PROVIDER_STATUS_SLOW } + providersJsonMapUpdated.filter { it.value.status == PROVIDER_STATUS_OK || it.value.status == PROVIDER_STATUS_SLOW } .map { it.key }.toSet() val restrictedApis = - if (hasBenene) providersJsonMap.filter { it.value.status == PROVIDER_STATUS_BETA_ONLY } + if (hasBenene) providersJsonMapUpdated.filter { it.value.status == PROVIDER_STATUS_BETA_ONLY } .map { it.key }.toSet() else emptySet() apis = allProviders.filter { api -> @@ -425,6 +482,17 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { } } else { apis = allProviders + try { + val nginxProviderName = NginxProvider().name + val nginxProviderIndex = apis.indexOf(APIHolder.getApiFromName(nginxProviderName)) + val createdJsonProvider = createNginxJson() + if (createdJsonProvider != null) { + apis[nginxProviderIndex].overrideWithNewData(createdJsonProvider) // people will have access to it if they disable metadata check (they are not filtered) + } + } catch (e: Exception) { + logError(e) + } + } loadThemes(this) @@ -619,4 +687,4 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { val output = src?.doMath() println("MASTER OUTPUT = $output")*/ } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/NginxProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/NginxProvider.kt new file mode 100644 index 00000000..ce62f013 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/NginxProvider.kt @@ -0,0 +1,268 @@ +package com.lagradost.cloudstream3.movieproviders + +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.LoadResponse.Companion.addRating +import com.lagradost.cloudstream3.utils.ExtractorLink +import com.lagradost.cloudstream3.TvType +import com.lagradost.cloudstream3.app +import com.lagradost.cloudstream3.utils.Qualities +import java.lang.Exception + +class NginxProvider : MainAPI() { + override var name = "Nginx" + override val hasQuickSearch = false + override val hasMainPage = true + override val supportedTypes = setOf(TvType.AnimeMovie, TvType.TvSeries, TvType.Movie) + + + + fun getAuthHeader(storedCredentials: String?): Map { + if (storedCredentials == null) { + return mapOf(Pair("Authorization", "Basic ")) // no Authorization headers + } + val basicAuthToken = base64Encode(storedCredentials.toByteArray()) // will this be loaded when not using the provider ??? can increase load + return mapOf(Pair("Authorization", "Basic $basicAuthToken")) + } + + override suspend fun load(url: String): LoadResponse { + val authHeader = getAuthHeader(storedCredentials) // call again because it isn't reloaded if in main class and storedCredentials loads after + // url can be tvshow.nfo for series or mediaRootUrl for movies + + val mediaRootDocument = app.get(url, authHeader).document + + val nfoUrl = url + mediaRootDocument.getElementsByAttributeValueContaining("href", ".nfo").attr("href") // metadata url file + + val metadataDocument = app.get(nfoUrl, authHeader).document // get the metadata nfo file + + val isMovie = !nfoUrl.contains("tvshow.nfo") + + val title = metadataDocument.selectFirst("title").text() + + val description = metadataDocument.selectFirst("plot").text() + + if (isMovie) { + val poster = metadataDocument.selectFirst("thumb").text() + val trailer = metadataDocument.select("trailer")?.mapNotNull { + it?.text()?.replace( + "plugin://plugin.video.youtube/play/?video_id=", + "https://www.youtube.com/watch?v=" + ) + } + val partialUrl = mediaRootDocument.getElementsByAttributeValueContaining("href", ".nfo").attr("href").replace(".nfo", ".") + val date = metadataDocument.selectFirst("year")?.text()?.toIntOrNull() + val ratingAverage = metadataDocument.selectFirst("value")?.text()?.toIntOrNull() + val tagsList = metadataDocument.select("genre") + ?.mapNotNull { // all the tags like action, thriller ... + it?.text() + + } + + + val dataList = mediaRootDocument.getElementsByAttributeValueContaining( // list of all urls of the webpage + "href", + partialUrl + ) + + val data = url + dataList.firstNotNullOf { item -> item.takeIf { (!it.attr("href").contains(".nfo") && !it.attr("href").contains(".jpg"))} }.attr("href").toString() // exclude poster and nfo (metadata) file + + + return MovieLoadResponse( + title, + data, + this.name, + TvType.Movie, + data, + poster, + date, + description, + ratingAverage, + tagsList, + null, + trailer, + null, + null, + ) + } else // a tv serie + { + + val list = ArrayList>() + val mediaRootUrl = url.replace("tvshow.nfo", "") + val posterUrl = mediaRootUrl + "poster.jpg" + val mediaRootDocument = app.get(mediaRootUrl, authHeader).document + val seasons = + mediaRootDocument.getElementsByAttributeValueContaining("href", "Season%20") + + + val tagsList = metadataDocument.select("genre") + ?.mapNotNull { // all the tags like action, thriller ...; unused variable + it?.text() + } + + //val actorsList = document.select("actor") + // ?.mapNotNull { // all the tags like action, thriller ...; unused variable + // it?.text() + // } + + seasons.forEach { element -> + val season = + element.attr("href")?.replace("Season%20", "")?.replace("/", "")?.toIntOrNull() + val href = mediaRootUrl + element.attr("href") + if (season != null && season > 0 && href.isNotBlank()) { + list.add(Pair(season, href)) + } + } + + if (list.isEmpty()) throw ErrorLoadingException("No Seasons Found") + + val episodeList = ArrayList() + + + list.apmap { (seasonInt, seasonString) -> + val seasonDocument = app.get(seasonString, authHeader).document + val episodes = seasonDocument.getElementsByAttributeValueContaining( + "href", + ".nfo" + ) // get metadata + episodes.forEach { episode -> + val nfoDocument = app.get(seasonString + episode.attr("href"), authHeader).document // get episode metadata file + val epNum = nfoDocument.selectFirst("episode")?.text()?.toIntOrNull() + val poster = + seasonString + episode.attr("href").replace(".nfo", "-thumb.jpg") + val name = nfoDocument.selectFirst("title").text() + // val seasonInt = nfoDocument.selectFirst("season").text().toIntOrNull() + val date = nfoDocument.selectFirst("aired")?.text() + val plot = nfoDocument.selectFirst("plot")?.text() + + val dataList = seasonDocument.getElementsByAttributeValueContaining( + "href", + episode.attr("href").replace(".nfo", "") + ) + val data = seasonString + dataList.firstNotNullOf { item -> item.takeIf { (!it.attr("href").contains(".nfo") && !it.attr("href").contains(".jpg"))} }.attr("href").toString() // exclude poster and nfo (metadata) file + + episodeList.add( + newEpisode(data) { + this.name = name + this.season = seasonInt + this.episode = epNum + this.posterUrl = poster + addDate(date) + this.description = plot + } + ) + } + } + return newTvSeriesLoadResponse(title, url, TvType.TvSeries, episodeList) { + this.name = title + this.url = url + this.posterUrl = posterUrl + this.episodes = episodeList + this.plot = description + this.tags = tagsList + } + } + + } + + + override suspend fun loadLinks( + data: String, + isCasting: Boolean, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ): Boolean { + // loadExtractor(data, null) { callback(it.copy(headers=authHeader)) } + val authHeader = getAuthHeader(storedCredentials) // call again because it isn't reloaded if in main class and storedCredentials loads after + callback.invoke ( + ExtractorLink( + name, + name, + data, + data, // referer not needed + Qualities.Unknown.value, + false, + authHeader, + ) + ) + + return true + } + + + + override suspend fun getMainPage(): HomePageResponse? { + val authHeader = getAuthHeader(storedCredentials) // call again because it isn't reloaded if in main class and storedCredentials loads after + if (mainUrl == "NONE"){ + throw ErrorLoadingException("No nginx url specified in the settings: Nginx Settigns > Nginx server url, try again in a few seconds") + } + val document = app.get(mainUrl, authHeader).document + val categories = document.select("a") + val returnList = categories.mapNotNull { + val categoryPath = mainUrl + it.attr("href") ?: return@mapNotNull null // get the url of the category; like http://192.168.1.10/media/Movies/ + val categoryTitle = it.text() // get the category title like Movies or Series + if (categoryTitle != "../" && categoryTitle != "Music/") { // exclude parent dir and Music dir + val categoryDocument = app.get(categoryPath, authHeader).document // queries the page http://192.168.1.10/media/Movies/ + val contentLinks = categoryDocument.select("a") + val currentList = contentLinks.mapNotNull { head -> + if (head.attr("href") != "../") { + try { + val mediaRootUrl = + categoryPath + head.attr("href")// like http://192.168.1.10/media/Series/Chernobyl/ + val mediaDocument = app.get(mediaRootUrl, authHeader).document + val nfoFilename = mediaDocument.getElementsByAttributeValueContaining( + "href", + ".nfo" + )[0].attr("href") + val isMovieType = nfoFilename != "tvshow.nfo" + val nfoPath = + mediaRootUrl + nfoFilename // must exist or will raise errors, only the first one is taken + val nfoContent = + app.get(nfoPath, authHeader).document // all the metadata + + + if (isMovieType) { + val movieName = nfoContent.select("title").text() + + val posterUrl = mediaRootUrl + "poster.jpg" + + return@mapNotNull MovieSearchResponse( + movieName, + mediaRootUrl, + this.name, + TvType.Movie, + posterUrl, + null, + ) + } else { // tv serie + val serieName = nfoContent.select("title").text() + + val posterUrl = mediaRootUrl + "poster.jpg" + + TvSeriesSearchResponse( + serieName, + nfoPath, + this.name, + TvType.TvSeries, + posterUrl, + null, + null, + ) + + + } + } catch (e: Exception) { // can cause issues invisible errors + null + //logError(e) // not working because it changes the return type of currentList to Any + } + + + } else null + } + if (currentList.isNotEmpty() && categoryTitle != "../") { // exclude upper dir + HomePageList(categoryTitle, currentList) + } else null + } else null // the path is ../ which is parent directory + } + // if (returnList.isEmpty()) return null // maybe doing nothing idk + return HomePageResponse(returnList) + } +} diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragment.kt index 4bad5c57..cb7f1138 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragment.kt @@ -47,6 +47,7 @@ import com.lagradost.cloudstream3.utils.InAppUpdater.Companion.runAutoUpdate import com.lagradost.cloudstream3.utils.Qualities import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showDialog +import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showNginxTextInputDialog import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showMultiDialog import com.lagradost.cloudstream3.utils.SubtitleHelper import com.lagradost.cloudstream3.utils.SubtitleHelper.getFlagFromIso @@ -485,6 +486,32 @@ class SettingsFragment : PreferenceFragmentCompat() { return@setOnPreferenceClickListener true } + getPref(R.string.nginx_url_key)?.setOnPreferenceClickListener { + + activity?.showNginxTextInputDialog( + settingsManager.getString(getString(R.string.nginx_url_pref), "Nginx server url").toString(), + settingsManager.getString(getString(R.string.nginx_url_key), "").toString(), // key: the actual you use rn + android.text.InputType.TYPE_TEXT_VARIATION_URI, // uri + {}) { + settingsManager.edit() + .putString(getString(R.string.nginx_url_key), it).apply() // change the stored url in nginx_url_key to it + } + return@setOnPreferenceClickListener true + } + + getPref(R.string.nginx_credentials)?.setOnPreferenceClickListener { + + activity?.showNginxTextInputDialog( + settingsManager.getString(getString(R.string.nginx_credentials_title), "Nginx Credentials").toString(), + settingsManager.getString(getString(R.string.nginx_credentials), "").toString(), // key: the actual you use rn + android.text.InputType.TYPE_TEXT_VARIATION_URI, + {}) { + settingsManager.edit() + .putString(getString(R.string.nginx_credentials), it).apply() // change the stored url in nginx_url_key to it + } + return@setOnPreferenceClickListener true + } + getPref(R.string.prefer_media_type_key)?.setOnPreferenceClickListener { val prefNames = resources.getStringArray(R.array.media_type_pref) val prefValues = resources.getIntArray(R.array.media_type_pref_values) diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/SingleSelectionHelper.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/SingleSelectionHelper.kt index ce49bac1..0a069cec 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/SingleSelectionHelper.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/SingleSelectionHelper.kt @@ -144,6 +144,46 @@ object SingleSelectionHelper { } } + + + private fun Activity.showInputDialog( + dialog: Dialog, + value: String, + name: String, + textInputType: Int?, + callback: (String) -> Unit, + dismissCallback: () -> Unit + ) { + val inputView = dialog.findViewById(R.id.nginx_text_input)!! + val textView = dialog.findViewById(R.id.text1)!! + val applyButton = dialog.findViewById(R.id.apply_btt)!! + val cancelButton = dialog.findViewById(R.id.cancel_btt)!! + val applyHolder = dialog.findViewById(R.id.apply_btt_holder)!! + + applyHolder.isVisible = true + textView.text = name + + if (textInputType != null) { + inputView.inputType = textInputType // 16 for website url input type + } + inputView.setText(value, TextView.BufferType.EDITABLE) + + + applyButton.setOnClickListener { + callback.invoke(inputView.text.toString()) // try to save the setting, using callback + dialog.dismissSafe(this) + } + + cancelButton.setOnClickListener { // just dismiss + dialog.dismissSafe(this) + } + + dialog.setOnDismissListener { + dismissCallback.invoke() + } + + } + fun Activity.showMultiDialog( items: List, selectedIndex: List, @@ -192,7 +232,7 @@ object SingleSelectionHelper { selectedIndex: Int, name: String, showApply: Boolean, - dismissCallback: () -> Unit, + dismissCallback: () -> Unit, callback: (Int) -> Unit, ) { val builder = @@ -211,4 +251,25 @@ object SingleSelectionHelper { dismissCallback ) } -} \ No newline at end of file + + fun Activity.showNginxTextInputDialog( + name: String, + value: String, + textInputType: Int?, + dismissCallback: () -> Unit, + callback: (String) -> Unit, + ) { + val builder = BottomSheetDialog(this) // probably the stuff at the bottom + builder.setContentView(R.layout.bottom_input_dialog) // input layout + + builder.show() + showInputDialog( + builder, + value, + name, + textInputType, // type is a uri + callback, + dismissCallback + ) + } +} diff --git a/app/src/main/res/layout/bottom_input_dialog.xml b/app/src/main/res/layout/bottom_input_dialog.xml new file mode 100644 index 00000000..c7755b9e --- /dev/null +++ b/app/src/main/res/layout/bottom_input_dialog.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8d026710..eb644776 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -31,6 +31,9 @@ provider_lang_key dns_key download_path_key + nginx_url_key + nginx_credentials + nginx_info Cloudstream app_layout_key primary_color_key @@ -227,6 +230,12 @@ Error backing up %s Search + Nginx Settings + Nginx Credential + You have to use the following format mycoolusername:mysecurepassword123 + What is Nginx ? + Nginx is a software that can be used to display files from a server that you own. Click to see a Nginx setup guide + Info Advanced Search Gives you the search results separated by provider @@ -352,6 +361,8 @@ Download path + Nginx server url + Display Dubbed/Subbed Anime Fit to screen diff --git a/app/src/main/res/xml/settings.xml b/app/src/main/res/xml/settings.xml index 5552b587..c73765a6 100644 --- a/app/src/main/res/xml/settings.xml +++ b/app/src/main/res/xml/settings.xml @@ -162,6 +162,31 @@ app:defaultValue="true" /> + + + + + + + + + Date: Tue, 12 Apr 2022 18:24:16 +0700 Subject: [PATCH 05/11] Fixed MAL login when user does not set MAL photo (#942) --- .../lagradost/cloudstream3/syncproviders/providers/MALApi.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/MALApi.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/MALApi.kt index 66fe3db9..5853ad94 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/MALApi.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/MALApi.kt @@ -663,7 +663,7 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { @JsonProperty("name") val name: String, @JsonProperty("location") val location: String, @JsonProperty("joined_at") val joined_at: String, - @JsonProperty("picture") val picture: String, + @JsonProperty("picture") val picture: String?, ) data class MalMainPicture( @@ -694,4 +694,4 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { val id: Int, val name: String, ) -} \ No newline at end of file +} From d6d4bfaa0ea17a4f4755a05f0124879669cfd498 Mon Sep 17 00:00:00 2001 From: Jace <54625750+Jacekun@users.noreply.github.com> Date: Tue, 12 Apr 2022 20:23:30 +0800 Subject: [PATCH 06/11] [Feature] Add setting to Truncate Video title on Player (#918) * [Feature] Add setting to Truncate Video title on Player * add missing icons * Revert "add missing icons" This reverts commit 51c8a85ed18b5664fde21262f5b8ee3ae3a894fb. * applied PR suggestions * edit color --- .../cloudstream3/ui/player/GeneratorPlayer.kt | 21 +++++++++++++++---- .../ui/settings/SettingsFragment.kt | 18 ++++++++++++++++ .../drawable/ic_baseline_text_format_24.xml | 10 +++++++++ app/src/main/res/values/array.xml | 15 +++++++++++++ app/src/main/res/values/strings.xml | 2 ++ app/src/main/res/xml/settings.xml | 4 ++++ 6 files changed, 66 insertions(+), 4 deletions(-) create mode 100644 app/src/main/res/drawable/ic_baseline_text_format_24.xml diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt index c82d3788..f340f234 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt @@ -545,10 +545,14 @@ class GeneratorPlayer : FullScreenPlayer() { tvType = meta.tvType } } - - player_episode_filler_holder?.isVisible = isFiller ?: false - - player_video_title?.text = if (headerName != null) { + //Get limit of characters on Video Title + var limitTitle = 0 + context?.let { + val settingsManager = PreferenceManager.getDefaultSharedPreferences(it) + limitTitle = settingsManager.getInt(getString(R.string.prefer_limit_title_key), 0) + } + //Generate video title + var playerVideoTitle = if (headerName != null) { (headerName + if (tvType.isEpisodeBased() && episode != null) if (season == null) @@ -559,6 +563,15 @@ class GeneratorPlayer : FullScreenPlayer() { } else { "" } + //Truncate video title if it exceeds limit + val differenceInLength = playerVideoTitle.length - limitTitle + val margin = 3 //If the difference is smaller than or equal to this value, ignore it + if (limitTitle > 0 && differenceInLength > margin) { + playerVideoTitle = playerVideoTitle.substring(0, limitTitle-1) + "..." + } + + player_episode_filler_holder?.isVisible = isFiller ?: false + player_video_title?.text = playerVideoTitle } @SuppressLint("SetTextI18n") diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragment.kt index cb7f1138..90ca1acb 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragment.kt @@ -705,6 +705,24 @@ class SettingsFragment : PreferenceFragmentCompat() { return@setOnPreferenceClickListener true } + getPref(R.string.prefer_limit_title_key)?.setOnPreferenceClickListener { + val prefNames = resources.getStringArray(R.array.limit_title_pref_names) + val prefValues = resources.getIntArray(R.array.limit_title_pref_values) + val current = settingsManager.getInt(getString(R.string.prefer_limit_title_key), 0) + + activity?.showBottomDialog( + prefNames.toList(), + prefValues.indexOf(current), + getString(R.string.limit_title), + true, + {}) { + settingsManager.edit() + .putInt(getString(R.string.prefer_limit_title_key), prefValues[it]) + .apply() + } + return@setOnPreferenceClickListener true + } + getPref(R.string.dns_key)?.setOnPreferenceClickListener { val prefNames = resources.getStringArray(R.array.dns_pref) val prefValues = resources.getIntArray(R.array.dns_pref_values) diff --git a/app/src/main/res/drawable/ic_baseline_text_format_24.xml b/app/src/main/res/drawable/ic_baseline_text_format_24.xml new file mode 100644 index 00000000..ca03854f --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_text_format_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/values/array.xml b/app/src/main/res/values/array.xml index 8cd09501..e8795592 100644 --- a/app/src/main/res/values/array.xml +++ b/app/src/main/res/values/array.xml @@ -59,6 +59,21 @@ 3 + + @string/none + 16 characters + 32 characters + 64 characters + 128 characters + + + 0 + 16 + 32 + 64 + 128 + + @string/automatic 1min diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index eb644776..8d355814 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -13,6 +13,7 @@ subtitle_settings_key subtitle_settings_chromecast_key quality_pref_key + prefer_limit_title_key video_buffer_size_key video_buffer_length_key video_buffer_clear_key @@ -348,6 +349,7 @@ Don\'t show again Update Preferred watch quality + Limit title characters on player Video buffer size Video buffer length Video cache on disk diff --git a/app/src/main/res/xml/settings.xml b/app/src/main/res/xml/settings.xml index c73765a6..e70d7ad9 100644 --- a/app/src/main/res/xml/settings.xml +++ b/app/src/main/res/xml/settings.xml @@ -20,6 +20,10 @@ android:key="@string/quality_pref_key" android:title="@string/watch_quality_pref" android:icon="@drawable/ic_baseline_hd_24" /> + Date: Tue, 12 Apr 2022 17:20:16 +0100 Subject: [PATCH 07/11] Vidstream Fix (#944) * Attempt Uno * Attempt Uno Pt2 --- .../com/lagradost/cloudstream3/extractors/WcoStream.kt | 9 +++++++-- .../com/lagradost/cloudstream3/utils/ExtractorApi.kt | 1 + 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/WcoStream.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/WcoStream.kt index 5f388559..4ab037d9 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/WcoStream.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/WcoStream.kt @@ -34,6 +34,10 @@ class VizcloudInfo : WcoStream() { override var mainUrl = "https://vizcloud.info" } +class MwvnVizcloudInfo : WcoStream() { + override var mainUrl = "https://mwvn.vizcloud.info" +} + open class WcoStream : ExtractorApi() { override var name = "VidStream" //Cause works for animekisa and wco override var mainUrl = "https://vidstream.pro" @@ -107,8 +111,9 @@ open class WcoStream : ExtractorApi() { } } if (mainUrl == "https://vidstream.pro" || mainUrl == "https://vidstreamz.online" || mainUrl == "https://vizcloud2.online" - || mainUrl == "https://vizcloud.xyz" || mainUrl == "https://vizcloud.live" || mainUrl == "https://vizcloud.info") { - if (it.file.contains("m3u8")) { + || mainUrl == "https://vizcloud.xyz" || mainUrl == "https://vizcloud.live" || mainUrl == "https://vizcloud.info" + || mainUrl == "https://mwvn.vizcloud.info") { + if (it.file.contains("m3u8")) { hlsHelper.m3u8Generation(M3u8Helper.M3u8Stream(it.file.replace("#.mp4",""), null, headers = mapOf("Referer" to url)), true) .forEach { stream -> diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt index f46e3b4e..febb99c4 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt @@ -101,6 +101,7 @@ val extractorApis: Array = arrayOf( VizcloudXyz(), VizcloudLive(), VizcloudInfo(), + MwvnVizcloudInfo(), Mp4Upload(), StreamTape(), MixDrop(), From e05f8d0f3af13fa34a94e72d21cc82cd61df8eb8 Mon Sep 17 00:00:00 2001 From: GustavoASantos <64141391+GustavoASantos@users.noreply.github.com> Date: Tue, 12 Apr 2022 17:20:30 +0100 Subject: [PATCH 08/11] Made "None" string in the DNS setting translatable (#943) --- app/src/main/res/values/array.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values/array.xml b/app/src/main/res/values/array.xml index e8795592..ff244c9d 100644 --- a/app/src/main/res/values/array.xml +++ b/app/src/main/res/values/array.xml @@ -32,7 +32,7 @@ - None + @string/none Google Cloudflare From 1db8f9839130d0a5a6f29cf9ca9de27dfae6e03f Mon Sep 17 00:00:00 2001 From: Cloudburst <18114966+C10udburst@users.noreply.github.com> Date: Tue, 12 Apr 2022 18:48:50 +0200 Subject: [PATCH 09/11] site list changes (#872) * site list changes - made site-list.json automatically create providers.json - removed site list from readme (now shows a link) - remove jquery dependency and split src into multiple files * update workflow file * oops wrong number * fix invalid regex * status 1 * revert providers.json --- .github/site-list.py | 77 +++++++++----------- .github/workflows/site_list.yml | 4 +- README.md | 52 +------------- docs/index.html | 120 +------------------------------- docs/script.js | 49 +++++++++++++ docs/style.css | 49 +++++++++++++ 6 files changed, 139 insertions(+), 212 deletions(-) create mode 100644 docs/script.js create mode 100644 docs/style.css diff --git a/.github/site-list.py b/.github/site-list.py index 450767c6..d6a13b58 100644 --- a/.github/site-list.py +++ b/.github/site-list.py @@ -1,63 +1,54 @@ #!/usr/bin/python3 from glob import glob -from re import findall, compile, sub, DOTALL +from re import findall, compile, DOTALL +from json import dump, load from typing import List, Dict # Globals URL_REGEX = compile( "override\sva[lr]\smainUrl[^\"']+[\"'](https?://[a-zA-Z0-9\.-]+)[\"']") -NAME_REGEX = compile("class (.+?) ?: \w+\(\)\s\{") -START_MARKER = "" -END_MARKER = "" +NAME_REGEX = compile("([A-Za-z0-9]+)(?:.kt)$") +JSON_PATH = "docs/providers.json" GLOB = "app/src/main/java/com/lagradost/cloudstream3/*providers/*Provider.kt" -MAIN_API = "app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt" -API_REGEX = compile( - "val\s*allProviders.*?{\s.*?arrayListOf\(([\W\w]*?)\)\s*\n*\s*}", DOTALL) -sites: Dict[str, str] = {} -enabled_sites: List[str] = [] - - -with open(MAIN_API, "r", encoding="utf-8") as f: - apis = findall(API_REGEX, f.read()) - for api_list in apis: - for api in api_list.split("\n"): - if not api.strip() or api.strip().startswith("/"): - continue - enabled_sites.append(api.strip().split("(")[0]) +old_sites: Dict[str, Dict] = load(open(JSON_PATH, "r", encoding="utf-8")) +sites: Dict[str, Dict] = {} +# parse all *Provider.kt files for path in glob(GLOB): with open(path, "r", encoding='utf-8') as file: try: site_text: str = file.read() - name: List[str] = findall(NAME_REGEX, site_text) - provider_text: str = findall(URL_REGEX, site_text) + name: str = findall(NAME_REGEX, path)[0] + provider_url: str = [*findall(URL_REGEX, site_text), ""][0] - if name: - if name[0] not in enabled_sites: - continue - sites[name[0]] = provider_text[0] + if name in old_sites.keys(): # if already in previous list use old status and name + sites[name] = { + "name": old_sites[name]['name'], + "url": provider_url if provider_url else old_sites[name]['url'], + "status": old_sites[name]['status'] + } + else: # if not in previous list add with new data + display_name = name + if display_name.endswith("Provider"): + display_name = display_name[:-len("Provider")] + sites[name] = { + "name": display_name, + "url": provider_url if provider_url else "", + "status": 1 + } except Exception as ex: print("{0}: {1}".format(path, ex)) + +# add sites from old_sites that are missing in new list +for name in old_sites.keys(): + if name not in sites.keys(): + sites[name] = { + "name": old_sites[name]['name'], + "url": old_sites[name]['url'], + "status": old_sites[name]['status'] + } - -with open("README.md", "r+", encoding='utf-8') as readme: - raw = readme.read() - if START_MARKER not in raw or END_MARKER not in raw: - raise RuntimeError("Missing start and end markers") - readme.seek(0) - - readme.write(raw.split(START_MARKER)[0]) - readme.write(START_MARKER+"\n") - - for site in enabled_sites: - if site in sites: - readme.write( - "- [{0}]({1}) \n".format(sub("^https?://(?:www\.)?", "", sites[site]), sites[site])) - - readme.write(END_MARKER) - readme.write(raw.split(END_MARKER)[-1]) - - readme.truncate() +dump(sites, open(JSON_PATH, "w+", encoding="utf-8"), indent=4, sort_keys=True) diff --git a/.github/workflows/site_list.yml b/.github/workflows/site_list.yml index 81a9dc1a..ab2d30dd 100644 --- a/.github/workflows/site_list.yml +++ b/.github/workflows/site_list.yml @@ -4,10 +4,10 @@ on: push: branches: [ master ] paths: - - 'README.md' - 'app/src/main/java/com/lagradost/cloudstream3/*providers/*Provider.kt' - '.github/workflows/site_list.yml' - '.github/site-list.py' + - 'docs/providers.json' - 'app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt' concurrency: @@ -19,7 +19,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - name: Edit README.md + - name: Edit providers.json run: | python3 .github/site-list.py - name: Commit to the repo diff --git a/README.md b/README.md index 1bdb95cb..2d42a363 100644 --- a/README.md +++ b/README.md @@ -58,53 +58,5 @@ It merely scrapes 3rd-party websites that are publicly accessable via any regula ***Sites used:*** -https://lagradost.github.io/CloudStream-3/ - - -- [pelisplus.icu](https://pelisplus.icu) -- [pelismart.com](https://pelismart.com) -- [melomovie.com](https://melomovie.com) -- [doramasyt.com](https://doramasyt.com) -- [cuevana3.me](https://cuevana3.me) -- [pelisflix.li](https://pelisflix.li) -- [seriesflix.video](https://seriesflix.video) -- [ihavenotv.com](https://ihavenotv.com) -- [lookmovie.io](https://lookmovie.io) -- [vmovee.watch](https://www.vmovee.watch) -- [allmoviesforyou.net](https://allmoviesforyou.net) -- [vidembed.cc](https://vidembed.cc) -- [vf-film.me](https://vf-film.me) -- [vf-serie.org](https://vf-serie.org) -- [asianembed.io](https://asianembed.io) -- [asiaflix.app](https://asiaflix.app) -- [fmovies.to](https://fmovies.to) -- [filman.cc](https://filman.cc) -- [dopebox.to](https://dopebox.to) -- [pinoymoviepedia.ru](https://pinoymoviepedia.ru) -- [pinoy-hd.xyz](https://www.pinoy-hd.xyz) -- [pinoymovies.es](https://pinoymovies.es) -- [trailers.to](https://trailers.to) -- [2embed.ru](https://www.2embed.ru) -- [dramasee.net](https://dramasee.net) -- [watchasian.sh](https://watchasian.sh) -- [kdramahood.com](https://kdramahood.com) -- [akwam.to](https://akwam.to) -- [mycima.tv](https://mycima.tv) -- [egy.best](https://www.egy.best) -- [hdm.to](https://hdm.to) -- [theflix.to](https://theflix.to) -- [v2.apimdb.net](https://v2.apimdb.net) -- [wcostream.com](https://www.wcostream.com) -- [gogoanime.film](https://gogoanime.film) -- [allanime.site](https://allanime.site) -- [animekisa.in](https://animekisa.in) -- [animeflick.net](https://animeflick.net) -- [tenshi.moe](https://tenshi.moe) -- [wcostream.cc](https://wcostream.cc) -- [9anime.id](https://9anime.id) -- [animeworld.tv](https://www.animeworld.tv) -- [zoro.to](https://zoro.to) -- [bestdubbedanime.com](https://bestdubbedanime.com) -- [monoschinos2.com](https://monoschinos2.com) -- [kawaiifu.com](https://kawaiifu.com) - + +Look [here](https://lagradost.github.io/CloudStream-3/) for a comprehensive list diff --git a/docs/index.html b/docs/index.html index a19815da..2db8f386 100644 --- a/docs/index.html +++ b/docs/index.html @@ -7,131 +7,17 @@ CloudStream-3 Supported Sites - - - +
-

Site supported:

+

Sites supported (0):

- + diff --git a/docs/script.js b/docs/script.js new file mode 100644 index 00000000..8501c879 --- /dev/null +++ b/docs/script.js @@ -0,0 +1,49 @@ +const count = document.getElementById("count") +const mainContainer = document.getElementById("siteList"); +fetch("/providers.json" + "?v=" + Date.now()) + .then(r => r.json()) + .then(function (data) { + count.innerHTML = Object.keys(data).length; + for (var key in data) { + if (data.hasOwnProperty(key)) { + var value = data[key]; + if (value.url == "NONE") { continue; } + + var _status = value.status + + var node = document.createElement("tr"); + node.classList.add("row"); + + var _a = document.createElement("a"); + _a.setAttribute('href', value.url); + _a.innerHTML = value.name + + var _statusText = "Unknown"; + var _buttonText = "yellow"; + switch (_status) { + case 0: + _statusText = "Unavailable"; + _buttonText = "red"; + break; + case 1: + _statusText = "Available"; + _buttonText = "green"; + + break; + case 2: + _statusText = "Slow"; + _buttonText = "yellow"; + break; + case 3: + _statusText = "Beta"; + _buttonText = "blue"; + break; + } + _a.classList.add(_buttonText + "Button"); + _a.classList.add("indicator"); + _a.classList.add("button"); + node.appendChild(_a); + mainContainer.appendChild(node); + } + } + }) \ No newline at end of file diff --git a/docs/style.css b/docs/style.css new file mode 100644 index 00000000..0f0b6a45 --- /dev/null +++ b/docs/style.css @@ -0,0 +1,49 @@ +body { + font-family: "Roboto", sans-serif; + background-color: #FFF; +} +.whiteText { + color : #FFF; +} + +.button { + color : #000; + text-decoration: none; +} + +.row { + padding: 0px 10px; + white-space: nowrap; +} +table { + border-spacing: 0.5rem; +} + +.yellowButton::before { + background-image: url("data:image/svg+xml;charset=utf8,%3Csvg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='m12 24c6.6274 0 12-5.3726 12-12 0-6.62744-5.3726-12-12-12-6.62744 0-12 5.37256-12 12 0 6.6274 5.37256 12 12 12zm0-17.5c.4141 0 .75.33582.75.75v4.5c0 .4142-.3359.75-.75.75s-.75-.3358-.75-.75v-4.5c0-.41418.3359-.75.75-.75zm.8242 9.5658c.0635-.0919.1118-.195.1416-.3054.0132-.0482.0225-.0979.0283-.1487.0039-.0366.0059-.074.0059-.1117 0-.5522-.4478-1-1-1s-1 .4478-1 1 .4478 1 1 1c.3423 0 .644-.172.8242-.4342z' fill='%23dbab09'/%3E%3C/svg%3e"); +} + +.blueButton::before { + filter: sepia(100%) saturate(300%) brightness(70%) hue-rotate(180deg); + background-image: url("data:image/svg+xml;charset=utf8,%3Csvg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='m12 24c6.6274 0 12-5.3726 12-12 0-6.62744-5.3726-12-12-12-6.62744 0-12 5.37256-12 12 0 6.6274 5.37256 12 12 12zm0-17.5c.4141 0 .75.33582.75.75v4.5c0 .4142-.3359.75-.75.75s-.75-.3358-.75-.75v-4.5c0-.41418.3359-.75.75-.75zm.8242 9.5658c.0635-.0919.1118-.195.1416-.3054.0132-.0482.0225-.0979.0283-.1487.0039-.0366.0059-.074.0059-.1117 0-.5522-.4478-1-1-1s-1 .4478-1 1 .4478 1 1 1c.3423 0 .644-.172.8242-.4342z' fill='%23dbab09'/%3E%3C/svg%3e"); +} + +.redButton::before { + background-image: url("data:image/svg+xml;charset=utf8,%3Csvg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='m12 24c6.6274 0 12-5.3726 12-12 0-6.62744-5.3726-12-12-12-6.62744 0-12 5.37256-12 12 0 6.6274 5.37256 12 12 12zm0-17.5c.4141 0 .75.33582.75.75v4.5c0 .4142-.3359.75-.75.75s-.75-.3358-.75-.75v-4.5c0-.41418.3359-.75.75-.75zm.8242 9.5658c.0635-.0919.1118-.195.1416-.3054.0132-.0482.0225-.0979.0283-.1487.0039-.0366.0059-.074.0059-.1117 0-.5522-.4478-1-1-1s-1 .4478-1 1 .4478 1 1 1c.3423 0 .644-.172.8242-.4342z' fill='%23d73a49'/%3E%3C/svg%3e"); +} + +.greenButton::before{ + background-image: url("data:image/svg+xml;charset=utf8,%3Csvg width='15' height='15' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='m1 12c0-6.07513 4.92487-11 11-11 6.0751 0 11 4.92487 11 11 0 6.0751-4.9249 11-11 11-6.07513 0-11-4.9249-11-11zm16.2803-2.71967c.2929-.29289.2929-.76777 0-1.06066s-.7677-.29289-1.0606 0l-5.9697 5.96963-2.46967-2.4696c-.29289-.2929-.76777-.2929-1.06066 0s-.29289.7677 0 1.0606l3 3c.29293.2929.76773.2929 1.06063 0z' fill='%2328a745'/%3E%3C/svg%3e");ontent: ''; +} + +.indicator::before { + display: inline-block; + width: 24px; + height: 24px; + content: ""; + vertical-align: text-bottom; + background-size: 100% 100%; + background-repeat: no-repeat; + background-position: center center; + margin-right:10px; +} \ No newline at end of file From dbbd0b29a72e799fd91e0a9661b48fd7fef39977 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Tue, 12 Apr 2022 16:49:04 +0000 Subject: [PATCH 10/11] chore(docs): update list of sites --- docs/providers.json | 441 ++++++++++++++++++++++++-------------------- 1 file changed, 238 insertions(+), 203 deletions(-) diff --git a/docs/providers.json b/docs/providers.json index 5ab2c093..147c5fde 100644 --- a/docs/providers.json +++ b/docs/providers.json @@ -1,287 +1,322 @@ { "AkwamProvider": { "name": "Akwam", - "url": "https://akwam.to", - "status": 1 + "status": 1, + "url": "https://akwam.to" }, "AllAnimeProvider": { "name": "AllAnime", - "url": "https://allanime.site", - "status": 1 + "status": 1, + "url": "https://allanime.site" }, "AllMoviesForYouProvider": { "name": "AllMoviesForYou", - "url": "https://allmoviesforyou.net", - "status": 1 + "status": 1, + "url": "https://allmoviesforyou.net" }, "AnimeFlickProvider": { "name": "AnimeFlick", - "url": "https://animeflick.net", - "status": 1 + "status": 1, + "url": "https://animeflick.net" }, "AnimePaheProvider": { "name": "AnimePahe", - "url": "https://animepahe.com", - "status": 0 + "status": 0, + "url": "https://animepahe.com" }, "AnimeWorldProvider": { "name": "AnimeWorld", - "url": "https://www.animeworld.tv", - "status": 1 + "status": 1, + "url": "https://www.animeworld.tv" + }, + "AnimeflvProvider": { + "name": "Animeflv", + "status": 1, + "url": "https://www3.animeflv.net" }, "AnimeflvnetProvider": { "name": "Animeflv.net", - "url": "https://www3.animeflv.net", - "status": 1 + "status": 1, + "url": "https://www3.animeflv.net" }, "AnimekisaProvider": { "name": "Animekisa", - "url": "https://animekisa.in", - "status": 1 + "status": 1, + "url": "https://animekisa.in" }, - "AsianLoadProvider": { - "name": "AsianLoad", - "url": "https://asianembed.io", - "status": 1 + "ApiMDBProvider": { + "name": "ApiMDB", + "status": 1, + "url": "https://v2.apimdb.net" }, "AsiaFlixProvider": { "name": "AsiaFlix", - "url": "https://asiaflix.app", - "status": 3 + "status": 3, + "url": "https://asiaflix.app" + }, + "AsianLoadProvider": { + "name": "AsianLoad", + "status": 1, + "url": "https://asianembed.io" }, "BflixProvider": { "name": "Bflix", - "url": "https://bflix.to", - "status": 1 - }, - "FmoviesToProvider": { - "name": "Fmovies.to", - "url": "https://fmovies.to", - "status": 1 - }, - "SflixProProvider": { - "name": "Sflix.pro", - "url": "https://sflix.pro", - "status": 1 + "status": 1, + "url": "https://fmovies.to" }, "CinecalidadProvider": { "name": "Cinecalidad", - "url": "https://cinecalidad.lol", - "status": 1 + "status": 1, + "url": "https://cinecalidad.lol" }, "CrossTmdbProvider": { "name": "MultiMovie", - "url": "NONE", - "status": 1 + "status": 1, + "url": "NONE" }, "CuevanaProvider": { "name": "Cuevana", - "url": "https://cuevana3.me", - "status": 1 - }, - "DoramasYTProvider": { - "name": "DoramasYT", - "url": "https://doramasyt.com", - "status": 1 - }, - "DramaSeeProvider": { - "name": "DramaSee", - "url": "https://dramasee.net", - "status": 1 - }, - "DubbedAnimeProvider": { - "name": "DubbedAnime", - "url": "https://bestdubbedanime.com", - "status": 1 - }, - "EgyBestProvider": { - "name": "EgyBest", - "url": "https://egy.best", - "status": 1 - }, - "EntrepeliculasyseriesProvider": { - "name": "EntrePeliculasySeries", - "url": "https://entrepeliculasyseries.nu", - "status": 1 - }, - "FilmanProvider": { - "name": "filman.cc", - "url": "https://filman.cc", - "status": 1 - }, - "FrenchStreamProvider": { - "name": "French Stream", - "url": "https://french-stream.re", - "status": 1 - }, - "GogoanimeProvider": { - "name": "GogoAnime", - "url": "https://gogoanime.film", - "status": 1 - }, - "KawaiifuProvider": { - "name": "Kawaiifu", - "url": "https://kawaiifu.com", - "status": 0 - }, - "HDMProvider": { - "name": "HD Movies", - "url": "https://hdm.to", - "status": 0 - }, - "IHaveNoTvProvider": { - "name": "I Have No TV", - "url": "https://ihavenotv.com", - "status": 1 - }, - "KdramaHoodProvider": { - "name": "KDramaHood", - "url": "https://kdramahood.com", - "status": 1 - }, - "LookMovieProvider": { - "name": "LookMovie", - "url": "https://lookmovie.io", - "status": 0 - }, - "MeloMovieProvider": { - "name": "MeloMovie", - "url": "https://melomovie.com", - "status": 0 - }, - "MonoschinosProvider": { - "name": "Monoschinos", - "url": "https://monoschinos2.com", - "status": 1 - }, - "MyCimaProvider": { - "name": "MyCima", - "url": "https://mycima.tv", - "status": 1 - }, - "NineAnimeProvider": { - "name": "9Anime", - "url": "https://9anime.id", - "status": 1 - }, - "PeliSmartProvider": { - "name": "PeliSmart", - "url": "https://pelismart.com", - "status": 1 - }, - "PelisflixProvider": { - "name": "Pelisflix", - "url": "https://pelisflix.li", - "status": 1 - }, - "PelisplusHDProvider": { - "name": "PelisplusHD", - "url": "https://pelisplushd.net", - "status": 1 - }, - "PelisplusProvider": { - "name": "Pelisplus", - "url": "https://pelisplus.icu", - "status": 1 - }, - "PinoyHDXyzProvider": { - "name": "Pinoy-HD", - "url": "https://www.pinoy-hd.xyz", - "status": 1 - }, - "PinoyMoviePediaProvider": { - "name": "Pinoy Moviepedia", - "url": "https://pinoymoviepedia.ru", - "status": 1 - }, - "PinoyMoviesEsProvider": { - "name": "Pinoy Movies", - "url": "https://pinoymovies.es", - "status": 1 - }, - "SflixProvider": { - "name": "Sflix.to", - "url": "https://sflix.to", - "status": 1 + "status": 1, + "url": "https://cuevana3.me" }, "DopeboxProvider": { "name": "Dopebox", - "url": "https://dopebox.to", - "status": 1 + "status": 1, + "url": "https://dopebox.to" }, - "SolarmovieProvider": { - "name": "Solarmovie", - "url": "https://solarmovie.pe", - "status": 1 + "DoramasYTProvider": { + "name": "DoramasYT", + "status": 1, + "url": "https://doramasyt.com" + }, + "DramaSeeProvider": { + "name": "DramaSee", + "status": 1, + "url": "https://dramasee.net" + }, + "DubbedAnimeProvider": { + "name": "DubbedAnime", + "status": 1, + "url": "https://bestdubbedanime.com" + }, + "EgyBestProvider": { + "name": "EgyBest", + "status": 1, + "url": "https://www.egy.best" + }, + "EntrePeliculasySeriesProvider": { + "name": "EntrePeliculasySeries", + "status": 1, + "url": "https://entrepeliculasyseries.nu" + }, + "EntrepeliculasyseriesProvider": { + "name": "EntrePeliculasySeries", + "status": 1, + "url": "https://entrepeliculasyseries.nu" + }, + "FilmanProvider": { + "name": "filman.cc", + "status": 1, + "url": "https://filman.cc" + }, + "FmoviesToProvider": { + "name": "Fmovies.to", + "status": 1, + "url": "https://fmovies.to" + }, + "FrenchStreamProvider": { + "name": "French Stream", + "status": 1, + "url": "https://french-stream.re" + }, + "GogoanimeProvider": { + "name": "GogoAnime", + "status": 1, + "url": "https://gogoanime.film" + }, + "HDMProvider": { + "name": "HD Movies", + "status": 0, + "url": "https://hdm.to" + }, + "IHaveNoTvProvider": { + "name": "I Have No TV", + "status": 1, + "url": "https://ihavenotv.com" + }, + "KawaiifuProvider": { + "name": "Kawaiifu", + "status": 0, + "url": "https://kawaiifu.com" + }, + "KdramaHoodProvider": { + "name": "KDramaHood", + "status": 1, + "url": "https://kdramahood.com" + }, + "LookMovieProvider": { + "name": "LookMovie", + "status": 0, + "url": "https://lookmovie.io" + }, + "MeloMovieProvider": { + "name": "MeloMovie", + "status": 0, + "url": "https://melomovie.com" + }, + "MonoschinosProvider": { + "name": "Monoschinos", + "status": 1, + "url": "https://monoschinos2.com" + }, + "MultiAnimeProvider": { + "name": "MultiAnime", + "status": 1, + "url": "" + }, + "MyCimaProvider": { + "name": "MyCima", + "status": 1, + "url": "https://mycima.tv" + }, + "NginxProvider": { + "name": "Nginx", + "status": 1, + "url": "" + }, + "NineAnimeProvider": { + "name": "9Anime", + "status": 1, + "url": "https://9anime.id" + }, + "NyaaProvider": { + "name": "Nyaa", + "status": 1, + "url": "https://nyaa.si" + }, + "PeliSmartProvider": { + "name": "PeliSmart", + "status": 1, + "url": "https://pelismart.com" + }, + "PelisflixProvider": { + "name": "Pelisflix", + "status": 1, + "url": "https://pelisflix.li" + }, + "PelisplusHDProvider": { + "name": "PelisplusHD", + "status": 1, + "url": "https://pelisplushd.net" + }, + "PelisplusProvider": { + "name": "Pelisplus", + "status": 1, + "url": "https://pelisplus.icu" + }, + "PinoyHDXyzProvider": { + "name": "Pinoy-HD", + "status": 1, + "url": "https://www.pinoy-hd.xyz" + }, + "PinoyMoviePediaProvider": { + "name": "Pinoy Moviepedia", + "status": 1, + "url": "https://pinoymoviepedia.ru" + }, + "PinoyMoviesEsProvider": { + "name": "Pinoy Movies", + "status": 1, + "url": "https://pinoymovies.es" }, "SeriesflixProvider": { "name": "Seriesflix", - "url": "https://seriesflix.video", - "status": 1 + "status": 1, + "url": "https://seriesflix.video" + }, + "SflixProProvider": { + "name": "Sflix.pro", + "status": 1, + "url": "https://sflix.pro" + }, + "SflixProvider": { + "name": "Sflix.to", + "status": 1, + "url": "https://dopebox.to" }, "SoaptwoDayProvider": { "name": "Soap2Day", - "url": "https://secretlink.xyz", - "status": 1 + "status": 1, + "url": "https://secretlink.xyz" + }, + "SolarmovieProvider": { + "name": "Solarmovie", + "status": 1, + "url": "https://solarmovie.pe" }, "TenshiProvider": { "name": "Tenshi.moe", - "url": "https://tenshi.moe", - "status": 1 - }, - "TrailersTwoProvider": { - "name": "Trailers.to", - "url": "https://trailers.to", - "status": 1 + "status": 1, + "url": "https://tenshi.moe" }, "TheFlixToProvider": { "name": "TheFlix.to", - "url": "https://theflix.to", - "status": 0 + "status": 0, + "url": "https://theflix.to" + }, + "TmdbProvider": { + "name": "Tmdb", + "status": 1, + "url": "" + }, + "TrailersTwoProvider": { + "name": "Trailers.to", + "status": 1, + "url": "https://trailers.to" }, "TwoEmbedProvider": { "name": "2Embed", - "url": "https://www.2embed.ru", - "status": 1 + "status": 1, + "url": "https://www.2embed.ru" }, "VMoveeProvider": { "name": "VMovee", - "url": "https://www.vmovee.watch", - "status": 1 + "status": 1, + "url": "https://www.vmovee.watch" }, "VfFilmProvider": { "name": "vf-film.me", - "url": "https://vf-film.me", - "status": 1 + "status": 1, + "url": "https://vf-film.me" }, "VfSerieProvider": { "name": "vf-serie.org", - "url": "https://vf-serie.org", - "status": 1 + "status": 1, + "url": "https://vf-serie.org" }, "VidEmbedProvider": { "name": "VidEmbed", - "url": "https://vidembed.cc", - "status": 1 + "status": 1, + "url": "https://vidembed.cc" }, "WatchAsianProvider": { "name": "WatchAsian", - "url": "https://watchasian.sh", - "status": 1 + "status": 1, + "url": "https://watchasian.sh" }, "WatchCartoonOnlineProvider": { "name": "WatchCartoonOnline", - "url": "https://www.wcostream.com", - "status": 1 + "status": 1, + "url": "https://www.wcostream.com" }, "WcoProvider": { "name": "WCO Stream", - "url": "https://wcostream.cc", - "status": 1 + "status": 1, + "url": "https://wcostream.cc" }, "ZoroProvider": { "name": "Zoro", - "url": "https://zoro.to", - "status": 1 + "status": 1, + "url": "https://zoro.to" } -} +} \ No newline at end of file From fc3caab866b941eeea66f13f4a78890641e87c80 Mon Sep 17 00:00:00 2001 From: Cloudburst <18114966+C10udburst@users.noreply.github.com> Date: Tue, 12 Apr 2022 20:09:17 +0200 Subject: [PATCH 11/11] fix website (#946) * ahh im sorry * Update script.js --- docs/index.html | 4 ++-- docs/script.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/index.html b/docs/index.html index 2db8f386..deaa29a8 100644 --- a/docs/index.html +++ b/docs/index.html @@ -7,7 +7,7 @@ CloudStream-3 Supported Sites - + @@ -17,7 +17,7 @@ - + diff --git a/docs/script.js b/docs/script.js index 8501c879..0c0ec8c0 100644 --- a/docs/script.js +++ b/docs/script.js @@ -1,6 +1,6 @@ const count = document.getElementById("count") const mainContainer = document.getElementById("siteList"); -fetch("/providers.json" + "?v=" + Date.now()) +fetch("providers.json" + "?v=" + Date.now()) .then(r => r.json()) .then(function (data) { count.innerHTML = Object.keys(data).length; @@ -46,4 +46,4 @@ fetch("/providers.json" + "?v=" + Date.now()) mainContainer.appendChild(node); } } - }) \ No newline at end of file + })