From 3d781d88c04ddba68640b6bce3b658d7f7302396 Mon Sep 17 00:00:00 2001 From: CakesTwix Date: Wed, 15 Feb 2023 15:14:48 +0200 Subject: [PATCH] uakino: Import from cloudstream-extensions-multilingual I didn't make the extension, I just added myself for modifications and fixes. Fixed compilation errors associated with JSON library. Added voice studio to the episode titles In the future we need to fix Naruto parsing. It's mistakenly thought to be a movie, but it's not. --- ExampleProvider/build.gradle.kts | 24 --- .../kotlin/com/example/ExampleProvider.kt | 21 -- UakinoProvider/build.gradle.kts | 27 +++ .../src/main/AndroidManifest.xml | 2 +- .../kotlin/com/lagradost/UakinoProvider.kt | 188 ++++++++++++++++++ .../com/lagradost/UakinoProviderPlugin.kt | 7 +- build.gradle.kts | 8 +- 7 files changed, 224 insertions(+), 53 deletions(-) delete mode 100644 ExampleProvider/build.gradle.kts delete mode 100644 ExampleProvider/src/main/kotlin/com/example/ExampleProvider.kt create mode 100644 UakinoProvider/build.gradle.kts rename {ExampleProvider => UakinoProvider}/src/main/AndroidManifest.xml (52%) create mode 100644 UakinoProvider/src/main/kotlin/com/lagradost/UakinoProvider.kt rename ExampleProvider/src/main/kotlin/com/example/ExamplePlugin.kt => UakinoProvider/src/main/kotlin/com/lagradost/UakinoProviderPlugin.kt (75%) diff --git a/ExampleProvider/build.gradle.kts b/ExampleProvider/build.gradle.kts deleted file mode 100644 index 58645fe..0000000 --- a/ExampleProvider/build.gradle.kts +++ /dev/null @@ -1,24 +0,0 @@ -// use an integer for version numbers -version = 1 - - -cloudstream { - // All of these properties are optional, you can safely remove them - - description = "Lorem Ipsum" - authors = listOf("Cloudburst") - - /** - * Status int as the following: - * 0: Down - * 1: Ok - * 2: Slow - * 3: Beta only - * */ - status = 1 // will be 3 if unspecified - - // List of video source types. Users are able to filter for extensions in a given category. - // You can find a list of avaliable types here: - // https://recloudstream.github.io/cloudstream/html/app/com.lagradost.cloudstream3/-tv-type/index.html - tvTypes = listOf("Others") -} diff --git a/ExampleProvider/src/main/kotlin/com/example/ExampleProvider.kt b/ExampleProvider/src/main/kotlin/com/example/ExampleProvider.kt deleted file mode 100644 index e44a21b..0000000 --- a/ExampleProvider/src/main/kotlin/com/example/ExampleProvider.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.example - -import com.lagradost.cloudstream3.TvType -import com.lagradost.cloudstream3.MainAPI -import com.lagradost.cloudstream3.SearchResponse - -class ExampleProvider : MainAPI() { // all providers must be an instance of MainAPI - override var mainUrl = "https://example.com/" - override var name = "Example provider" - override val supportedTypes = setOf(TvType.Movie) - - override var lang = "en" - - // enable this when your provider has a main page - override val hasMainPage = true - - // this function gets called when you search for something - override suspend fun search(query: String): List { - return listOf() - } -} \ No newline at end of file diff --git a/UakinoProvider/build.gradle.kts b/UakinoProvider/build.gradle.kts new file mode 100644 index 0000000..49b997d --- /dev/null +++ b/UakinoProvider/build.gradle.kts @@ -0,0 +1,27 @@ +// use an integer for version numbers +version = 1 + + +cloudstream { + language = "uk" + // All of these properties are optional, you can safely remove them + + description = "Дивитися фільми та серіали онлайн в 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( + "Anime", + "TvSeries", + "Movie", + ) + + iconUrl = "https://www.google.com/s2/favicons?domain=uakino.club&sz=%size%" +} \ No newline at end of file diff --git a/ExampleProvider/src/main/AndroidManifest.xml b/UakinoProvider/src/main/AndroidManifest.xml similarity index 52% rename from ExampleProvider/src/main/AndroidManifest.xml rename to UakinoProvider/src/main/AndroidManifest.xml index 1863f02..29aec9d 100644 --- a/ExampleProvider/src/main/AndroidManifest.xml +++ b/UakinoProvider/src/main/AndroidManifest.xml @@ -1,2 +1,2 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/UakinoProvider/src/main/kotlin/com/lagradost/UakinoProvider.kt b/UakinoProvider/src/main/kotlin/com/lagradost/UakinoProvider.kt new file mode 100644 index 0000000..09656cb --- /dev/null +++ b/UakinoProvider/src/main/kotlin/com/lagradost/UakinoProvider.kt @@ -0,0 +1,188 @@ +package com.lagradost + +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.LoadResponse.Companion.addActors +import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer +import com.lagradost.cloudstream3.mvvm.safeApiCall +import com.lagradost.cloudstream3.utils.ExtractorLink +import com.lagradost.cloudstream3.utils.M3u8Helper +import org.jsoup.Jsoup +import org.jsoup.nodes.Element +import java.util.* + +class UakinoProvider : MainAPI() { + + // Basic Info + override var mainUrl = "https://uakino.club" + override var name = "Uakino" + override val hasMainPage = true + override var lang = "uk" + override val hasDownloadSupport = true + override val supportedTypes = setOf( + TvType.Movie, + TvType.TvSeries, + TvType.Anime + ) + + // Sections + override val mainPage = mainPageOf( + "$mainUrl/filmy/page/" to "Фільми", + "$mainUrl/seriesss/page/" to "Серіали", + "$mainUrl/seriesss/doramy/page/" to "Дорами", + "$mainUrl/animeukr/page/" to "Аніме", + "$mainUrl/cartoon/page/" to "Мультфільми", + "$mainUrl/cartoon/cartoonseries/page/" to "Мультсеріали", + ) + + override suspend fun getMainPage( + page: Int, + request: MainPageRequest + ): HomePageResponse { + val document = app.get(request.data + page).document + val home = document.select("div.owl-item, div.movie-item").map { + it.toSearchResponse() + } + return newHomePageResponse(request.name, home) + } + + private fun Element.toSearchResponse(): SearchResponse { + val title = this.selectFirst("a.movie-title")?.text()?.trim().toString() + val href = this.selectFirst("a.movie-title")?.attr("href").toString() + val posterUrl = fixUrlNull(this.selectFirst("img")?.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("div.movie-item.short-item").map { + it.toSearchResponse() + } + } + + override suspend fun load(url: String): LoadResponse { + val document = app.get(url).document + + // Parse info + val title = document.selectFirst("h1 span.solototle")?.text()?.trim().toString() + val poster = fixUrl(document.selectFirst("div.film-poster img")?.attr("src").toString()) + val tags = document.select("div.film-info > div:nth-child(4) a").map { it.text() } + val year = document.select("div.film-info > div:nth-child(2) a").text().toIntOrNull() + // TODO: Fix Naruto. Its series, not Movie Lol + // https://uakino.club/animeukr/6268-naruto-1-sezon.html + val tvType = + if (url.contains(Regex("(/anime-series)|(/seriesss)|(/cartoonseries)"))) TvType.TvSeries else TvType.Movie + val description = document.selectFirst("div[itemprop=description]")?.text()?.trim() + val trailer = document.selectFirst("iframe#pre")?.attr("data-src") + val rating = document.selectFirst("div.film-info > div:nth-child(8) div.fi-desc")?.text() + ?.substringBefore("/").toRatingInt() + val actors = document.select("div.film-info > div:nth-child(6) a").map { it.text() } + + val recommendations = document.select("div#full-slides div.owl-item").map { + it.toSearchResponse() + } + + // Return to app + // Parse Episodes as Series + return if (tvType == TvType.TvSeries) { + val id = url.split("/").last().split("-").first() + val episodes = + app.get("$mainUrl/engine/ajax/playlists.php?news_id=$id&xfield=playlist&time=${Date().time}") + .parsedSafe()?.response.let { + Jsoup.parse(it.toString()).select("ul > li").mapNotNull { eps -> + val href = fixUrl(eps.attr("data-file")) // ashdi.vip/... + val name = "${eps.text().trim()} (${eps.attr("data-voice")})" // Серія 1 (Озвучення) + if (href.isNotEmpty()) { + Episode( + href, + name, + ) + } else { + null + } + } + } + newTvSeriesLoadResponse(title, url, TvType.TvSeries, episodes) { + this.posterUrl = poster + this.year = year + this.plot = description + this.tags = tags + this.rating = rating + addActors(actors) + this.recommendations = recommendations + addTrailer(trailer) + } + } else { // Parse as Movie. + newMovieLoadResponse(title, url, TvType.Movie, url) { + this.posterUrl = poster + this.year = year + this.plot = description + this.tags = tags + this.rating = rating + addActors(actors) + this.recommendations = recommendations + addTrailer(trailer) + } + } + } + + override suspend fun loadLinks( + data: String, + isCasting: Boolean, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ): Boolean { + + val links = ArrayList() + + if (data.startsWith("https://ashdi.vip")) { + links.add(data) + } else { + val iframeUrl = app.get(data).document.selectFirst("iframe#pre")?.attr("src") + if (iframeUrl.isNullOrEmpty()) { + val id = data.split("/").last().split("-").first() + app.get("$mainUrl/engine/ajax/playlists.php?news_id=$id&xfield=playlist&time=${Date().time}") + .parsedSafe()?.response.let { + Jsoup.parse(it.toString()).select("ul > li").mapNotNull { mirror -> + links.add(fixUrl(mirror.attr("data-file"))) + } + } + } else { + links.add(iframeUrl) + } + } + + links.apmap { link -> + safeApiCall { + app.get(link, referer = "$mainUrl/").document.select("script").map { script -> + if (script.data().contains("var player = new Playerjs({")) { + val m3uLink = + script.data().substringAfterLast("file:\"").substringBefore("\",") + M3u8Helper.generateM3u8( + source = this.name, + streamUrl = m3uLink, + referer = "https://ashdi.vip/" + ).forEach(callback) + } + } + } + } + + return true + } + + data class Responses( + val success: Boolean?, + val response: String, + ) + +} \ No newline at end of file diff --git a/ExampleProvider/src/main/kotlin/com/example/ExamplePlugin.kt b/UakinoProvider/src/main/kotlin/com/lagradost/UakinoProviderPlugin.kt similarity index 75% rename from ExampleProvider/src/main/kotlin/com/example/ExamplePlugin.kt rename to UakinoProvider/src/main/kotlin/com/lagradost/UakinoProviderPlugin.kt index 8ddce48..47f1bcc 100644 --- a/ExampleProvider/src/main/kotlin/com/example/ExamplePlugin.kt +++ b/UakinoProvider/src/main/kotlin/com/lagradost/UakinoProviderPlugin.kt @@ -1,13 +1,14 @@ -package com.example + +package com.lagradost import com.lagradost.cloudstream3.plugins.CloudstreamPlugin import com.lagradost.cloudstream3.plugins.Plugin import android.content.Context @CloudstreamPlugin -class TestPlugin: Plugin() { +class UakinoProviderPlugin: Plugin() { override fun load(context: Context) { // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(ExampleProvider()) + registerMainAPI(UakinoProvider()) } } \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index b19ff67..55f36bf 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -37,15 +37,15 @@ subprojects { cloudstream { // when running through github workflow, GITHUB_REPOSITORY should contain current repository name // you can modify it to use other git hosting services, like gitlab - setRepo(System.getenv("GITHUB_REPOSITORY") ?: "https://github.com/user/repo") + setRepo(System.getenv("GITHUB_REPOSITORY") ?: "https://github.com/CakesTwix/cloudstream-extensions-uk") } android { - compileSdkVersion(30) + compileSdkVersion(33) defaultConfig { minSdk = 21 - targetSdk = 30 + targetSdk = 33 } compileOptions { @@ -77,7 +77,7 @@ subprojects { // https://github.com/recloudstream/cloudstream/blob/master/app/build.gradle implementation(kotlin("stdlib")) // adds standard kotlin features, like listOf, mapOf etc implementation("com.github.Blatzar:NiceHttp:0.3.2") // http library - implementation("org.jsoup:jsoup:1.13.1") // html parser + implementation("org.jsoup:jsoup:1.15.1") // html parser } }