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 = "