diff --git a/README.md b/README.md index 69b670ae..6692e975 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,7 @@ It merely scrapes 3rd-party websites that are publicly accessable via any regula - [allmoviesforyou.co](https://allmoviesforyou.co) - [vidembed.cc](https://vidembed.cc) - [vf-film.me](https://vf-film.me) +- [vf-serie.org](https://vf-serie.org) - [asianload.cc](https://asianload.cc) - [sflix.to](https://sflix.to) - [zoro.to](https://zoro.to) diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt index a312f170..f2413231 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt @@ -46,6 +46,7 @@ object APIHolder { VidEmbedProvider(), VfFilmProvider(), + VfSerieProvider(), AsianLoadProvider(), SflixProvider(), diff --git a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/VfFilmProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/VfFilmProvider.kt index f4a86852..df717f7a 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/VfFilmProvider.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/VfFilmProvider.kt @@ -77,11 +77,11 @@ class VfFilmProvider : MainAPI() { private fun getDirect(original: String): String { // original data, https://vf-film.org/?trembed=1&trid=55313&trtype=1 for example val response = get(original).text - val url = "iframe .*src=\"(.*?)\"".toRegex().find(response)?.groupValues?.get(1) - .toString() // https://vudeo.net/embed-uweno86lzx8f.html for example + val url = "iframe .*src=\"(.*?)\"".toRegex().find(response)?.groupValues?.get(1).toString() // https://vudeo.net/embed-uweno86lzx8f.html for example val vudoResponse = get(url).text val document = Jsoup.parse(vudoResponse) - return Regex("sources: \\[\"(.*?)\"]").find(document.html())?.groupValues?.get(1).toString() + val vudoUrl = Regex("sources: \\[\"(.*?)\"]").find(document.html())?.groupValues?.get(1).toString() // direct mp4 link, https://m11.vudeo.net/2vp3ukyw2avjdohilpebtzuct42q5jwvpmpsez3xjs6d7fbs65dpuey2rbra/v.mp4 for exemple + return vudoUrl } @@ -105,23 +105,23 @@ class VfFilmProvider : MainAPI() { val players = document.select("ul.TPlayerNv > li") - var numberPlayer = 0 + var number_player = 0 var found = false for (player in players) { if (player.selectFirst("> span").text() == "Vudeo") { found = true break } else { - numberPlayer += 1 + number_player += 1 } } - if (!found) { - numberPlayer = 0 + if (found == false) { + number_player = 0 } - val i = numberPlayer.toString() + val i = number_player.toString() val trid = Regex("iframe .*trid=(.*?)&").find(document.html())?.groupValues?.get(1) - val data = getDirect("https://vf-film.me/?trembed=$i&trid=$trid&trtype=1") + val data = getDirect("$mainUrl/?trembed=$i&trid=$trid&trtype=1") return MovieLoadResponse( diff --git a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/VfSerieProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/VfSerieProvider.kt new file mode 100644 index 00000000..1621615d --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/VfSerieProvider.kt @@ -0,0 +1,190 @@ +package com.lagradost.cloudstream3.movieproviders + +import com.fasterxml.jackson.module.kotlin.readValue +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.network.get +import com.lagradost.cloudstream3.network.post +import com.lagradost.cloudstream3.network.text +import com.lagradost.cloudstream3.network.url +import com.lagradost.cloudstream3.utils.ExtractorLink +import com.lagradost.cloudstream3.utils.Qualities +import okio.Buffer +import org.jsoup.Jsoup + +// referer = https://vf-serie.org, USERAGENT ALSO REQUIRED +class VfSerieProvider : MainAPI() { + override val mainUrl: String + get() = "https://vf-serie.org" + override val name: String + get() = "vf-serie.org" + + override val lang: String = "fr" + + override val hasQuickSearch: Boolean + get() = false + + override val hasMainPage: Boolean + get() = false + + override val hasChromecastSupport: Boolean + get() = false + + override val supportedTypes: Set + get() = setOf( + TvType.TvSeries, + ) + + + override fun search(query: String): List { + val url = "$mainUrl/?s=$query" + val response = get(url).text + val document = Jsoup.parse(response) + val items = document.select("ul.MovieList > li > article > a") + if (items.isNullOrEmpty()) return ArrayList() + + val returnValue = ArrayList() + for (item in items) { + val href = item.attr("href") + + val poster = item.selectFirst("> div.Image > figure > img").attr("src").replace("//image", "https://image") + + if (poster == "$mainUrl/wp-content/themes/toroplay/img/cnt/noimg-thumbnail.png") { // if the poster is missing (the item is just a redirect to something like https://vf-serie.org/series-tv/) + continue + } + val name = item.selectFirst("> h3.Title").text() + + val year = item.selectFirst("> span.Year").text()?.toIntOrNull() + + returnValue.add(TvSeriesSearchResponse(name, href, this.name, TvType.TvSeries, poster, year, null)) + } + return returnValue + } + + + + + + private fun getDirect(original: String): String { // original data, https://vf-serie.org/?trembed=1&trid=80467&trtype=2 for example + val response = get(original).text + val url = "iframe .*src=\\\"(.*?)\\\"".toRegex().find(response)?.groupValues?.get(1).toString() // https://vudeo.net/embed-7jdb1t5b2mvo.html for example + val vudoResponse = get(url).text + val document = Jsoup.parse(vudoResponse) + val vudoUrl = Regex("sources: \\[\"(.*?)\"\\]").find(document.html())?.groupValues?.get(1).toString() // direct mp4 link, https://m5.vudeo.net/2vp3xgpw2avjdohilpfbtyuxzzrqzuh4z5yxvztral5k3rjnba6f4byj3saa/v.mp4 for exemple + return vudoUrl + } + + override fun loadLinks( + data: String, + isCasting: Boolean, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ): Boolean { + if (data == "") return false + + val response = get(data).text + val document = Jsoup.parse(response) + val players = document.select("ul.TPlayerNv > li") + var number_player = Regex(".*trembed=(.*?)&").find(document.html())?.groupValues?.get(1)!!.toInt() // the starting trembed number of the first player website, some start at 0 other at 1 + var found = false + for (player in players) { + if (player.selectFirst("> span").text() == "Vudeo") { + found = true + break + } else { + number_player += 1 + } + } + if (found == false) { + number_player = 1 + } + val i = number_player.toString() + val trid = Regex("iframe .*trid=(.*?)&").find(document.html())?.groupValues?.get(1) + + val data = getDirect("$mainUrl/?trembed=$i&trid=$trid&trtype=2") + callback.invoke( + ExtractorLink( + this.name, + this.name, + data, + "", + Qualities.P720.value, + false + ) + ) + return true + } + + + override fun load(url: String): LoadResponse { + val response = get(url).text + val document = Jsoup.parse(response) + val title = document?.selectFirst(".Title")?.text()?.replace("Regarder Serie ","")?.replace(" En Streaming", "") + ?: throw ErrorLoadingException("Service might be unavailable") + + + val year = document.select("span.Date").text()?.toIntOrNull() + + val rating = document.select("span.AAIco-star").text()?.toIntOrNull() + + val duration = document.select("span.Time").text()?.toIntOrNull() + + val backgroundPoster = document.selectFirst("div.Image > figure > img").attr("src").replace("//image", "https://image") + + val descript = document.selectFirst("div.Description > p").text() + + + + val list = ArrayList() + + // episode begin + document.select(".Wdgt").forEach { element -> + val season = element.selectFirst("> .AA-Season > span")?.text()?.toIntOrNull() + if (season != null && season > 0) { + list.add(season) + } + } + if (list.isEmpty()) throw ErrorLoadingException("No Seasons Found") + + val episodeList = ArrayList() + + for (season in list) { + val episodes = document.select("table > tbody > tr") + if (episodes.isNotEmpty()) { + episodes.forEach { episode -> + val epNum = episode.selectFirst("> span.Num")?.text()?.toIntOrNull() + val poster = episode.selectFirst("> td.MvTbImg > a > img")?.attr("src")?.replace("//image", "https://image") + val aName = episode.selectFirst("> td.MvTbTtl > a") + val date = episode.selectFirst("> td.MvTbTtl > span")?.text()?.toString() + val name = aName.text() + val href = aName.attr("href") + episodeList.add( + TvSeriesEpisode( + name, + season, + epNum, + href, + poster, + date + ) + ) + } + } + } + return TvSeriesLoadResponse( + title, + url, + this.name, + TvType.TvSeries, + episodeList, + backgroundPoster, + year, + descript, + null, + null, + rating + ) + + + + } +} diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index ce0c141a..2b53af01 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -17,14 +17,12 @@ Ouvrir dans le naviguateur Passer le chargement Chargement… - En visionnage En pose Terminé Abandonné A regarder Aucun - Lire Streamer Torrent Sources @@ -34,7 +32,6 @@ Miniature de l\'Episode Lire l\'Episode Permet de télécharger les épisodes - Télécharger Téléchargé Téléchargement @@ -43,18 +40,14 @@ Echec du Téléchargement Téléchargement Annulé Téléchargement Terminé - Erreur lors du téléchargement des liens Stockage Interne - Dub Sub - Supprimer le fichier Lire le fichier Reprendre le téléchargement Mettre en pause le téléchargement - Désactiver le rapport de bug automatique Plus d\'informations Cacher @@ -80,24 +73,19 @@ Police Rechercher en utilisant les fournisseurs Rechercher en utilisant les types - %d Benenes données au dev Aucune Benenes donnée - Séléction automatique de la langue Télécharger les langues Maintenir pour réinitialliser les valeurs par défault Continer à regarder - Supprimer Plus d\'info - Un VPN peut être requit pour que ce fournisseur fonctionne Ce fournisseur est un torrent, un VPN est recommendé Description Aucune description trouvée Aucune description trouvée - Lecteur en mode Picture-in-Picture Continuer la lecture dans une fenêtre miniature en superposition sur d\'autres applis Boutton de redimentionnement du lecteur @@ -128,29 +116,24 @@ Rejoindre le serveur Discord Donner une benene aux devs benenes données - Language de l\'application - Ce fournisseur ne supporte pas le Chromecast Aucun lien trouvé Lien copié dans le presse-papiers Lecture de l\'episode Réinitialiser aux valeurs par défault Désolé, l\'application à crashé. Un rapport de bug anonyme va être envoyé aux devloppeurs - Saison Pas de Saison Episode Episodes S E - Supprimer le Fichier Supprimer Pause Reprendre Cela va supprimer définitivement %s\nÊtes vous sûr ? - En cours Terminé Status @@ -159,27 +142,22 @@ Durée Site Synopsis - Liste d\'attente Pas de sous-titres Défault - Libre Utilisé Application - Films Séries TV Dessin animés Animés Torrents - Erreur de la source Erreur distante Erreur d\'affichage Erreur innatendue du lecteur Erreur du téléchargement, vérifier l\'autorisation du stockage - Episode Chromecast Miroir Chromecast Lecture dans l\'application @@ -189,15 +167,39 @@ Téléchargement Automatique Télécharger depuis le miroir Recharger les liens - Pas de mise-à-jour trouvés Vérifier les mise-à-jour - Verouiller Redimensionner Source Passer l\'OP - Ne pas afficher à nouveau Mettre à jour + Utile pour contourner les bloquages des FAI + Nouvelle mise à jour trouvée ! +\n%s -> %s + (Épisode spécial) %s + Qualité de visionnage préférée + Étendre + Non-responsabilité + Couleur principale + Thème de l\'application + Vitesse (%.2fx) + Utiliser la luminosité du système + Général + DNS avec HTTPS + Afficher les animés en Anglais (Dub) / sous-titrés + Disposition en mode téléphone + %s Ep %d + Note : %.1f + Taille de la police + Utiliser la luminosité du système dans le lecteur de l\'application au lieu d\'un écran noir + Afficher les épisodes spéciaux pour les animés + Zoom + Adapter à l\'écran + Disposition de l\'application + Disposition TV + Language des fournisseurs + Médias préfères + Auto