diff --git a/ElOstora/build.gradle.kts b/ElOstora/build.gradle.kts
new file mode 100644
index 00000000..f4a5f804
--- /dev/null
+++ b/ElOstora/build.gradle.kts
@@ -0,0 +1,25 @@
+// use an integer for version numbers
+version = 1
+
+
+cloudstream {
+ language = "ar"
+ // All of these properties are optional, you can safely remove them
+
+ description = "El Ostora TV livestreams"
+ authors = listOf("KingLucius")
+
+ /**
+ * Status int as the following:
+ * 0: Down
+ * 1: Ok
+ * 2: Slow
+ * 3: Beta only
+ * */
+ status = 1 // will be 3 if unspecified
+ tvTypes = listOf(
+ "Live",
+ )
+
+ iconUrl = "https://raw.githubusercontent.com/KingLucius/cs-hx/master/YacienTVProvider/icon.png"
+}
\ No newline at end of file
diff --git a/ElOstora/src/main/AndroidManifest.xml b/ElOstora/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..7fa00a96
--- /dev/null
+++ b/ElOstora/src/main/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/ElOstora/src/main/kotlin/com/elostoratv/ElOstoraTV.kt b/ElOstora/src/main/kotlin/com/elostoratv/ElOstoraTV.kt
new file mode 100644
index 00000000..e9dabc3f
--- /dev/null
+++ b/ElOstora/src/main/kotlin/com/elostoratv/ElOstoraTV.kt
@@ -0,0 +1,174 @@
+package com.elostoratv
+
+import android.icu.util.Calendar
+import android.util.Log
+import com.fasterxml.jackson.annotation.JsonProperty
+import com.lagradost.cloudstream3.*
+import com.lagradost.cloudstream3.utils.AppUtils.parseJson
+import com.lagradost.cloudstream3.utils.AppUtils.toJson
+import com.lagradost.cloudstream3.utils.ExtractorLink
+import com.lagradost.cloudstream3.utils.Qualities
+import com.lagradost.nicehttp.requestCreator
+
+class ElOstoraTV : MainAPI() {
+ override var lang = "ar"
+
+ override var name = "El Ostora TV"
+ override val usesWebView = false
+ override val hasMainPage = true
+ override val hasDownloadSupport = false
+ override val supportedTypes =
+ setOf(
+ TvType.Live
+ )
+ private val APIurl = "https://z420572.radwan.shop/api/v4_8.php"
+
+ override val mainPage = generateHomePage()
+
+ private fun generateHomePage() : List {
+
+ val homepage = mutableListOf()
+ val data = mapOf(
+ "main_id" to "1",
+ "id" to "",
+ "sub_id" to "0"
+ )
+ val decodedbody = getDecoded(data)
+ //Log.d("King", "decodedbody:$decodedbody")
+
+ parseJson(decodedbody).results?.map { element ->
+ homepage.add(mainPage(name = element.category_name, url = element.cid))
+ } ?: throw ErrorLoadingException("Invalid Json response")
+
+ return homepage
+ }
+
+ override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse {
+ val items = mutableListOf()
+ if (page <= 1) {
+ //Log.d("King", "request:$request")
+ val data = mapOf(
+ "id" to "",
+ "cat_id" to request.data
+ )
+ val decodedbody = getDecoded(data)
+ //Log.d("King", "decodedbody:$decodedbody")
+ val list = parseJson(decodedbody).results?.map { element ->
+ element.toSearchResponse(request)
+ } ?: throw ErrorLoadingException("Invalid Json response")
+ if (list.isNotEmpty()) items.add(HomePageList(request.name, list, true))
+ }
+ return newHomePageResponse(items)
+ }
+
+ private fun Channel.toSearchResponse(request: MainPageRequest): SearchResponse {
+ return LiveSearchResponse(
+ name = this.channel_title,
+ url = Data(id = id, channel_title = this.channel_title, channel_thumbnail = this.channel_thumbnail, category = request.name, channel_url = this.channel_url).toJson(),
+ this@ElOstoraTV.name,
+ TvType.Live,
+ this.channel_thumbnail,
+ )
+ }
+
+ override suspend fun load(url: String): LoadResponse {
+ //Log.d("King", "Load:$url")
+ val data = parseJson(url)
+
+ return LiveStreamLoadResponse(
+ name = data.channel_title,
+ url = Data(id = data.id, channel_title = data.channel_title, channel_thumbnail = data.channel_thumbnail, category = data.category, channel_url = urlfix(data.channel_url)).toJson(),
+ dataUrl = Data(id = data.id, channel_title = data.channel_title, channel_thumbnail = data.channel_thumbnail, category = data.category, channel_url = urlfix(data.channel_url)).toJson(),
+ apiName = name,
+ posterUrl = data.channel_thumbnail,
+ type = TvType.Live,
+ )
+ }
+ override suspend fun loadLinks(
+ data: String,
+ isCasting: Boolean,
+ subtitleCallback: (SubtitleFile) -> Unit,
+ callback: (ExtractorLink) -> Unit
+ ): Boolean {
+ //Log.d("King", "loadLinks:$data")
+ val data = parseJson(data)
+ callback.invoke(
+ ExtractorLink(
+ source = data.channel_title,
+ name = data.channel_title,
+ url = data.channel_url,
+ referer = "",
+ quality = Qualities.Unknown.value,
+ isM3u8 = true,
+ )
+ )
+ return true
+ }
+
+ private fun urlfix(url: String): String {
+ return url.replace("https://raw.githubusercontent.com/ostoratv/m3u8/master/update.m3u8?302","")
+ }
+ private fun getDecoded(payload: Map): String {
+
+ val t = Calendar.getInstance().timeInMillis.toString()
+
+ val client = app.baseClient.newBuilder()
+ .build()
+
+ val request = requestCreator(
+ method = "POST",
+ url = APIurl,
+ headers = mapOf(
+ "user-agent" to "Mozilla/5.0 (Linux; U; Android 10; en; YAL-L41 Api/HUAWEIYAL-L41) AppleWebKit/534.30 (KHTML, like Gecko) Version/5.0 Mobile Safari/534.30",
+ "Time" to t,
+ ),
+ data = payload,
+ )
+ val req = client.newCall(request).execute()
+ val decryptedBody = decrypt(req.body.string(), t.toCharArray())
+ //Log.d("King", "decryptedBody:" + decryptedBody)
+ return decryptedBody
+ }
+
+ private fun decrypt(str: String, key: CharArray): String {
+ val sb = java.lang.StringBuilder()
+ for (i in str.indices) {
+ val charAt = str[i]
+ val cArr: CharArray = key
+ sb.append((charAt.code xor cArr[i % cArr.size].code).toChar())
+ }
+ return sb.toString()
+ }
+
+ data class Channel(
+ @JsonProperty("id") val id: String,
+ @JsonProperty("channel_title") val channel_title: String,
+ @JsonProperty("channel_thumbnail") val channel_thumbnail: String,
+ @JsonProperty("channel_url") val channel_url: String,
+ @JsonProperty("download_url") val download_url: String,
+ @JsonProperty("agent") val agent: String,
+ @JsonProperty("video_player") val video_player: String,
+ )
+ data class Results(
+ @JsonProperty("data") val results: ArrayList? = arrayListOf(),
+ )
+
+ data class Categories(
+ @JsonProperty("data") val results: ArrayList? = arrayListOf(),
+ )
+ data class Category(
+ @JsonProperty("cid") val cid: String,
+ @JsonProperty("cat") val cat: String,
+ @JsonProperty("category_name") val category_name: String,
+ @JsonProperty("image") val image: String,
+ @JsonProperty("adp") val adp: String,
+ )
+
+ data class Data(
+ val id: String? = null,
+ val channel_title: String,
+ val channel_thumbnail: String,
+ val category: String,
+ val channel_url: String,
+ )
+}
\ No newline at end of file
diff --git a/ElOstora/src/main/kotlin/com/elostoratv/ElOstoraTVPlugin.kt b/ElOstora/src/main/kotlin/com/elostoratv/ElOstoraTVPlugin.kt
new file mode 100644
index 00000000..c3c98323
--- /dev/null
+++ b/ElOstora/src/main/kotlin/com/elostoratv/ElOstoraTVPlugin.kt
@@ -0,0 +1,11 @@
+package com.elostoratv
+import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
+import com.lagradost.cloudstream3.plugins.Plugin
+import android.content.Context
+
+@CloudstreamPlugin
+class ElOstoraTVPlugin: Plugin() {
+ override fun load(context: Context) {
+ registerMainAPI(ElOstoraTV())
+ }
+}
\ No newline at end of file