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.
This commit is contained in:
CakesTwix 2023-02-15 15:14:48 +02:00
parent 23b11ebdd3
commit 3d781d88c0
Signed by: CakesTwix
GPG key ID: 7B11051D5CE19825
7 changed files with 224 additions and 53 deletions

View file

@ -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")
}

View file

@ -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<SearchResponse> {
return listOf<SearchResponse>()
}
}

View file

@ -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%"
}

View file

@ -1,2 +1,2 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest package="com.example"/> <manifest package="com.lagradost"/>

View file

@ -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<SearchResponse> {
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<Responses>()?.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<String>()
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<Responses>()?.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,
)
}

View file

@ -1,13 +1,14 @@
package com.example
package com.lagradost
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
import com.lagradost.cloudstream3.plugins.Plugin import com.lagradost.cloudstream3.plugins.Plugin
import android.content.Context import android.content.Context
@CloudstreamPlugin @CloudstreamPlugin
class TestPlugin: Plugin() { class UakinoProviderPlugin: Plugin() {
override fun load(context: Context) { override fun load(context: Context) {
// All providers should be added in this manner. Please don't edit the providers list directly. // All providers should be added in this manner. Please don't edit the providers list directly.
registerMainAPI(ExampleProvider()) registerMainAPI(UakinoProvider())
} }
} }

View file

@ -37,15 +37,15 @@ subprojects {
cloudstream { cloudstream {
// when running through github workflow, GITHUB_REPOSITORY should contain current repository name // when running through github workflow, GITHUB_REPOSITORY should contain current repository name
// you can modify it to use other git hosting services, like gitlab // 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 { android {
compileSdkVersion(30) compileSdkVersion(33)
defaultConfig { defaultConfig {
minSdk = 21 minSdk = 21
targetSdk = 30 targetSdk = 33
} }
compileOptions { compileOptions {
@ -77,7 +77,7 @@ subprojects {
// https://github.com/recloudstream/cloudstream/blob/master/app/build.gradle // https://github.com/recloudstream/cloudstream/blob/master/app/build.gradle
implementation(kotlin("stdlib")) // adds standard kotlin features, like listOf, mapOf etc implementation(kotlin("stdlib")) // adds standard kotlin features, like listOf, mapOf etc
implementation("com.github.Blatzar:NiceHttp:0.3.2") // http library 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
} }
} }