From 48a83c0d8be34fcfd595cbf3bc8bd0a87a5158f0 Mon Sep 17 00:00:00 2001 From: CakesTwix Date: Tue, 21 Feb 2023 16:43:10 +0200 Subject: [PATCH] uaserial: done --- UASerialProvider/build.gradle.kts | 25 ++++ UASerialProvider/src/main/AndroidManifest.xml | 2 + .../kotlin/com/lagradost/UASerialProvider.kt | 141 ++++++++++++++++++ .../com/lagradost/UASerialProviderPlugin.kt | 13 ++ .../com/lagradost/models/GeneralInfo.kt | 61 ++++++++ 5 files changed, 242 insertions(+) create mode 100644 UASerialProvider/build.gradle.kts create mode 100644 UASerialProvider/src/main/AndroidManifest.xml create mode 100644 UASerialProvider/src/main/kotlin/com/lagradost/UASerialProvider.kt create mode 100644 UASerialProvider/src/main/kotlin/com/lagradost/UASerialProviderPlugin.kt create mode 100644 UASerialProvider/src/main/kotlin/com/lagradost/models/GeneralInfo.kt diff --git a/UASerialProvider/build.gradle.kts b/UASerialProvider/build.gradle.kts new file mode 100644 index 0000000..33eaa5a --- /dev/null +++ b/UASerialProvider/build.gradle.kts @@ -0,0 +1,25 @@ +// use an integer for version numbers +version = 1 + + +cloudstream { + language = "uk" + // All of these properties are optional, you can safely remove them + + description = "Дивись онлайн серіали тільки найкращих світових студій Netflix, HBO, Disney, Amazon та інших. Серіали із українською озвучкою та онлайн, у HD якості." + authors = listOf("CakesTwix") + + /** + * Status int as the following: + * 0: Down + * 1: Ok + * 2: Slow + * 3: Beta only + * */ + status = 1 // will be 3 if unspecified + tvTypes = listOf( + "TvSeries", + ) + + iconUrl = "https://www.google.com/s2/favicons?domain=uaserial.tv&sz=%size%" +} \ No newline at end of file diff --git a/UASerialProvider/src/main/AndroidManifest.xml b/UASerialProvider/src/main/AndroidManifest.xml new file mode 100644 index 0000000..29aec9d --- /dev/null +++ b/UASerialProvider/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/UASerialProvider/src/main/kotlin/com/lagradost/UASerialProvider.kt b/UASerialProvider/src/main/kotlin/com/lagradost/UASerialProvider.kt new file mode 100644 index 0000000..56013a6 --- /dev/null +++ b/UASerialProvider/src/main/kotlin/com/lagradost/UASerialProvider.kt @@ -0,0 +1,141 @@ +package com.lagradost + +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson +import com.lagradost.cloudstream3.utils.ExtractorLink +import com.lagradost.cloudstream3.utils.M3u8Helper +import com.lagradost.models.GeneralInfo +import org.jsoup.nodes.Element + +class UASerialProvider : MainAPI() { + + // Basic Info + override var mainUrl = "https://uaserial.tv" + override var name = "UASerial" + override val hasMainPage = true + override var lang = "uk" + override val hasDownloadSupport = true + override val supportedTypes = setOf( + TvType.TvSeries, + ) + + // Sections + override val mainPage = mainPageOf( + "$mainUrl/%d?priority=popular" to "⭐ За популярністю", + "$mainUrl/%d?priority=views" to "\uD83D\uDC40 За переглядами", + "$mainUrl/%d?priority=rating" to "\uD83C\uDFC6 За рейтингом", + "$mainUrl/%d?priority=date" to "\uD83D\uDD25 За новизною", + ) + + override suspend fun getMainPage( + page: Int, + request: MainPageRequest + ): HomePageResponse { + var document = app.get(request.data.format(page)).document + + val home = document.select(".row .col").map { + it.toSearchResponse() + } + return newHomePageResponse(request.name, home) + } + + private fun Element.toSearchResponse(): SearchResponse { + val title = this.selectFirst("div.name")?.text()?.trim().toString() + val href = this.selectFirst("a")?.attr("href").toString() + val posterUrl = mainUrl + this.selectFirst("img.cover")?.attr("src") + + return newMovieSearchResponse(title, href, TvType.Movie) { + this.posterUrl = posterUrl + } + + } + + override suspend fun search(query: String): List { + val document = app.post( + url = "$mainUrl", + data = mapOf( + "do" to "search", + "subaction" to "search", + "story" to query.replace(" ", "+") + ) + ).document + + return document.select("article.short").map { + it.toSearchResponse() + } + } + + // Detailed information + override suspend fun load(url: String): LoadResponse { + val document = app.get(url).document + // Log.d("load-debug", document.select("script[type=application/ld+json]").html()) + val titleJson = + tryParseJson(document.select("script[type=application/ld+json]").html())!! + + // Parse info + val title = titleJson.partOfTVSeries.name + val poster = mainUrl + document.selectFirst("img.cover")?.attr("src") + val tags = document.select("div.genre div a").map { it.text() } + val year = document.select("div.release div a").text().toIntOrNull() + + val tvType = TvType.TvSeries + val description = document.selectFirst(".text")?.text()?.trim() + val rating = document.select("div.rating__item--imdb div.number").text().toRatingInt() + + // val actors = full_info[4].select("a").map { it.text() } + + var episodes: List = emptyList() + titleJson.partOfTVSeries.containsSeason.map { season -> + season.episode.map { episode -> + episodes = episodes.plus( + Episode( + "${season.url}, ${episode.episodeNumber}", + episode.name.replaceFirstChar { it.uppercase() }, + season.seasonNumber, + episode.episodeNumber + ) + ) + } + } + + return newTvSeriesLoadResponse(title, url, tvType, episodes) { + this.posterUrl = poster + this.year = year + this.plot = description + this.tags = tags + this.rating = rating + } + } + + + // It works when I click to view the series + override suspend fun loadLinks( + data: String, // Url with season, episode number + isCasting: Boolean, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ): Boolean { + val dataList = data.split(", ") + val document = app.get(mainUrl + app.get(dataList[0]).document.select("option[data-series-number=${dataList[1]}]").attr("value")).document + document.select(".player .voices__wrap").map{ player -> + // Log.d("load-debug", player.attr("data-player-id")) // Player name + player.select("select.voices__select option").map{ dub -> + // Log.d("load-debug", dub.text()) // Name + // Log.d("load-debug", dub.attr("value"))// Url + + val m3u8Url = app.get(dub.attr("value")).document.select("script").html() + .substringAfterLast("file:\"") + .substringBefore("\",") + + M3u8Helper.generateM3u8( + source = "${dub.text()} (${player.attr("data-player-id").replaceFirstChar { it.uppercase() }})", + streamUrl = m3u8Url, + referer = "https://tortuga.wtf/" + ).forEach(callback) + } + } + // selecto__dropdown-item + return true + } + +} \ No newline at end of file diff --git a/UASerialProvider/src/main/kotlin/com/lagradost/UASerialProviderPlugin.kt b/UASerialProvider/src/main/kotlin/com/lagradost/UASerialProviderPlugin.kt new file mode 100644 index 0000000..1bfe419 --- /dev/null +++ b/UASerialProvider/src/main/kotlin/com/lagradost/UASerialProviderPlugin.kt @@ -0,0 +1,13 @@ +package com.lagradost + +import com.lagradost.cloudstream3.plugins.CloudstreamPlugin +import com.lagradost.cloudstream3.plugins.Plugin +import android.content.Context + +@CloudstreamPlugin +class UASerialProviderPlugin: Plugin() { + override fun load(context: Context) { + // All providers should be added in this manner. Please don't edit the providers list directly. + registerMainAPI(UASerialProvider()) + } +} \ No newline at end of file diff --git a/UASerialProvider/src/main/kotlin/com/lagradost/models/GeneralInfo.kt b/UASerialProvider/src/main/kotlin/com/lagradost/models/GeneralInfo.kt new file mode 100644 index 0000000..bf28e9b --- /dev/null +++ b/UASerialProvider/src/main/kotlin/com/lagradost/models/GeneralInfo.kt @@ -0,0 +1,61 @@ +package com.lagradost.models + +data class GeneralInfo ( + + // val @context : String, + // val @type : String, + val datePublished : String, + val numberOfEpisodes : Int, + val partOfTVSeries : PartOfTVSeries, + val aggregateRating : AggregateRating, + val url : String, + val name : String +) + +data class PartOfTVSeries ( + + // val @type : String, + val actor : List, + val director : List, + val name : String, + val containsSeason : List +) + +data class AggregateRating ( + + // val @type : String, + val ratingValue : Double, + val ratingCount : Int, + val bestRating : Int, + val worstRating : Int +) + +data class Actor ( + + // val @type : String, + val name : String +) + +data class ContainsSeason ( + + val url : String, + // val @type : String, + val seasonNumber : Int, + val datePublished : String, + val name : String, + val episode : List, + val numberOfEpisodes : Int +) + +data class Episode ( + + // val @type : String, + val episodeNumber : Int, + val name : String +) + +data class Director ( + + // val @type : String, + val name : String +)