From d8eb73747d0fc41a13b8f7a5ac5081fcb31223de Mon Sep 17 00:00:00 2001 From: Rabbitkumarraj <124805547+Rabbitkumarraj@users.noreply.github.com> Date: Mon, 27 Apr 2026 14:31:25 +0530 Subject: [PATCH 1/9] Refactor ExampleProvider to ProtonMovies provider --- .../kotlin/com/example/ExampleProvider.kt | 44 ++++++++++++------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/ExampleProvider/src/main/kotlin/com/example/ExampleProvider.kt b/ExampleProvider/src/main/kotlin/com/example/ExampleProvider.kt index e788021aa8..fbec2741d6 100644 --- a/ExampleProvider/src/main/kotlin/com/example/ExampleProvider.kt +++ b/ExampleProvider/src/main/kotlin/com/example/ExampleProvider.kt @@ -1,21 +1,35 @@ -package com.example +package com.lagradost -import com.lagradost.cloudstream3.MainAPI -import com.lagradost.cloudstream3.SearchResponse -import com.lagradost.cloudstream3.TvType +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.utils.* +import org.jsoup.nodes.Element -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 +class ProtonMovies : MainAPI() { + override var mainUrl = "https://protonmovies.com" + override var name = "Proton Movies" override val hasMainPage = true + override var lang = "en" + override val hasQuickSearch = true - // This function gets called when you search for something override suspend fun search(query: String): List { - return listOf() + val document = app.get("$mainUrl/search?q=$query").document + return document.select(".iq-card").mapNotNull { + it.toSearchResult() + } + } + + private fun Element.toSearchResult(): SearchResponse { + val title = this.selectFirst(".iq-title a")?.text() ?: "" + val href = fixUrl(this.selectFirst(".iq-title a")?.attr("href") ?: "") + val posterUrl = this.selectFirst("img")?.attr("src") + val type = if (this.selectFirst(".movie-tag .badge")?.text()?.contains("Movie", true) == true) { + TvType.Movie + } else { + TvType.TvSeries + } + + return newMovieSearchResponse(title, href, type) { + this.posterUrl = posterUrl + } } -} \ No newline at end of file +} From 7c7ec7bfb17a851b2d33c4cc6e484ae54ce0e4cb Mon Sep 17 00:00:00 2001 From: Rabbitkumarraj <124805547+Rabbitkumarraj@users.noreply.github.com> Date: Mon, 27 Apr 2026 14:39:31 +0530 Subject: [PATCH 2/9] Fix formatting issue in ExampleProvider.kt --- ExampleProvider/src/main/kotlin/com/example/ExampleProvider.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/ExampleProvider/src/main/kotlin/com/example/ExampleProvider.kt b/ExampleProvider/src/main/kotlin/com/example/ExampleProvider.kt index fbec2741d6..5f9909c8ea 100644 --- a/ExampleProvider/src/main/kotlin/com/example/ExampleProvider.kt +++ b/ExampleProvider/src/main/kotlin/com/example/ExampleProvider.kt @@ -33,3 +33,4 @@ class ProtonMovies : MainAPI() { } } } + From 4d147ec1628e8df766ebacb9f5e0fefbc430652b Mon Sep 17 00:00:00 2001 From: Rabbitkumarraj <124805547+Rabbitkumarraj@users.noreply.github.com> Date: Mon, 27 Apr 2026 14:45:47 +0530 Subject: [PATCH 3/9] Refactor ExampleProvider package and search logic --- .../main/kotlin/com/example/ExampleProvider.kt | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/ExampleProvider/src/main/kotlin/com/example/ExampleProvider.kt b/ExampleProvider/src/main/kotlin/com/example/ExampleProvider.kt index 5f9909c8ea..413000f1c6 100644 --- a/ExampleProvider/src/main/kotlin/com/example/ExampleProvider.kt +++ b/ExampleProvider/src/main/kotlin/com/example/ExampleProvider.kt @@ -1,7 +1,9 @@ -package com.lagradost +package com.example import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.utils.* +import com.lagradost.cloudstream3.utils.AppUtils.toJson +import com.lagradost.cloudstream3.utils.ExtractorLink +import com.lagradost.cloudstream3.utils.loadExtractor import org.jsoup.nodes.Element class ProtonMovies : MainAPI() { @@ -12,7 +14,8 @@ class ProtonMovies : MainAPI() { override val hasQuickSearch = true override suspend fun search(query: String): List { - val document = app.get("$mainUrl/search?q=$query").document + val url = "$mainUrl/search?q=$query" + val document = app.get(url).document return document.select(".iq-card").mapNotNull { it.toSearchResult() } @@ -22,15 +25,12 @@ class ProtonMovies : MainAPI() { val title = this.selectFirst(".iq-title a")?.text() ?: "" val href = fixUrl(this.selectFirst(".iq-title a")?.attr("href") ?: "") val posterUrl = this.selectFirst("img")?.attr("src") - val type = if (this.selectFirst(".movie-tag .badge")?.text()?.contains("Movie", true) == true) { - TvType.Movie - } else { - TvType.TvSeries - } + val isMovie = this.selectFirst(".movie-tag .badge")?.text()?.contains("Movie", true) ?: true + + val type = if (isMovie) TvType.Movie else TvType.TvSeries return newMovieSearchResponse(title, href, type) { this.posterUrl = posterUrl } } } - From 7750aab697fbd93397fdcd9d872a00e7773bd3e9 Mon Sep 17 00:00:00 2001 From: Rabbitkumarraj <124805547+Rabbitkumarraj@users.noreply.github.com> Date: Mon, 27 Apr 2026 14:52:22 +0530 Subject: [PATCH 4/9] Update Kotlin Gradle plugin version to 2.3.0 --- build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 7dc3f4709a..78a803e931 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -15,7 +15,7 @@ buildscript { classpath("com.android.tools.build:gradle:8.7.3") // Cloudstream gradle plugin which makes everything work and builds plugins classpath("com.github.recloudstream:gradle:-SNAPSHOT") - classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:2.1.0") + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:2.3.0") } } @@ -88,4 +88,4 @@ subprojects { task("clean") { delete(rootProject.layout.buildDirectory) -} \ No newline at end of file +} From 71f8d3e5e236c08f79ac010da1acd87e87025613 Mon Sep 17 00:00:00 2001 From: Rabbitkumarraj <124805547+Rabbitkumarraj@users.noreply.github.com> Date: Mon, 27 Apr 2026 15:03:40 +0530 Subject: [PATCH 5/9] Update ExamplePlugin.kt --- .../main/kotlin/com/example/ExamplePlugin.kt | 57 +++++++++++++------ 1 file changed, 39 insertions(+), 18 deletions(-) diff --git a/ExampleProvider/src/main/kotlin/com/example/ExamplePlugin.kt b/ExampleProvider/src/main/kotlin/com/example/ExamplePlugin.kt index 79fffa5ea3..f28c11ddbe 100644 --- a/ExampleProvider/src/main/kotlin/com/example/ExamplePlugin.kt +++ b/ExampleProvider/src/main/kotlin/com/example/ExamplePlugin.kt @@ -1,25 +1,46 @@ package com.example -import android.content.Context -import androidx.appcompat.app.AppCompatActivity -import com.lagradost.cloudstream3.plugins.CloudstreamPlugin -import com.lagradost.cloudstream3.plugins.Plugin +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.utils.* +import org.jsoup.nodes.Element -@CloudstreamPlugin -class ExamplePlugin: Plugin() { - private var activity: AppCompatActivity? = null +class ExampleProvider : MainAPI() { + override var mainUrl = "https://protonmovies.com" + override var name = "ProtonMovies" + override val hasMainPage = true + override val supportedTypes = setOf(TvType.Movie, TvType.TvSeries) - override fun load(context: Context) { - activity = context as? AppCompatActivity - - // All providers should be added in this manner - registerMainAPI(ExampleProvider()) + override suspend fun search(query: String): List { + val url = "$mainUrl/search/$query" + val document = app.get(url).document + + return document.select(".iq-card").mapNotNull { + it.toSearchResult() + } + } - openSettings = { - val frag = BlankFragment(this) - activity?.let { - frag.show(it.supportFragmentManager, "Frag") - } + private fun Element.toSearchResult(): SearchResponse? { + val title = this.selectFirst(".iq-title a")?.text() ?: return null + val href = fixUrl(this.selectFirst(".iq-title a")?.attr("href") ?: return null) + val posterUrl = this.selectFirst("img")?.attr("src") + + return newMovieSearchResponse(title, href, TvType.Movie) { + this.posterUrl = posterUrl } } -} \ No newline at end of file + + override suspend fun load(url: String): LoadResponse? { + // TODO: Implement movie/series page loading + return null + } + + override suspend fun loadLinks( + data: String, + isCasting: Boolean, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ): Boolean { + // TODO: Implement link extraction + return false + } +} From 59b647434b22972e3b55bd72ae291810eb2ee1ac Mon Sep 17 00:00:00 2001 From: Rabbitkumarraj <124805547+Rabbitkumarraj@users.noreply.github.com> Date: Mon, 27 Apr 2026 15:04:56 +0530 Subject: [PATCH 6/9] Refactor ProtonMovies class to ExampleProvider --- .../kotlin/com/example/ExampleProvider.kt | 42 ++++++++++++------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/ExampleProvider/src/main/kotlin/com/example/ExampleProvider.kt b/ExampleProvider/src/main/kotlin/com/example/ExampleProvider.kt index 413000f1c6..f28c11ddbe 100644 --- a/ExampleProvider/src/main/kotlin/com/example/ExampleProvider.kt +++ b/ExampleProvider/src/main/kotlin/com/example/ExampleProvider.kt @@ -1,36 +1,46 @@ package com.example import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.utils.AppUtils.toJson -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.loadExtractor +import com.lagradost.cloudstream3.utils.* import org.jsoup.nodes.Element -class ProtonMovies : MainAPI() { - override var mainUrl = "https://protonmovies.com" - override var name = "Proton Movies" +class ExampleProvider : MainAPI() { + override var mainUrl = "https://protonmovies.com" + override var name = "ProtonMovies" override val hasMainPage = true - override var lang = "en" - override val hasQuickSearch = true + override val supportedTypes = setOf(TvType.Movie, TvType.TvSeries) override suspend fun search(query: String): List { - val url = "$mainUrl/search?q=$query" + val url = "$mainUrl/search/$query" val document = app.get(url).document + return document.select(".iq-card").mapNotNull { it.toSearchResult() } } - private fun Element.toSearchResult(): SearchResponse { - val title = this.selectFirst(".iq-title a")?.text() ?: "" - val href = fixUrl(this.selectFirst(".iq-title a")?.attr("href") ?: "") + private fun Element.toSearchResult(): SearchResponse? { + val title = this.selectFirst(".iq-title a")?.text() ?: return null + val href = fixUrl(this.selectFirst(".iq-title a")?.attr("href") ?: return null) val posterUrl = this.selectFirst("img")?.attr("src") - val isMovie = this.selectFirst(".movie-tag .badge")?.text()?.contains("Movie", true) ?: true - - val type = if (isMovie) TvType.Movie else TvType.TvSeries - return newMovieSearchResponse(title, href, type) { + return newMovieSearchResponse(title, href, TvType.Movie) { this.posterUrl = posterUrl } } + + override suspend fun load(url: String): LoadResponse? { + // TODO: Implement movie/series page loading + return null + } + + override suspend fun loadLinks( + data: String, + isCasting: Boolean, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ): Boolean { + // TODO: Implement link extraction + return false + } } From 9635a6ec1a5eb0e2a30cf277ab256f7d9e0f8543 Mon Sep 17 00:00:00 2001 From: Rabbitkumarraj <124805547+Rabbitkumarraj@users.noreply.github.com> Date: Mon, 27 Apr 2026 15:05:27 +0530 Subject: [PATCH 7/9] Refactor ExampleProvider to ExamplePlugin structure --- .../main/kotlin/com/example/ExamplePlugin.kt | 50 +++---------------- 1 file changed, 8 insertions(+), 42 deletions(-) diff --git a/ExampleProvider/src/main/kotlin/com/example/ExamplePlugin.kt b/ExampleProvider/src/main/kotlin/com/example/ExamplePlugin.kt index f28c11ddbe..4e0f5a41cf 100644 --- a/ExampleProvider/src/main/kotlin/com/example/ExamplePlugin.kt +++ b/ExampleProvider/src/main/kotlin/com/example/ExamplePlugin.kt @@ -1,46 +1,12 @@ package com.example -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.utils.* -import org.jsoup.nodes.Element - -class ExampleProvider : MainAPI() { - override var mainUrl = "https://protonmovies.com" - override var name = "ProtonMovies" - override val hasMainPage = true - override val supportedTypes = setOf(TvType.Movie, TvType.TvSeries) - - override suspend fun search(query: String): List { - val url = "$mainUrl/search/$query" - val document = app.get(url).document - - return document.select(".iq-card").mapNotNull { - it.toSearchResult() - } - } - - private fun Element.toSearchResult(): SearchResponse? { - val title = this.selectFirst(".iq-title a")?.text() ?: return null - val href = fixUrl(this.selectFirst(".iq-title a")?.attr("href") ?: return null) - val posterUrl = this.selectFirst("img")?.attr("src") - - return newMovieSearchResponse(title, href, TvType.Movie) { - this.posterUrl = posterUrl - } - } - - override suspend fun load(url: String): LoadResponse? { - // TODO: Implement movie/series page loading - return null - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - // TODO: Implement link extraction - return false +import com.lagradost.cloudstream3.plugins.CloudstreamPlugin +import com.lagradost.cloudstream3.plugins.Plugin +import android.content.Context + +@CloudstreamPlugin +class ExamplePlugin : Plugin() { + override fun load(context: Context) { + registerMainAPI(ExampleProvider()) } } From 45a24e66961d9da6972e5dcc443db74834929b65 Mon Sep 17 00:00:00 2001 From: Rabbitkumarraj <124805547+Rabbitkumarraj@users.noreply.github.com> Date: Mon, 27 Apr 2026 15:17:32 +0530 Subject: [PATCH 8/9] Add empty line after the class definition --- ExampleProvider/src/main/kotlin/com/example/ExampleProvider.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ExampleProvider/src/main/kotlin/com/example/ExampleProvider.kt b/ExampleProvider/src/main/kotlin/com/example/ExampleProvider.kt index f28c11ddbe..dc428d6bb6 100644 --- a/ExampleProvider/src/main/kotlin/com/example/ExampleProvider.kt +++ b/ExampleProvider/src/main/kotlin/com/example/ExampleProvider.kt @@ -44,3 +44,5 @@ class ExampleProvider : MainAPI() { return false } } + + From 39fc53618239f9330e41d4c2093b828a4fe69479 Mon Sep 17 00:00:00 2001 From: Rabbitkumarraj <124805547+Rabbitkumarraj@users.noreply.github.com> Date: Mon, 27 Apr 2026 15:26:15 +0530 Subject: [PATCH 9/9] Update gradle.properties --- gradle.properties | 1 + 1 file changed, 1 insertion(+) diff --git a/gradle.properties b/gradle.properties index 01b80d70cf..06260494d8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -17,3 +17,4 @@ org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 android.useAndroidX=true # Automatically convert third-party libraries to use AndroidX android.enableJetifier=true +systemProp.com.lagradost.cloudstream3.app.testmode=true