From f802addd7796fb5ebfb9edcb01273996b28a0a2d Mon Sep 17 00:00:00 2001 From: CakesTwix Date: Mon, 13 Jan 2025 20:48:31 +0200 Subject: [PATCH] cikavaideya: Wew --- CikavaIdeyaProvider/build.gradle.kts | 27 +++ .../src/main/AndroidManifest.xml | 2 + .../com/lagradost/CikavaIdeyaProvider.kt | 211 ++++++++++++++++++ .../lagradost/CikavaIdeyaProviderPlugin.kt | 13 ++ 4 files changed, 253 insertions(+) create mode 100644 CikavaIdeyaProvider/build.gradle.kts create mode 100644 CikavaIdeyaProvider/src/main/AndroidManifest.xml create mode 100644 CikavaIdeyaProvider/src/main/kotlin/com/lagradost/CikavaIdeyaProvider.kt create mode 100644 CikavaIdeyaProvider/src/main/kotlin/com/lagradost/CikavaIdeyaProviderPlugin.kt diff --git a/CikavaIdeyaProvider/build.gradle.kts b/CikavaIdeyaProvider/build.gradle.kts new file mode 100644 index 0000000..8777131 --- /dev/null +++ b/CikavaIdeyaProvider/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 = "А це ідея)" + 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( + "Cartoon", + "TvSeries", + "Movie", + ) + + iconUrl = "https://www.google.com/s2/favicons?domain=cikava-ideya.top&sz=%size%" +} diff --git a/CikavaIdeyaProvider/src/main/AndroidManifest.xml b/CikavaIdeyaProvider/src/main/AndroidManifest.xml new file mode 100644 index 0000000..29aec9d --- /dev/null +++ b/CikavaIdeyaProvider/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/CikavaIdeyaProvider/src/main/kotlin/com/lagradost/CikavaIdeyaProvider.kt b/CikavaIdeyaProvider/src/main/kotlin/com/lagradost/CikavaIdeyaProvider.kt new file mode 100644 index 0000000..7183abd --- /dev/null +++ b/CikavaIdeyaProvider/src/main/kotlin/com/lagradost/CikavaIdeyaProvider.kt @@ -0,0 +1,211 @@ +package com.lagradost + +import com.lagradost.cloudstream3.Episode +import com.lagradost.cloudstream3.HomePageResponse +import com.lagradost.cloudstream3.LoadResponse +import com.lagradost.cloudstream3.MainAPI +import com.lagradost.cloudstream3.MainPageRequest +import com.lagradost.cloudstream3.SearchResponse +import com.lagradost.cloudstream3.SubtitleFile +import com.lagradost.cloudstream3.TvType +import com.lagradost.cloudstream3.app +import com.lagradost.cloudstream3.mainPageOf +import com.lagradost.cloudstream3.newHomePageResponse +import com.lagradost.cloudstream3.newMovieLoadResponse +import com.lagradost.cloudstream3.newMovieSearchResponse +import com.lagradost.cloudstream3.newTvSeriesLoadResponse +import com.lagradost.cloudstream3.toRatingInt +import com.lagradost.cloudstream3.utils.ExtractorLink +import com.lagradost.cloudstream3.utils.M3u8Helper +import org.json.JSONObject +import org.jsoup.nodes.Element + +class CikavaIdeyaProvider : MainAPI() { + + // Basic Info + override var mainUrl = "https://cikava-ideya.top" + override var name = "Цікава Ідея" + override val hasMainPage = true + override var lang = "uk" + override val hasDownloadSupport = true + override val supportedTypes = setOf( + TvType.Movie, + TvType.TvSeries, + TvType.Cartoon, + ) + + private var dle_login_hash = "" + + // Sections + override val mainPage = mainPageOf( + "$mainUrl/filmy/page/" to "Фільми", + "$mainUrl/serialy/page/" to "Серіали", + "$mainUrl/arthaus/page/" to "Артхаус", + "$mainUrl/cartoon/page/" to "Мультсеріали", + ) + + override suspend fun getMainPage( + page: Int, + request: MainPageRequest + ): HomePageResponse { + val document = app.get(request.data + page).document + + val home = document.select(".th-item").map { + it.toSearchResponse() + } + + // Need for search + dle_login_hash = + document + .body() + .selectFirst("script")!! + .html() + .substringAfterLast("dle_login_hash = '") + .substringBefore("';") + + return newHomePageResponse(request.name, home) + } + + private fun Element.toSearchResponse(): SearchResponse { + val title = this.selectFirst(".th-title")?.text()?.trim().toString() + val href = this.selectFirst(".th-in")?.attr("href").toString() + val posterUrl = mainUrl + this.selectFirst(".img-fit 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(".th-item").map { it.toSearchResponse() } + } + + // Detailed information + override suspend fun load(url: String): LoadResponse { + val document = app.get(url).document + // Parse info + val fullInfo = document.select(".flist li") + val title = document.selectFirst(".full h1")?.text()?.trim().toString() + val poster = mainUrl + document.selectFirst(".img-fit img")?.attr("src") + val banner = document.select(".fx-row").attr("data-img") + val tags = fullInfo[2].select("a").map { it.text() } + val year = fullInfo[0].select("li").text().toIntOrNull() + val playerUrl = document.select(".video-box iframe").attr("src") + + val tvType = if (tags.contains("Фільми") or tags.contains("Артхаус")) TvType.Movie else TvType.TvSeries + val description = document.selectFirst(".fdesc")?.text()?.trim() + // val trailer = document.selectFirst("div#trailer_place iframe")?.attr("src").toString() + val rating = document.select(".likes").text().dropLast(1).toRatingInt() + // val actors = fullInfo[4].select("a").map { it.text() } + + val recommendations = document.select(".th-rel").map { + it.toSearchResponse() + } + + // Grab player json from html + var playerJson = JSONObject() + document.select("script").forEach { + val scriptContent = it.html() + // Skip if no switches + if (!scriptContent.contains("switches = Object")) return@forEach + + val jsonStart = scriptContent.indexOf("Object(") + "Object(".length + val jsonEnd = scriptContent.lastIndexOf(");") + val jsonString = scriptContent.substring(jsonStart, jsonEnd) + + playerJson = JSONObject(jsonString) + // Log.d("CakesTwix-Debug", playerJson.getJSONObject("Player1").toString()) + } + + // Return to app + // Parse Episodes as Series + return if (tvType == TvType.TvSeries) { + val episodesList = mutableListOf() + + if (playerJson.has("Player1")) { + val player1 = playerJson.getJSONObject("Player1") + for (seasonKey in player1.keys()) { + + val episodes = player1.getJSONObject(seasonKey) + for (episodeKey in episodes.keys()) { + episodesList.add( + Episode( + episodes.getString(episodeKey), + episodeKey, + seasonKey.replace(" сезон","").toIntOrNull(), + episodeKey.replace(" серія","").toIntOrNull(), + ) + ) + } + } + } + + + newTvSeriesLoadResponse(title, url, TvType.TvSeries, episodesList) { + this.posterUrl = poster + this.backgroundPosterUrl = banner + 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, playerJson.getString("Player1")) { + this.posterUrl = poster + this.backgroundPosterUrl = banner + this.year = year + this.plot = description + this.tags = tags + this.rating = rating + // addActors(actors) + this.recommendations = recommendations + // addTrailer(trailer) + } + } + } + + // It works when I click to view the series + override suspend fun loadLinks( + data: String, + isCasting: Boolean, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ): Boolean { + val document = app.get(data).document + val m3u8Url = document.select("script").html() + .substringAfterLast("file:\"") + .substringBefore("\",") + M3u8Helper.generateM3u8( + source = "Цікава Ідея", + streamUrl = m3u8Url.replace("https://", "http://"), + referer = "https://tortuga.wtf/" + ).last().let(callback) + + val subtitleUrl = document.select("script").html() + .substringAfterLast("subtitle:\"") + .substringBefore("\",") + + if(subtitleUrl.isNullOrBlank()) return true + subtitleCallback.invoke( + SubtitleFile( + subtitleUrl.substringAfterLast("[").substringBefore("]"), + subtitleUrl.substringAfter("]") + ) + ) + return true + } +} diff --git a/CikavaIdeyaProvider/src/main/kotlin/com/lagradost/CikavaIdeyaProviderPlugin.kt b/CikavaIdeyaProvider/src/main/kotlin/com/lagradost/CikavaIdeyaProviderPlugin.kt new file mode 100644 index 0000000..3d551d4 --- /dev/null +++ b/CikavaIdeyaProvider/src/main/kotlin/com/lagradost/CikavaIdeyaProviderPlugin.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 CikavaIdeyaProviderPlugin: Plugin() { + override fun load(context: Context) { + // All providers should be added in this manner. Please don't edit the providers list directly. + registerMainAPI(CikavaIdeyaProvider()) + } +}