diff --git a/CanliTV/build.gradle.kts b/CanliTV/build.gradle.kts new file mode 100644 index 00000000..4782f0df --- /dev/null +++ b/CanliTV/build.gradle.kts @@ -0,0 +1,18 @@ +version = 1 + +cloudstream { + authors = listOf("keyiflerolsun") + language = "tr" + description = "Canlı TV" + + /** + * 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://www.google.com/s2/favicons?domain=tr.canlitv.team&sz=%size%" +} \ No newline at end of file diff --git a/CanliTV/src/main/AndroidManifest.xml b/CanliTV/src/main/AndroidManifest.xml new file mode 100644 index 00000000..1ffaf63d --- /dev/null +++ b/CanliTV/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/CanliTV/src/main/kotlin/com/keyiflerolsun/CanliTV.kt b/CanliTV/src/main/kotlin/com/keyiflerolsun/CanliTV.kt new file mode 100644 index 00000000..d2755ff3 --- /dev/null +++ b/CanliTV/src/main/kotlin/com/keyiflerolsun/CanliTV.kt @@ -0,0 +1,339 @@ +package com.keyiflerolsun + +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 java.io.InputStream + +class CanliTV : MainAPI() { + override var mainUrl = "https://raw.githubusercontent.com/keyiflerolsun/IPTV_YenirMi/main/Kanallar/KekikAkademi.m3u" + override var name = "CanliTV" + override val hasMainPage = true + override var lang = "tr" + override val hasChromecastSupport = true + override val supportedTypes = setOf(TvType.Live) + + override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse { + val data = IptvPlaylistParser().parseM3U(app.get(mainUrl).text) + return HomePageResponse( + data.items + .groupBy { it.attributes["group-title"] } + .map { group -> + val title = group.key ?: "" + val show = + group.value.map { channel -> + val streamurl = channel.url.toString() + val channelname = channel.title.toString() + val posterurl = channel.attributes["tvg-logo"].toString() + // val nation = channel.attributes["group-title"].toString() + val nation = "TR" + LiveSearchResponse( + channelname, + LoadData(streamurl, channelname, posterurl, nation).toJson(), + this@CanliTV.name, + TvType.Live, + posterurl, + // lang = channel.attributes["group-title"] + lang = "TR" + ) + } + HomePageList(title, show, isHorizontalImages = true) + } + ) + } + + override suspend fun search(query: String): List { + val data = IptvPlaylistParser().parseM3U(app.get(mainUrl).text) + + return data.items + .filter { it.attributes["tvg-id"]?.contains(query) ?: false } + .map { channel -> + val streamurl = channel.url.toString() + val channelname = channel.attributes["tvg-id"].toString() + val posterurl = channel.attributes["tvg-logo"].toString() + val nation = channel.attributes["group-title"].toString() + LiveSearchResponse( + channelname, + LoadData(streamurl, channelname, posterurl, nation).toJson(), + this@CanliTV.name, + TvType.Live, + posterurl, + ) + } + } + + override suspend fun load(url: String): LoadResponse { + val data = parseJson(url) + return LiveStreamLoadResponse( + data.title, + data.url, + this.name, + url, + data.poster, + plot = data.nation + ) + } + + data class LoadData(val url: String, val title: String, val poster: String, val nation: String) + + override suspend fun loadLinks( + data: String, + isCasting: Boolean, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ): Boolean { + val loadData = parseJson(data) + callback.invoke( + ExtractorLink( + this.name, + loadData.title, + loadData.url, + "", + Qualities.Unknown.value, + isM3u8 = true + ) + ) + return true + } +} + +data class Playlist( + val items: List = emptyList(), +) + +data class PlaylistItem( + val title: String? = null, + val attributes: Map = emptyMap(), + val headers: Map = emptyMap(), + val url: String? = null, + val userAgent: String? = null, +) + +class IptvPlaylistParser { + + /** + * Parse M3U8 string into [Playlist] + * + * @param content M3U8 content string. + * @throws PlaylistParserException if an error occurs. + */ + fun parseM3U(content: String): Playlist { + return parseM3U(content.byteInputStream()) + } + + /** + * Parse M3U8 content [InputStream] into [Playlist] + * + * @param input Stream of input data. + * @throws PlaylistParserException if an error occurs. + */ + @Throws(PlaylistParserException::class) + fun parseM3U(input: InputStream): Playlist { + val reader = input.bufferedReader() + + if (!reader.readLine().isExtendedM3u()) { + throw PlaylistParserException.InvalidHeader() + } + + val playlistItems: MutableList = mutableListOf() + var currentIndex = 0 + + var line: String? = reader.readLine() + + while (line != null) { + if (line.isNotEmpty()) { + if (line.startsWith(EXT_INF)) { + val title = line.getTitle() + val attributes = line.getAttributes() + playlistItems.add(PlaylistItem(title, attributes)) + } else if (line.startsWith(EXT_VLC_OPT)) { + val item = playlistItems[currentIndex] + val userAgent = line.getTagValue("http-user-agent") + val referrer = line.getTagValue("http-referrer") + val headers = + if (referrer != null) { + item.headers + mapOf("referrer" to referrer) + } else item.headers + playlistItems[currentIndex] = + item.copy(userAgent = userAgent, headers = headers) + } else { + if (!line.startsWith("#")) { + val item = playlistItems[currentIndex] + val url = line.getUrl() + val userAgent = line.getUrlParameter("user-agent") + val referrer = line.getUrlParameter("referer") + val urlHeaders = + if (referrer != null) { + item.headers + mapOf("referrer" to referrer) + } else item.headers + playlistItems[currentIndex] = + item.copy( + url = url, + headers = item.headers + urlHeaders, + userAgent = userAgent + ) + currentIndex++ + } + } + } + + line = reader.readLine() + } + return Playlist(playlistItems) + } + + /** Replace "" (quotes) from given string. */ + private fun String.replaceQuotesAndTrim(): String { + return replace("\"", "").trim() + } + + /** Check if given content is valid M3U8 playlist. */ + private fun String.isExtendedM3u(): Boolean = startsWith(EXT_M3U) + + /** + * Get title of media. + * + * Example:- + * + * Input: + * ``` + * #EXTINF:-1 tvg-id="1234" group-title="Kids" tvg-logo="url/to/logo", Title + * ``` + * + * Result: Title + */ + private fun String.getTitle(): String? { + return split(",").lastOrNull()?.replaceQuotesAndTrim() + } + + /** + * Get media url. + * + * Example:- + * + * Input: + * ``` + * https://example.com/sample.m3u8|user-agent="Custom" + * ``` + * + * Result: https://example.com/sample.m3u8 + */ + private fun String.getUrl(): String? { + return split("|").firstOrNull()?.replaceQuotesAndTrim() + } + + /** + * Get url parameters. + * + * Example:- + * + * Input: + * ``` + * http://192.54.104.122:8080/d/abcdef/video.mp4|User-Agent=Mozilla&Referer=CustomReferrer + * ``` + * + * Result will be equivalent to kotlin map: + * ```Kotlin + * mapOf( + * "User-Agent" to "Mozilla", + * "Referer" to "CustomReferrer" + * ) + * ``` + */ + private fun String.getUrlParameters(): Map { + val urlRegex = Regex("^(.*)\\|", RegexOption.IGNORE_CASE) + val headersString = replace(urlRegex, "").replaceQuotesAndTrim() + return headersString + .split("&") + .mapNotNull { + val pair = it.split("=") + if (pair.size == 2) pair.first() to pair.last() else null + } + .toMap() + } + + /** + * Get url parameter with key. + * + * Example:- + * + * Input: + * ``` + * http://192.54.104.122:8080/d/abcdef/video.mp4|User-Agent=Mozilla&Referer=CustomReferrer + * ``` + * + * If given key is `user-agent`, then + * + * Result: Mozilla + */ + private fun String.getUrlParameter(key: String): String? { + val urlRegex = Regex("^(.*)\\|", RegexOption.IGNORE_CASE) + val keyRegex = Regex("$key=(\\w[^&]*)", RegexOption.IGNORE_CASE) + val paramsString = replace(urlRegex, "").replaceQuotesAndTrim() + return keyRegex.find(paramsString)?.groups?.get(1)?.value + } + + /** + * Get attributes from `#EXTINF` tag as Map. + * + * Example:- + * + * Input: + * ``` + * #EXTINF:-1 tvg-id="1234" group-title="Kids" tvg-logo="url/to/logo", Title + * ``` + * + * Result will be equivalent to kotlin map: + * ```Kotlin + * mapOf( + * "tvg-id" to "1234", + * "group-title" to "Kids", + * "tvg-logo" to "url/to/logo" + * ) + * ``` + */ + private fun String.getAttributes(): Map { + val extInfRegex = Regex("(#EXTINF:.?[0-9]+)", RegexOption.IGNORE_CASE) + val attributesString = replace(extInfRegex, "").replaceQuotesAndTrim().split(",").first() + return attributesString + .split(Regex("\\s")) + .mapNotNull { + val pair = it.split("=") + if (pair.size == 2) pair.first() to pair.last().replaceQuotesAndTrim() else null + } + .toMap() + } + + /** + * Get value from a tag. + * + * Example:- + * + * Input: + * ``` + * #EXTVLCOPT:http-referrer=http://example.com/ + * ``` + * + * Result: http://example.com/ + */ + private fun String.getTagValue(key: String): String? { + val keyRegex = Regex("$key=(.*)", RegexOption.IGNORE_CASE) + return keyRegex.find(this)?.groups?.get(1)?.value?.replaceQuotesAndTrim() + } + + companion object { + const val EXT_M3U = "#EXTM3U" + const val EXT_INF = "#EXTINF" + const val EXT_VLC_OPT = "#EXTVLCOPT" + } +} + +/** Exception thrown when an error occurs while parsing playlist. */ +sealed class PlaylistParserException(message: String) : Exception(message) { + + /** Exception thrown if given file content is not valid. */ + class InvalidHeader : + PlaylistParserException("Invalid file header. Header doesn't start with #EXTM3U") +} diff --git a/CanliTV/src/main/kotlin/com/keyiflerolsun/CanliTVPlugin.kt b/CanliTV/src/main/kotlin/com/keyiflerolsun/CanliTVPlugin.kt new file mode 100644 index 00000000..4ecbcc01 --- /dev/null +++ b/CanliTV/src/main/kotlin/com/keyiflerolsun/CanliTVPlugin.kt @@ -0,0 +1,12 @@ +package com.keyiflerolsun + +import com.lagradost.cloudstream3.plugins.CloudstreamPlugin +import com.lagradost.cloudstream3.plugins.Plugin +import android.content.Context + +@CloudstreamPlugin +class CanliTVPlugin: Plugin() { + override fun load(context: Context) { + registerMainAPI(CanliTV()) + } +} \ No newline at end of file diff --git a/FullHDFilmizlesene/build.gradle.kts b/FullHDFilmizlesene/build.gradle.kts new file mode 100644 index 00000000..f53b71ac --- /dev/null +++ b/FullHDFilmizlesene/build.gradle.kts @@ -0,0 +1,18 @@ +version = 1 + +cloudstream { + authors = listOf("keyiflerolsun") + language = "tr" + description = "Sinema zevkini evinize kadar getirdik. Türkiye'nin lider Film sitesinde, en yeni filmleri Full HD izleyin." + + /** + * Status int as the following: + * 0: Down + * 1: Ok + * 2: Slow + * 3: Beta only + **/ + status = 1 // will be 3 if unspecified + tvTypes = listOf("Movie") + iconUrl = "https://www.google.com/s2/favicons?domain=www.fullhdfilmizlesene.pw&sz=%size%" +} \ No newline at end of file diff --git a/FullHDFilmizlesene/src/main/AndroidManifest.xml b/FullHDFilmizlesene/src/main/AndroidManifest.xml new file mode 100644 index 00000000..1ffaf63d --- /dev/null +++ b/FullHDFilmizlesene/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/FullHDFilmizlesene/src/main/kotlin/com/keyiflerolsun/FullHDFilmizlesene.kt b/FullHDFilmizlesene/src/main/kotlin/com/keyiflerolsun/FullHDFilmizlesene.kt new file mode 100644 index 00000000..ee724239 --- /dev/null +++ b/FullHDFilmizlesene/src/main/kotlin/com/keyiflerolsun/FullHDFilmizlesene.kt @@ -0,0 +1,105 @@ +package com.keyiflerolsun + +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.LoadResponse.Companion.addActors +import com.lagradost.cloudstream3.utils.ExtractorLink +import com.lagradost.cloudstream3.utils.M3u8Helper +import com.lagradost.cloudstream3.utils.loadExtractor +import org.jsoup.nodes.Element +import android.util.Log + +class FullHDFilmizlesene : MainAPI() { + override var mainUrl = "https://www.fullhdfilmizlesene.pw" + override var name = "FullHDFilmizlesene" + override val hasMainPage = true + override var lang = "tr" + override val hasQuickSearch = true + override val hasDownloadSupport = true + override val supportedTypes = setOf(TvType.Movie) + + override val mainPage = + mainPageOf( + "$mainUrl/en-cok-izlenen-filmler-izle-hd/" to "En Çok izlenen Filmler", + "$mainUrl/filmizle/imdb-puani-yuksek-filmler-izle-1/" to "IMDB Puanı Yüksek Filmler", + "$mainUrl/filmizle/bilim-kurgu-filmleri-izle-1/" to "Bilim Kurgu Filmleri", + "$mainUrl/filmizle/komedi-filmleri-izle-2/" to "Komedi Filmleri", + ) + + override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse { + val document = app.get(request.data + page).document + val home = document.select("li.film").mapNotNull { it.toSearchResult() } + return newHomePageResponse(request.name, home) + } + + private fun Element.toSearchResult(): SearchResponse? { + val title = this.selectFirst("span.film-title")?.text() ?: return null + val href = fixUrlNull(this.selectFirst("a")?.attr("href")) ?: return null + val posterUrl = fixUrlNull(this.selectFirst("img")?.attr("data-src")) + return newMovieSearchResponse(title, href, TvType.Movie) { this.posterUrl = posterUrl } + } + + override suspend fun search(query: String): List { + val document = app.get("$mainUrl/arama/$query").document + return document.select("li.film").mapNotNull { it.toSearchResult() } + } + + override suspend fun load(url: String): LoadResponse? { + val document = app.get(url).document + + val title = document.selectFirst("div[class=izle-titles]")?.text()?.trim() ?: return null + val poster = fixUrlNull(document.selectFirst("div img")?.attr("data-src")) + val year = document.selectFirst("div.dd a.category")?.text()?.split(" ")?.get(0)?.trim()?.toIntOrNull() + val description = document.selectFirst("div.ozet-ic > p")?.text()?.trim() + val tags = document.select("a[rel='category tag']").map { it.text() } + val rating = document.selectFirst("div.puanx-puan")?.text()?.trim()?.split(".")?.get(0)?.toRatingInt() + val duration = document.selectFirst("span.sure")?.text()?.split(" ")?.get(0)?.trim()?.toRatingInt() + val recommendations = document.select("div.izle-alt-content:nth-of-type(3) ul li").mapNotNull { + val recName = it.selectFirst("h2.film-title")?.text() ?: return@mapNotNull null + val recHref = fixUrlNull(it.selectFirst("a")?.attr("href")) ?: return@mapNotNull null + val recPosterUrl = fixUrlNull(it.selectFirst("img")?.attr("src")) + newTvSeriesSearchResponse(recName, recHref, TvType.TvSeries) { + this.posterUrl = recPosterUrl + } + } + val actors = document.select("div.film-info ul li:nth-child(2) a > span").map { + Actor(it.text()) + } + + return newMovieLoadResponse(title, url, TvType.Movie, url) { + this.posterUrl = poster + this.year = year + this.plot = description + this.tags = tags + this.rating = rating + this.duration = duration + this.recommendations = recommendations + addActors(actors) + } + } + + override suspend fun loadLinks( + data: String, + isCasting: Boolean, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ): Boolean { + + val document = app.get(data).document + val iframe = document.selectFirst("div#plx iframe")?.attr("src") ?: return false + + val rapid = app.get(iframe, referer = "$mainUrl/").text + val pattern = """file": "(.*)",""".toRegex() + val matchResult = pattern.find(rapid) + val extractedValue = matchResult?.groups?.get(1)?.value ?: return false + + // val encoded = extractedValue.toByteArray(Charsets.UTF_8) + // val decoded = String(encoded, Charsets.UTF_8) + + val bytes = extractedValue.split("""\\x""").filter { it.isNotEmpty() }.map { it.toInt(16).toByte() }.toByteArray() + val decoded = String(bytes, Charsets.UTF_8) + + loadExtractor(decoded, "$mainUrl/", subtitleCallback, callback) + + return true + } +} diff --git a/FullHDFilmizlesene/src/main/kotlin/com/keyiflerolsun/FullHDFilmizlesenePlugin.kt b/FullHDFilmizlesene/src/main/kotlin/com/keyiflerolsun/FullHDFilmizlesenePlugin.kt new file mode 100644 index 00000000..e44f73fa --- /dev/null +++ b/FullHDFilmizlesene/src/main/kotlin/com/keyiflerolsun/FullHDFilmizlesenePlugin.kt @@ -0,0 +1,12 @@ +package com.keyiflerolsun + +import com.lagradost.cloudstream3.plugins.CloudstreamPlugin +import com.lagradost.cloudstream3.plugins.Plugin +import android.content.Context + +@CloudstreamPlugin +class FullHDFilmizlesenePlugin: Plugin() { + override fun load(context: Context) { + registerMainAPI(FullHDFilmizlesene()) + } +} \ No newline at end of file diff --git a/HDFilmCehennemi/build.gradle.kts b/HDFilmCehennemi/build.gradle.kts new file mode 100644 index 00000000..c9250f74 --- /dev/null +++ b/HDFilmCehennemi/build.gradle.kts @@ -0,0 +1,18 @@ +version = 11 + +cloudstream { + authors = listOf("Hexated") + language = "tr" + description = "Türkiye'nin en hızlı hd film izleme sitesi" + + /** + * Status int as the following: + * 0: Down + * 1: Ok + * 2: Slow + * 3: Beta only + **/ + status = 1 // will be 3 if unspecified + tvTypes = listOf("Movie") + iconUrl = "https://www.google.com/s2/favicons?domain=hdfilmcehennemi.live&sz=%size%" +} \ No newline at end of file diff --git a/Hdfilmcehennemi/src/main/AndroidManifest.xml b/HDFilmCehennemi/src/main/AndroidManifest.xml similarity index 100% rename from Hdfilmcehennemi/src/main/AndroidManifest.xml rename to HDFilmCehennemi/src/main/AndroidManifest.xml diff --git a/Hdfilmcehennemi/src/main/kotlin/com/hexated/Hdfilmcehennemi.kt b/HDFilmCehennemi/src/main/kotlin/com/keyiflerolsun/HDFilmCehennemi.kt similarity index 59% rename from Hdfilmcehennemi/src/main/kotlin/com/hexated/Hdfilmcehennemi.kt rename to HDFilmCehennemi/src/main/kotlin/com/keyiflerolsun/HDFilmCehennemi.kt index cce3f58e..d08f43f8 100644 --- a/Hdfilmcehennemi/src/main/kotlin/com/hexated/Hdfilmcehennemi.kt +++ b/HDFilmCehennemi/src/main/kotlin/com/keyiflerolsun/HDFilmCehennemi.kt @@ -9,35 +9,30 @@ import com.lagradost.cloudstream3.utils.* import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson import org.jsoup.nodes.Element -class Hdfilmcehennemi : MainAPI() { - override var mainUrl = "https://www.hdfilmcehennemi.life" - override var name = "hdfilmcehennemi" - override val hasMainPage = true - override var lang = "tr" - override val hasQuickSearch = true +class HDFilmCehennemi : MainAPI() { + override var mainUrl = "https://www.hdfilmcehennemi.life" + override var name = "HDFilmCehennemi" + override val hasMainPage = true + override var lang = "tr" + override val hasQuickSearch = true override val hasDownloadSupport = true - override val supportedTypes = setOf( - TvType.Movie, - TvType.TvSeries, - ) + override val supportedTypes = setOf(TvType.Movie) - override val mainPage = mainPageOf( - "$mainUrl/category/tavsiye-filmler-izle2/page/" to "Tavsiye Filmler Kategorisi", - "$mainUrl/yabancidiziizle-1/page/" to "Son Eklenen Yabancı Diziler", - "$mainUrl/imdb-7-puan-uzeri-filmler/page/" to "Imdb 7+ Filmler", - "$mainUrl/en-cok-yorumlananlar/page/" to "En Çok Yorumlananlar", - "$mainUrl/en-cok-begenilen-filmleri-izle/page/" to "En Çok Beğenilenler", - ) + override val mainPage = + mainPageOf( + "$mainUrl/category/tavsiye-filmler-izle2/page/" to "Tavsiye Filmler Kategorisi", + "$mainUrl/yabancidiziizle-1/page/" to "Son Eklenen Yabancı Diziler", + "$mainUrl/imdb-7-puan-uzeri-filmler/page/" to "IMDB 7+ Filmler", + "$mainUrl/en-cok-yorumlananlar/page/" to "En Çok Yorumlananlar", + "$mainUrl/en-cok-begenilen-filmleri-izle/page/" to "En Çok Beğenilenler", + ) - override suspend fun getMainPage( - page: Int, - request: MainPageRequest - ): HomePageResponse { + override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse { val document = app.get(request.data + page).document - val home = document.select("div.card-body div.row div.col-6.col-sm-3.poster-container") - .mapNotNull { - it.toSearchResult() - } + val home = + document + .select("div.card-body div.row div.col-6.col-sm-3.poster-container") + .mapNotNull { it.toSearchResult() } return newHomePageResponse(request.name, home) } @@ -45,10 +40,7 @@ class Hdfilmcehennemi : MainAPI() { val title = this.selectFirst("h2.title")?.text() ?: return null val href = fixUrlNull(this.selectFirst("a")?.attr("href")) ?: return null val posterUrl = fixUrlNull(this.selectFirst("img")?.attr("data-src")) - return newMovieSearchResponse(title, href, TvType.Movie) { - this.posterUrl = posterUrl - } - + return newMovieSearchResponse(title, href, TvType.Movie) { this.posterUrl = posterUrl } } private fun Media.toSearchResponse(): SearchResponse? { @@ -65,35 +57,43 @@ class Hdfilmcehennemi : MainAPI() { override suspend fun search(query: String): List { return app.post( - "$mainUrl/search/", - data = mapOf("query" to query), - referer = "$mainUrl/", - headers = mapOf( - "Accept" to "application/json, text/javascript, */*; q=0.01", - "X-Requested-With" to "XMLHttpRequest" + "$mainUrl/search/", + data = mapOf("query" to query), + referer = "$mainUrl/", + headers = + mapOf( + "Accept" to "application/json, text/javascript, */*; q=0.01", + "X-Requested-With" to "XMLHttpRequest" + ) ) - ).parsedSafe()?.result?.mapNotNull { media -> - media.toSearchResponse() - } ?: throw ErrorLoadingException("Invalid Json reponse") + .parsedSafe() + ?.result + ?.mapNotNull { media -> media.toSearchResponse() } + ?: throw ErrorLoadingException("Invalid Json reponse") } override suspend fun load(url: String): LoadResponse? { val document = app.get(url).document - val title = document.selectFirst("div.card-header > h1, div.card-header > h2")?.text() - ?.removeSuffix("Filminin Bilgileri")?.trim() - ?: return null + val title = + document + .selectFirst("div.card-header > h1, div.card-header > h2") + ?.text() + ?.removeSuffix("Filminin Bilgileri") + ?.trim() ?: return null val poster = fixUrlNull(document.select("img.img-fluid").lastOrNull()?.attr("src")) val tags = document.select("div.mb-0.lh-lg div:nth-child(5) a").map { it.text() } val year = document.selectFirst("div.mb-0.lh-lg div:nth-child(4) a")?.text()?.trim()?.toIntOrNull() - val tvType = if (document.select("nav#seasonsTabs").isNullOrEmpty() - ) TvType.Movie else TvType.TvSeries + val tvType = + if (document.select("nav#seasonsTabs").isNullOrEmpty()) TvType.Movie + else TvType.TvSeries val description = document.selectFirst("article.text-white > p")?.text()?.trim() val rating = document.selectFirst("div.rating-votes div.rate span")?.text()?.toRatingInt() - val actors = document.select("div.mb-0.lh-lg div:last-child a.chip").map { - Actor(it.text(), it.select("img").attr("src")) - } + val actors = + document.select("div.mb-0.lh-lg div:last-child a.chip").map { + Actor(it.text(), it.select("img").attr("src")) + } val recommendations = document.select("div.swiper-wrapper div.poster.poster-pop").mapNotNull { val recName = it.selectFirst("h2.title")?.text() ?: return@mapNotNull null @@ -107,24 +107,30 @@ class Hdfilmcehennemi : MainAPI() { return if (tvType == TvType.TvSeries) { val trailer = - document.selectFirst("button.btn.btn-fragman.btn-danger")?.attr("data-trailer") - ?.let { - "https://www.youtube.com/embed/$it" - } - val episodes = document.select("div#seasonsTabs-tabContent div.card-list-item").map { - val href = it.select("a").attr("href") - val name = it.select("h3").text().trim() - val episode = it.select("h3").text().let { num -> - Regex("Sezon\\s?([0-9]+).").find(num)?.groupValues?.getOrNull(1)?.toIntOrNull() + document + .selectFirst("button.btn.btn-fragman.btn-danger") + ?.attr("data-trailer") + ?.let { "https://www.youtube.com/embed/$it" } + val episodes = + document.select("div#seasonsTabs-tabContent div.card-list-item").map { + val href = it.select("a").attr("href") + val name = it.select("h3").text().trim() + val episode = + it.select("h3").text().let { num -> + Regex("Sezon\\s?([0-9]+).") + .find(num) + ?.groupValues + ?.getOrNull(1) + ?.toIntOrNull() + } + val season = it.parents()[1].attr("id").substringAfter("-").toIntOrNull() + Episode( + href, + name, + season, + episode, + ) } - val season = it.parents()[1].attr("id").substringAfter("-").toIntOrNull() - Episode( - href, - name, - season, - episode, - ) - } newTvSeriesLoadResponse(title, url, TvType.TvSeries, episodes) { this.posterUrl = poster this.year = year @@ -137,10 +143,10 @@ class Hdfilmcehennemi : MainAPI() { } } else { val trailer = - document.selectFirst("nav.nav.card-nav.nav-slider a[data-bs-toggle=\"modal\"]") - ?.attr("data-trailer")?.let { - "https://www.youtube.com/embed/$it" - } + document + .selectFirst("nav.nav.card-nav.nav-slider a[data-bs-toggle=\"modal\"]") + ?.attr("data-trailer") + ?.let { "https://www.youtube.com/embed/$it" } newMovieLoadResponse(title, url, TvType.Movie, url) { this.posterUrl = poster this.year = year @@ -160,11 +166,12 @@ class Hdfilmcehennemi : MainAPI() { subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit ) { - val script = app.get( - url, - referer = "${mainUrl}/" - ).document.select("script") - .find { it.data().contains("sources:") }?.data() ?: return + val script = + app.get(url, referer = "${mainUrl}/") + .document + .select("script") + .find { it.data().contains("sources:") } + ?.data() ?: return val videoData = getAndUnpack(script).substringAfter("file_link=\"").substringBefore("\";") val subData = script.substringAfter("tracks: [").substringBefore("]") @@ -180,12 +187,10 @@ class Hdfilmcehennemi : MainAPI() { ) tryParseJson>("[${subData}]") - ?.filter { it.kind == "captions" }?.map { + ?.filter { it.kind == "captions" } + ?.map { subtitleCallback.invoke( - SubtitleFile( - it.label.toString(), - fixUrl(it.file.toString()) - ) + SubtitleFile(it.label.toString(), fixUrl(it.file.toString())) ) } } @@ -196,12 +201,14 @@ class Hdfilmcehennemi : MainAPI() { subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit ): Boolean { - app.get(data).document.select("nav.nav.card-nav.nav-slider a.nav-link").map { - Pair(it.attr("href"), it.text()) - }.apmap { (url, source) -> - safeApiCall { - app.get(url).document.select("div.card-video > iframe").attr("data-src") - .let { url -> + app.get(data) + .document + .select("nav.nav.card-nav.nav-slider a.nav-link") + .map { Pair(it.attr("href"), it.text()) } + .apmap { (url, source) -> + safeApiCall { + app.get(url).document.select("div.card-video > iframe").attr("data-src").let { + url -> if (url.startsWith(mainUrl)) { invokeLocalSource(source, url, subtitleCallback, callback) } else { @@ -221,8 +228,8 @@ class Hdfilmcehennemi : MainAPI() { } } } + } } - } return true } @@ -242,4 +249,4 @@ class Hdfilmcehennemi : MainAPI() { @JsonProperty("slug") val slug: String? = null, @JsonProperty("slug_prefix") val slugPrefix: String? = null, ) -} \ No newline at end of file +} diff --git a/Hdfilmcehennemi/src/main/kotlin/com/hexated/HdfilmcehennemiPlugin.kt b/HDFilmCehennemi/src/main/kotlin/com/keyiflerolsun/HDFilmCehennemiPlugin.kt similarity index 54% rename from Hdfilmcehennemi/src/main/kotlin/com/hexated/HdfilmcehennemiPlugin.kt rename to HDFilmCehennemi/src/main/kotlin/com/keyiflerolsun/HDFilmCehennemiPlugin.kt index 257679b0..88bccafd 100644 --- a/Hdfilmcehennemi/src/main/kotlin/com/hexated/HdfilmcehennemiPlugin.kt +++ b/HDFilmCehennemi/src/main/kotlin/com/keyiflerolsun/HDFilmCehennemiPlugin.kt @@ -1,4 +1,3 @@ - package com.hexated import com.lagradost.cloudstream3.plugins.CloudstreamPlugin @@ -6,9 +5,8 @@ import com.lagradost.cloudstream3.plugins.Plugin import android.content.Context @CloudstreamPlugin -class HdfilmcehennemiPlugin: Plugin() { +class HDFilmCehennemiPlugin: Plugin() { override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(Hdfilmcehennemi()) + registerMainAPI(HDFilmCehennemi()) } } \ No newline at end of file diff --git a/Hdfilmcehennemi/build.gradle.kts b/Hdfilmcehennemi/build.gradle.kts deleted file mode 100644 index 3b75abc6..00000000 --- a/Hdfilmcehennemi/build.gradle.kts +++ /dev/null @@ -1,26 +0,0 @@ -// use an integer for version numbers -version = 11 - - -cloudstream { - language = "tr" - // All of these properties are optional, you can safely remove them - - // description = "Lorem Ipsum" - authors = listOf("Hexated") - - /** - * Status int as the following: - * 0: Down - * 1: Ok - * 2: Slow - * 3: Beta only - * */ - status = 1 // will be 3 if unspecified - tvTypes = listOf( - "TvSeries", - "Movie", - ) - - iconUrl = "https://www.google.com/s2/favicons?domain=hdfilmcehennemi.live&sz=%size%" -} \ No newline at end of file diff --git a/Turkish/build.gradle.kts b/Turkish/build.gradle.kts deleted file mode 100644 index bb0853d9..00000000 --- a/Turkish/build.gradle.kts +++ /dev/null @@ -1,26 +0,0 @@ -// use an integer for version numbers -version = 1 - - -cloudstream { - language = "tr" - // All of these properties are optional, you can safely remove them - - // description = "Lorem Ipsum" - authors = listOf("Hexated") - - /** - * Status int as the following: - * 0: Down - * 1: Ok - * 2: Slow - * 3: Beta only - * */ - status = 1 // will be 3 if unspecified - tvTypes = listOf( - "TvSeries", - "Movie", - ) - - iconUrl = "https://www.google.com/s2/favicons?domain=turkish123.com&sz=%size%" -} \ No newline at end of file diff --git a/Turkish/src/main/kotlin/com/hexated/Lajkema.kt b/Turkish/src/main/kotlin/com/hexated/Lajkema.kt deleted file mode 100644 index b2b6f570..00000000 --- a/Turkish/src/main/kotlin/com/hexated/Lajkema.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.hexated - -import com.lagradost.cloudstream3.extractors.XStreamCdn - -class Lajkema : XStreamCdn() { - override val name: String = "Lajkema" - override val mainUrl: String = "https://lajkema.com" -} \ No newline at end of file diff --git a/Turkish123/build.gradle.kts b/Turkish123/build.gradle.kts new file mode 100644 index 00000000..ae9e274e --- /dev/null +++ b/Turkish123/build.gradle.kts @@ -0,0 +1,18 @@ +version = 1 + +cloudstream { + authors = listOf("Hexated") + language = "tr" + description = "Watch Turkish Series with English Subtitles" + + /** + * Status int as the following: + * 0: Down + * 1: Ok + * 2: Slow + * 3: Beta only + **/ + status = 1 // will be 3 if unspecified + tvTypes = listOf("TvSeries") + iconUrl = "https://www.google.com/s2/favicons?domain=turkish123.com&sz=%size%" +} \ No newline at end of file diff --git a/Turkish/src/main/AndroidManifest.xml b/Turkish123/src/main/AndroidManifest.xml similarity index 100% rename from Turkish/src/main/AndroidManifest.xml rename to Turkish123/src/main/AndroidManifest.xml diff --git a/Turkish/src/main/kotlin/com/hexated/Turkish.kt b/Turkish123/src/main/kotlin/com/keyiflerolsun/Turkish123.kt similarity index 67% rename from Turkish/src/main/kotlin/com/hexated/Turkish.kt rename to Turkish123/src/main/kotlin/com/keyiflerolsun/Turkish123.kt index 7bb68e2f..a5be02a7 100644 --- a/Turkish/src/main/kotlin/com/hexated/Turkish.kt +++ b/Turkish123/src/main/kotlin/com/keyiflerolsun/Turkish123.kt @@ -7,28 +7,27 @@ import com.lagradost.cloudstream3.utils.M3u8Helper import com.lagradost.cloudstream3.utils.loadExtractor import org.jsoup.nodes.Element -class Turkish : MainAPI() { - override var mainUrl = "https://turkish123.com" - override var name = "Turkish123" - override val hasMainPage = true - override var lang = "tr" +class Turkish123 : MainAPI() { + override var mainUrl = "https://turkish123.com" + override var name = "Turkish123" + override val hasMainPage = true + override var lang = "tr" override val hasDownloadSupport = true - override val supportedTypes = setOf(TvType.TvSeries, TvType.Movie) + override val supportedTypes = setOf(TvType.TvSeries) companion object { private const val mainServer = "https://tukipasti.com" } - override val mainPage = mainPageOf( - "$mainUrl/series-list/page/" to "Series List", - "$mainUrl/episodes-list/page/" to "Episodes List", - ) + override val mainPage = + mainPageOf( + "$mainUrl/series-list/page/" to "Diziler", + "$mainUrl/episodes-list/page/" to "Bölümler", + ) override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse { val document = app.get(request.data + page).document - val home = document.select("div.movies-list div.ml-item").mapNotNull { - it.toSearchResult() - } + val home = document.select("div.movies-list div.ml-item").mapNotNull { it.toSearchResult() } return newHomePageResponse(request.name, home) } @@ -55,9 +54,7 @@ class Turkish : MainAPI() { override suspend fun search(query: String): List { val document = app.get("$mainUrl/?s=$query").document - return document.select("div.movies-list div.ml-item").mapNotNull { - it.toSearchResult() - } + return document.select("div.movies-list div.ml-item").mapNotNull { it.toSearchResult() } } override suspend fun load(url: String): LoadResponse? { @@ -67,24 +64,32 @@ class Turkish : MainAPI() { val poster = fixUrlNull(document.selectFirst("div.thumb.mvic-thumb img")?.attr("src")) val tags = document.select("div.mvici-left p:contains(Genre:) a").map { it.text() } - val year = document.selectFirst("div.mvici-right p:contains(Year:) a")?.text()?.trim() - ?.toIntOrNull() + val year = + document + .selectFirst("div.mvici-right p:contains(Year:) a") + ?.text() + ?.trim() + ?.toIntOrNull() val description = document.select("p.f-desc").text().trim() - val duration = document.selectFirst("div.mvici-right span[itemprop=duration]")?.text() - ?.filter { it.isDigit() }?.toIntOrNull() + val duration = + document + .selectFirst("div.mvici-right span[itemprop=duration]") + ?.text() + ?.filter { it.isDigit() } + ?.toIntOrNull() val rating = document.select("span.imdb-r").text().trim().toRatingInt() val actors = document.select("div.mvici-left p:contains(Actors:) a").map { it.text() } - val recommendations = document.select("div.movies-list div.ml-item").mapNotNull { - it.toSearchResult() - } + val recommendations = + document.select("div.movies-list div.ml-item").mapNotNull { it.toSearchResult() } - val episodes = document.select("div.les-content a").map { - Episode( - it.attr("href"), - it.text(), - ) - } + val episodes = + document.select("div.les-content a").map { + Episode( + it.attr("href"), + it.text(), + ) + } return newTvSeriesLoadResponse(title, url, TvType.TvSeries, episodes) { this.posterUrl = poster this.year = year @@ -97,21 +102,13 @@ class Turkish : MainAPI() { } } - private suspend fun invokeLocalSource( - url: String, - callback: (ExtractorLink) -> Unit - ) { + private suspend fun invokeLocalSource(url: String, callback: (ExtractorLink) -> Unit) { val document = app.get(url, referer = "$mainUrl/").text - Regex("var\\surlPlay\\s=\\s[\"|'](\\S+)[\"|'];").find(document)?.groupValues?.get(1) - ?.let { link -> - M3u8Helper.generateM3u8( - this.name, - link, - referer = "$mainServer/" - ).forEach(callback) - } - + Regex("var\\surlPlay\\s=\\s[\"|'](\\S+)[\"|'];").find(document)?.groupValues?.get(1)?.let { + link -> + M3u8Helper.generateM3u8(this.name, link, referer = "$mainServer/").forEach(callback) + } } override suspend fun loadLinks( @@ -123,8 +120,11 @@ class Turkish : MainAPI() { val document = app.get(data).text - Regex(" + Regex(" if (link.startsWith(mainServer)) { invokeLocalSource(link, callback) } else { @@ -133,7 +133,5 @@ class Turkish : MainAPI() { } return true - } - -} \ No newline at end of file +} diff --git a/Turkish/src/main/kotlin/com/hexated/TurkishPlugin.kt b/Turkish123/src/main/kotlin/com/keyiflerolsun/Turkish123Plugin.kt similarity index 51% rename from Turkish/src/main/kotlin/com/hexated/TurkishPlugin.kt rename to Turkish123/src/main/kotlin/com/keyiflerolsun/Turkish123Plugin.kt index 10f6b0bb..5773e37b 100644 --- a/Turkish/src/main/kotlin/com/hexated/TurkishPlugin.kt +++ b/Turkish123/src/main/kotlin/com/keyiflerolsun/Turkish123Plugin.kt @@ -1,4 +1,3 @@ - package com.hexated import com.lagradost.cloudstream3.plugins.CloudstreamPlugin @@ -6,10 +5,8 @@ import com.lagradost.cloudstream3.plugins.Plugin import android.content.Context @CloudstreamPlugin -class TurkishPlugin: Plugin() { +class Turkish123Plugin: Plugin() { override fun load(context: Context) { - // All providers should be added in this manner. Please don't edit the providers list directly. - registerMainAPI(Turkish()) - registerExtractorAPI(Lajkema()) + registerMainAPI(Turkish123()) } } \ No newline at end of file