diff --git a/JavFreeProvider/build.gradle.kts b/JavFreeProvider/build.gradle.kts new file mode 100644 index 0000000..fdb3c6c --- /dev/null +++ b/JavFreeProvider/build.gradle.kts @@ -0,0 +1,24 @@ +// use an integer for version numbers +version = 1 + + +cloudstream { + // All of these properties are optional, you can safely remove them + + description = "The best free NSFW site" + authors = listOf("Jace") + + /** + * 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("NSFW") +} diff --git a/JavFreeProvider/src/main/AndroidManifest.xml b/JavFreeProvider/src/main/AndroidManifest.xml new file mode 100644 index 0000000..1863f02 --- /dev/null +++ b/JavFreeProvider/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/JavFreeProvider/src/main/kotlin/com/jacekun/JavFreeProvider.kt b/JavFreeProvider/src/main/kotlin/com/jacekun/JavFreeProvider.kt new file mode 100644 index 0000000..c4195d5 --- /dev/null +++ b/JavFreeProvider/src/main/kotlin/com/jacekun/JavFreeProvider.kt @@ -0,0 +1,181 @@ +package com.jacekun + +import com.fasterxml.jackson.annotation.JsonProperty +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.app +import com.lagradost.cloudstream3.mvvm.logError +import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson +import com.lagradost.cloudstream3.utils.ExtractorLink +import com.lagradost.cloudstream3.utils.loadExtractor +import org.jsoup.Jsoup + +class JavFreeProvider : MainAPI() { + private val tvType = TvType.NSFW + override var name = "JavFree" + override var mainUrl = "https://javfree.sh" + override val supportedTypes: Set get() = setOf(tvType) + override val hasDownloadSupport: Boolean get() = false + override val hasMainPage: Boolean get() = true + override val hasQuickSearch: Boolean get() = false + + private data class ResponseJson( + @JsonProperty("list") val list: List? + ) + private data class ResponseData( + @JsonProperty("url") val file: String?, + @JsonProperty("server") val server: String?, + @JsonProperty("active") val active: Int? + ) + + fun String.cleanText() : String = this.trim().removePrefix("Watch JAV Free").removeSuffix("HD Free Online on JAVFree.SH").trim() + + override suspend fun getMainPage( + page: Int, + request: MainPageRequest + ): HomePageResponse { + val html = app.get(mainUrl).text + val document = Jsoup.parse(html) + val all = ArrayList() + + val mainbody = document.getElementsByTag("body").select("div#page") + .select("div#content").select("div#primary") + .select("main") + + mainbody.select("section").forEach { it2 -> + // Fetch row title + val title = it2?.select("h2.widget-title")?.text() ?: "Unnamed Row" + // Fetch list of items and map + it2.select("div.videos-list") + .select("article").let { inner -> + + val elements: List = inner.map { + + val aa = it.select("a").firstOrNull() + val link = fixUrl(aa?.attr("href") ?: "") + val name = aa?.attr("title") ?: "" + + var image = aa?.select("div")?.select("img")?.attr("data-src") ?: "" + if (image == "") { + image = aa?.select("div")?.select("video")?.attr("poster") ?: "" + } + val year = null + + MovieSearchResponse( + name = name, + url = link, + apiName = this.name, + type = tvType, + posterUrl = image, + year = year + ) + } + + all.add( + HomePageList( + name = title, + list = elements, + isHorizontalImages = true + ) + ) + } + } + return HomePageResponse(all) + } + + override suspend fun search(query: String): List { + val url = "$mainUrl/search/movie/${query}" + val html = app.get(url).text + val document = Jsoup.parse(html).select("div.videos-list").select("article[id^=post]") + + return document.map { + val aa = it.select("a") + val title = aa.attr("title") + val href = fixUrl(aa.attr("href")) + val year = null + val image = aa.select("div.post-thumbnail.thumbs-rotation") + .select("img").attr("data-src") + + MovieSearchResponse( + name = title, + url = href, + apiName = this.name, + type = tvType, + posterUrl = image, + year = year + ) + } + } + + override suspend fun load(url: String): LoadResponse { + val doc = app.get(url).document + //Log.i(this.name, "Result => (url) ${url}") + val poster = doc.select("meta[property=og:image]").firstOrNull()?.attr("content") + val title = doc.select("meta[name=title]").firstOrNull()?.attr("content")?.toString()?.cleanText() ?: "" + val descript = doc.select("meta[name=description]").firstOrNull()?.attr("content")?.cleanText() + + val body = doc.getElementsByTag("body") + val yearElem = body + .select("div#page > div#content > div#primary > main > article") + .select("div.entry-content > div.tab-content > div#video-about > div#video-date") + //Log.i(this.name, "Result => (yearElem) ${yearElem}") + val year = yearElem.text().trim().takeLast(4).toIntOrNull() + + var streamUrl = body + .select("div#page > div#content > div#primary > main > article > header > div > div > div > script") + .toString() + if (streamUrl.isNotEmpty()) { + val startS = "