Merge branch 'master' into adityajd-patch-1

This commit is contained in:
adityajd 2024-02-26 21:55:14 +07:00 committed by GitHub
commit 67d8853f1e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
110 changed files with 4572 additions and 2162 deletions

View File

@ -19,12 +19,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@master
uses: actions/checkout@v4
with:
path: "src"
- name: Checkout builds
uses: actions/checkout@master
uses: actions/checkout@v4
with:
ref: "builds"
path: "builds"
@ -33,8 +33,9 @@ jobs:
run: rm $GITHUB_WORKSPACE/builds/*.cs3 || true
- name: Setup JDK 11
uses: actions/setup-java@v1
uses: actions/setup-java@v4
with:
distribution: "adopt"
java-version: 11
- name: Setup Android SDK
@ -54,7 +55,11 @@ jobs:
ZSHOW_API: ${{ secrets.ZSHOW_API }}
SFMOVIES_API: ${{ secrets.SFMOVIES_API }}
CINEMATV_API: ${{ secrets.CINEMATV_API }}
OMOVIES_API: ${{ secrets.OMOVIES_API }}
GHOSTX_API: ${{ secrets.GHOSTX_API }}
SUPERSTREAM_FIRST_API: ${{ secrets.SUPERSTREAM_FIRST_API }}
SUPERSTREAM_SECOND_API: ${{ secrets.SUPERSTREAM_SECOND_API }}
SUPERSTREAM_THIRD_API: ${{ secrets.SUPERSTREAM_THIRD_API }}
SUPERSTREAM_FOURTH_API: ${{ secrets.SUPERSTREAM_FOURTH_API }}
run: |
cd $GITHUB_WORKSPACE/src
echo TMDB_API=$TMDB_API >> local.properties
@ -69,7 +74,11 @@ jobs:
echo ZSHOW_API=$ZSHOW_API >> local.properties
echo SFMOVIES_API=$SFMOVIES_API >> local.properties
echo CINEMATV_API=$CINEMATV_API >> local.properties
echo OMOVIES_API=$OMOVIES_API >> local.properties
echo GHOSTX_API=$GHOSTX_API >> local.properties
echo SUPERSTREAM_FIRST_API=$SUPERSTREAM_FIRST_API >> local.properties
echo SUPERSTREAM_SECOND_API=$SUPERSTREAM_SECOND_API >> local.properties
echo SUPERSTREAM_THIRD_API=$SUPERSTREAM_THIRD_API >> local.properties
echo SUPERSTREAM_FOURTH_API=$SUPERSTREAM_FOURTH_API >> local.properties
- name: Build Plugins
run: |

2
.gitignore vendored
View File

@ -10,4 +10,4 @@
.cxx
local.properties
.vscode
/ExampleProvider/
/ExampleProvider/

View File

@ -1,7 +1,7 @@
import org.jetbrains.kotlin.konan.properties.Properties
// use an integer for version numbers
version = 9
version = 10
android {
defaultConfig {
@ -38,5 +38,5 @@ cloudstream {
"OVA",
)
iconUrl = "https://media.discordapp.net/attachments/1059306855865782282/1123970193274712096/Anichi.png"
iconUrl = "https://cdn.discordapp.com/attachments/1109266606292488297/1200425504432472176/Anichi.png"
}

View File

@ -1,6 +1,5 @@
package com.hexated
import com.hexated.AnichiExtractors.invokeExternalSources
import com.hexated.AnichiExtractors.invokeInternalSources
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.LoadResponse.Companion.addActors
@ -214,25 +213,12 @@ open class Anichi : MainAPI() {
val loadData = parseJson<AnichiLoadData>(data)
argamap(
{
invokeInternalSources(
loadData.hash,
loadData.dubStatus,
loadData.episode,
subtitleCallback,
callback
)
},
{
invokeExternalSources(
loadData.idMal,
loadData.dubStatus,
loadData.episode,
subtitleCallback,
callback
)
}
invokeInternalSources(
loadData.hash,
loadData.dubStatus,
loadData.episode,
subtitleCallback,
callback
)
return true
@ -245,7 +231,6 @@ open class Anichi : MainAPI() {
const val anilistApi = "https://graphql.anilist.co"
const val jikanApi = "https://api.jikan.moe/v4"
const val marinHost = "https://marin.moe"
private const val mainHash = "e42a4466d984b2c0a2cecae5dd13aa68867f634b16ee0f17b380047d14482406"
private const val popularHash = "31a117653812a2547fd981632e8c99fa8bf8a75c4ef1a77a1567ef1741a7ab9c"

View File

@ -121,67 +121,6 @@ object AnichiExtractors : Anichi() {
}
}
suspend fun invokeExternalSources(
idMal: Int? = null,
dubStatus: String,
episode: String,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit,
) {
val ids = app.get("https://api.malsync.moe/mal/anime/${idMal ?: return}")
.parsedSafe<MALSyncResponses>()?.sites
if (dubStatus == "sub") invokeMarin(ids?.marin?.keys?.firstOrNull(), episode, callback)
}
private suspend fun invokeMarin(
id: String? = null,
episode: String,
callback: (ExtractorLink) -> Unit
) {
val url = "$marinHost/anime/${id ?: return}/$episode"
val cookies = app.get(
"$marinHost/anime",
headers = mapOf(
"Cookie" to "__ddg1_=;__ddg2_=;"
),
referer = "$marinHost/anime",
).cookies.let {
decode(it["XSRF-TOKEN"].toString()) to decode(it["marin_session"].toString())
}
val json = app.get(
url,
headers = mapOf(
"Accept" to "text/html, application/xhtml+xml",
"Cookie" to "__ddg1=;__ddg2_=;XSRF-TOKEN=${cookies.first};marin_session=${cookies.second};",
"X-XSRF-TOKEN" to cookies.first
),
referer = "$marinHost/anime/$id"
).document.selectFirst("div#app")?.attr("data-page")
tryParseJson<MarinResponses>(json)?.props?.video?.data?.mirror?.map { video ->
callback.invoke(
ExtractorLink(
"Marin",
"Marin",
video.code?.file ?: return@map,
url,
video.code.height ?: Qualities.Unknown.value,
headers = mapOf(
"Accept" to "video/webm,video/ogg,video/*;q=0.9,application/ogg;q=0.7,audio/*;q=0.6,*/*;q=0.5",
"Accept-Language" to "en-US,en;q=0.5",
"Cookie" to "__ddg1=;__ddg2_=; XSRF-TOKEN=${cookies.first}; marin_session=${cookies.second};",
"Connection" to "keep-alive",
"Sec-Fetch-Dest" to "video",
"Sec-Fetch-Mode" to "cors",
"Sec-Fetch-Site" to "cross-site",
)
)
)
}
}
private suspend fun invokeGogo(
link: String,
subtitleCallback: (SubtitleFile) -> Unit,

View File

@ -240,7 +240,6 @@ data class PageStatus(
@JsonProperty("__typename") val _typename: String? = null
)
data class Recommendations(
@JsonProperty("anyCard") val anyCard: AnyCard? = null,
@JsonProperty("pageStatus") val pageStatus: PageStatus? = PageStatus(),
@ -255,38 +254,4 @@ data class QueryPopular(
data class DataPopular(
@JsonProperty("queryPopular") val queryPopular: QueryPopular? = QueryPopular()
)
data class MALSyncSites(
@JsonProperty("Zoro") val zoro: HashMap<String?, HashMap<String, String?>>? = hashMapOf(),
@JsonProperty("Marin") val marin: HashMap<String?, HashMap<String, String?>>? = hashMapOf(),
)
data class MALSyncResponses(
@JsonProperty("Sites") val sites: MALSyncSites? = null,
)
data class MarinCode(
@JsonProperty("file") val file: String? = null,
@JsonProperty("height") val height: Int? = null,
)
data class MarinMirror(
@JsonProperty("code") val code: MarinCode? = null,
)
data class MarinData(
@JsonProperty("mirror") val mirror: ArrayList<MarinMirror>? = arrayListOf(),
)
data class MarinVideos(
@JsonProperty("data") val data: MarinData? = null,
)
data class MarinProps(
@JsonProperty("video") val video: MarinVideos? = null,
)
data class MarinResponses(
@JsonProperty("props") val props: MarinProps? = null,
)

View File

@ -1,5 +1,5 @@
// use an integer for version numbers
version = 2
version = 3
cloudstream {

View File

@ -84,17 +84,13 @@ class Anilibria : MainAPI() {
val document = app.get(url).document
val title = document.selectFirst("h1.release-title")?.text() ?: return null
val poster = fixUrlNull(document.selectFirst("img#adminPoster")?.attr("src"))
val trackTitle = (document.selectFirst("h1.release-title br")?.nextSibling()
val enTitle = (document.selectFirst("h1.release-title br")?.nextSibling()
?: document.selectFirst("h1.release-title")?.text()?.substringAfter("/")?.trim()).toString()
val poster = fixUrlNull(document.selectFirst("img#adminPoster")?.attr("src"))
val type = document.selectFirst("div#xreleaseInfo b:contains(Тип:)")?.nextSibling()
.toString().substringBefore(",").trim()
val trackType = type.let {
if(it.contains("Фильм", true)) "movie" else "tv"
}
val year = document.selectFirst("div#xreleaseInfo b:contains(Сезон:)")?.nextElementSibling()
?.text()?.filter { it.isDigit() }?.toIntOrNull()
val (malId, anilistId, image, cover) = getTracker(trackTitle, trackType, year)
val episodes = document.select("script").find { it.data().contains("var player =") }?.data()
?.substringAfter("file:[")?.substringBefore("],")?.let { data ->
tryParseJson<List<Episodes>>("[$data]")?.mapNotNull { eps ->
@ -106,15 +102,14 @@ class Anilibria : MainAPI() {
}
}
return newAnimeLoadResponse(title, url, getType(type)) {
posterUrl = image ?: poster
backgroundPosterUrl = cover ?: image ?: poster
japName = enTitle
posterUrl = poster
backgroundPosterUrl = poster
this.year = year
addEpisodes(DubStatus.Subbed, episodes)
plot = document.select("p.detail-description").text().trim()
this.tags = document.selectFirst("div#xreleaseInfo b:contains(Жанры:)")?.nextSibling()
.toString().split(",").map { it.trim() }
addMalId(malId)
addAniListId(anilistId?.toIntOrNull())
}
}
@ -143,43 +138,6 @@ class Anilibria : MainAPI() {
return true
}
private suspend fun getTracker(title: String?, type: String?, year: Int?): Tracker {
val res = app.get("https://api.consumet.org/meta/anilist/$title")
.parsedSafe<AniSearch>()?.results?.find { media ->
(media.title?.english.equals(title, true) || media.title?.romaji.equals(
title,
true
)) || (media.type.equals(type, true) && media.releaseDate == year)
}
return Tracker(res?.malId, res?.aniId, res?.image, res?.cover)
}
data class Tracker(
val malId: Int? = null,
val aniId: String? = null,
val image: String? = null,
val cover: String? = null,
)
data class Title(
@JsonProperty("romaji") val romaji: String? = null,
@JsonProperty("english") val english: String? = null,
)
data class Results(
@JsonProperty("id") val aniId: String? = null,
@JsonProperty("malId") val malId: Int? = null,
@JsonProperty("title") val title: Title? = null,
@JsonProperty("releaseDate") val releaseDate: Int? = null,
@JsonProperty("type") val type: String? = null,
@JsonProperty("image") val image: String? = null,
@JsonProperty("cover") val cover: String? = null,
)
data class AniSearch(
@JsonProperty("results") val results: ArrayList<Results>? = arrayListOf(),
)
private data class Episodes(
@JsonProperty("file") val file: String? = null,
@JsonProperty("title") val title: String? = null,
@ -194,4 +152,4 @@ class Anilibria : MainAPI() {
@JsonProperty("mes") val mes: String? = null,
)
}
}

View File

@ -0,0 +1,23 @@
version = 4
cloudstream {
language = "hi"
authors = listOf("anon")
/**
* 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",
"Anime",
"Cartoon"
)
iconUrl = "https://animedekho.com/wp-content/uploads/2023/07/AnimeDekho-Logo-300x-1.pngg"
}

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.anon"/>

View File

@ -0,0 +1,13 @@
package com.anon
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
import com.lagradost.cloudstream3.plugins.Plugin
import android.content.Context
@CloudstreamPlugin
class AnimeDekhoPlugin: Plugin() {
override fun load(context: Context) {
registerMainAPI(AnimeDekhoProvider())
//addExtractor(MultiQualityXYZ())
}
}

View File

@ -0,0 +1,148 @@
package com.anon
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.extractors.helper.AesHelper.cryptoAESHandler
import com.lagradost.cloudstream3.utils.*
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
import com.lagradost.cloudstream3.utils.AppUtils.toJson
import org.jsoup.nodes.Element
class AnimeDekhoProvider : MainAPI() {
override var mainUrl = "https://animedekho.com"
override var name = "Anime Dekho"
override val hasMainPage = true
override var lang = "hi"
override val hasDownloadSupport = true
private val serverUrl = "https://vidxstream.xyz"
override val supportedTypes =
setOf(
TvType.Cartoon,
TvType.Anime,
TvType.AnimeMovie,
TvType.Movie,
)
override val mainPage =
mainPageOf(
"/series/" to "Series",
"/movie/" to "Movies",
"/category/anime/" to "Anime",
"/category/cartoon/" to "Cartoon",
)
override suspend fun getMainPage(
page: Int,
request: MainPageRequest,
): HomePageResponse {
val link = "$mainUrl${request.data}"
val document = app.get(link).document
val home =
document.select("article").mapNotNull {
it.toSearchResult()
}
return newHomePageResponse(request.name, home)
}
private fun Element.toSearchResult(): AnimeSearchResponse? {
val href = this.selectFirst("a.lnk-blk")?.attr("href") ?: return null
val title = this.selectFirst("header h2")?.text() ?: "null"
val posterUrl = this.selectFirst("div figure img")?.attr("src")
return newAnimeSearchResponse(title, Media(href, posterUrl).toJson(), TvType.Anime, false) {
this.posterUrl = posterUrl
addDubStatus(dubExist = true, subExist = true)
}
}
override suspend fun search(query: String): List<AnimeSearchResponse> {
val document = app.get("$mainUrl/?s=$query").document
return document.select("ul[data-results] li article").mapNotNull {
it.toSearchResult()
}
}
override suspend fun load(url: String): LoadResponse {
val media = parseJson<Media>(url)
val document = app.get(media.url).document
val title = document.selectFirst("h1.entry-title")?.text()?.trim()
?: document.selectFirst("meta[property=og:image:alt]")?.attr("content") ?: "No Title"
val poster = fixUrlNull(document.selectFirst("div.post-thumbnail figure img")?.attr("src") ?: media.poster)
val plot = document.selectFirst("div.entry-content p")?.text()?.trim()
?: document.selectFirst("meta[name=twitter:description]")?.attr("content")
val year = (document.selectFirst("span.year")?.text()?.trim()
?: document.selectFirst("meta[property=og:updated_time]")?.attr("content")
?.substringBefore("-"))?.toIntOrNull()
val lst = document.select("ul.seasons-lst li")
return if (lst.isEmpty()) {
newMovieLoadResponse(title, url, TvType.Movie, Media(media.url, mediaType = 1).toJson()) {
this.posterUrl = poster
this.plot = plot
this.year = year
}
} else {
val episodes = document.select("ul.seasons-lst li").mapNotNull {
val name = it.selectFirst("h3.title")?.text() ?: "null"
val href = it.selectFirst("a")?.attr("href") ?: return@mapNotNull null
Episode(Media(href, mediaType = 2).toJson(), name)
}
newTvSeriesLoadResponse(title, url, TvType.TvSeries, episodes) {
this.posterUrl = poster
this.plot = plot
this.year = year
}
}
}
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit,
): Boolean {
val media = parseJson<Media>(data)
val body = app.get(media.url).document.selectFirst("body")?.attr("class") ?: return false
val term = Regex("""(?:term|postid)-(\d+)""").find(body)?.groupValues?.get(1) ?: throw ErrorLoadingException("no id found")
val vidLink = app.get("$mainUrl/?trembed=0&trid=$term&trtype=${media.mediaType}")
.document.selectFirst("iframe")?.attr("src")
?: throw ErrorLoadingException("no iframe found")
val doc = app.get(vidLink).text
val master = Regex("""JScript[\w+]?\s*=\s*'([^']+)""").find(doc)!!.groupValues[1]
val decrypt = cryptoAESHandler(master, "a7igbpIApajDyNe".toByteArray(), false)?.replace("\\", "")
?: throw ErrorLoadingException("error decrypting")
val vidFinal = Regex("""file:\s*"(https:[^"]+)"""").find(decrypt)!!.groupValues[1]
val headers =
mapOf(
"accept" to "*/*",
"accept-language" to "en-US,en;q=0.5",
"Origin" to serverUrl,
"Accept-Encoding" to "gzip, deflate, br",
"Connection" to "keep-alive",
// "Referer" to "https://vidxstream.xyz/",
"Sec-Fetch-Dest" to "empty",
"Sec-Fetch-Mode" to "cors",
"Sec-Fetch-Site" to "cross-site",
"user-agent" to "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/116.0",
)
callback.invoke(
ExtractorLink(
source = "Toon",
name = "Toon",
url = vidFinal,
referer = "$serverUrl/",
quality = Qualities.Unknown.value,
isM3u8 = true,
headers = headers,
),
)
return true
}
data class Media(val url: String, val poster: String? = null, val mediaType: Int? = null)
}

View File

@ -23,5 +23,5 @@ cloudstream {
"Anime",
)
iconUrl = "https://www.google.com/s2/favicons?domain=animeindo.fun&sz=%size%"
iconUrl = "https://animeindo.quest/wp-content/uploads/2023/05/favicon-300x300.png"
}

View File

@ -0,0 +1,26 @@
// use an integer for version numbers
version = 2
cloudstream {
language = "de"
// 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=cinemathek.net&sz=%size%"
}

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.hexated"/>

View File

@ -0,0 +1,202 @@
package com.hexated
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.LoadResponse.Companion.addActors
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
import com.lagradost.cloudstream3.extractors.DoodLaExtractor
import com.lagradost.cloudstream3.extractors.Filesim
import com.lagradost.cloudstream3.utils.*
import org.jsoup.nodes.Element
class Cinemathek : MainAPI() {
override var mainUrl = "https://cinemathek.net"
override var name = "Cinemathek"
override val hasMainPage = true
override var lang = "de"
override val supportedTypes = setOf(
TvType.Movie,
TvType.TvSeries,
)
override val mainPage = mainPageOf(
"filme" to "Filme",
"serien" to "TV Shows",
"episoden" to "Episodes",
)
override suspend fun getMainPage(
page: Int,
request: MainPageRequest
): HomePageResponse {
val document = app.get("$mainUrl/${request.data}/page/$page/").document
val home =
document.select("div.items.full article, div#archive-content article").mapNotNull {
it.toSearchResult()
}
return newHomePageResponse(
list = HomePageList(
name = request.name,
list = home,
request.data == "episoden"
),
hasNext = true
)
}
private fun getProperLink(uri: String): String {
return when {
uri.contains("/episoden/") -> {
uri.replace(Regex("-\\d+x\\d+"), "").replace("/episoden/", "/serien/")
}
else -> {
uri
}
}
}
private fun Element.toSearchResult(): SearchResponse {
val title = this.selectFirst("h3 > a")!!.text()
val href = getProperLink(this.selectFirst("h3 > a")!!.attr("href"))
val posterUrl = this.select("div.poster > img").attr("src").toString()
val quality = getQualityFromString(this.select("span.quality").text())
return newMovieSearchResponse(title, href, TvType.Movie) {
this.posterUrl = posterUrl
this.quality = quality
}
}
override suspend fun search(query: String): List<SearchResponse> {
val document = app.get("$mainUrl/search/$query").document
return document.select("div.result-item").map {
val title = it.selectFirst("div.title > a")!!.text()
val href = getProperLink(it.selectFirst("div.title > a")!!.attr("href"))
val posterUrl = it.selectFirst("img")!!.attr("src").toString()
newMovieSearchResponse(title, href, TvType.TvSeries) {
this.posterUrl = posterUrl
}
}
}
override suspend fun load(url: String): LoadResponse {
val document = app.get(url).document
val title = document.selectFirst("div.data > h1")?.text() ?: ""
val poster = document.select("div.poster > img").attr("src").toString()
val tags = document.select("div.sgeneros > a").map { it.text() }
val year = Regex(",\\s?(\\d+)").find(
document.select("span.date").text().trim()
)?.groupValues?.get(1).toString().toIntOrNull()
val tvType = if (document.select("ul#section > li:nth-child(1)").text().contains("Episodes")
) TvType.TvSeries else TvType.Movie
val description = document.select("div.wp-content > p").text().trim()
val trailer = document.selectFirst("div.embed iframe")?.attr("src")
val rating =
document.selectFirst("span.dt_rating_vgs")?.text()?.toRatingInt()
val actors = document.select("div.persons > div[itemprop=actor]").map {
Actor(it.select("meta[itemprop=name]").attr("content"), it.select("img").attr("src"))
}
val recommendations = document.select("div.owl-item").map {
val recName =
it.selectFirst("a")!!.attr("href").toString().removeSuffix("/").split("/").last()
val recHref = it.selectFirst("a")!!.attr("href")
val recPosterUrl = it.selectFirst("img")?.attr("src").toString()
newTvSeriesSearchResponse(recName, recHref, TvType.TvSeries) {
this.posterUrl = recPosterUrl
}
}
return if (tvType == TvType.TvSeries) {
val episodes = document.select("ul.episodios > li").map {
val href = it.select("a").attr("href")
val name = fixTitle(it.select("div.episodiotitle > a").text().trim())
val image = it.select("div.imagen > img").attr("src")
val episode = it.select("div.numerando").text().replace(" ", "").split("-").last()
.toIntOrNull()
val season = it.select("div.numerando").text().replace(" ", "").split("-").first()
.toIntOrNull()
Episode(
href,
name,
season,
episode,
image
)
}
newTvSeriesLoadResponse(title, url, TvType.TvSeries, episodes) {
this.posterUrl = poster
this.year = year
this.plot = description
this.tags = tags
this.rating = rating
addActors(actors)
this.recommendations = recommendations
addTrailer(trailer)
}
} else {
newMovieLoadResponse(title, url, TvType.Movie, url) {
this.posterUrl = poster
this.year = year
this.plot = description
this.tags = tags
this.rating = rating
addActors(actors)
this.recommendations = recommendations
addTrailer(trailer)
}
}
}
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
val document = app.get(data).document
document.select("ul#playeroptionsul > li").map {
Triple(
it.attr("data-post"),
it.attr("data-nume"),
it.attr("data-type")
)
}.apmap { (id, nume, type) ->
val iframe = app.get(
url = "$mainUrl/wp-json/dooplayer/v2/$id/$type/$nume",
referer = data,
headers = mapOf("Accept" to "*/*", "X-Requested-With" to "XMLHttpRequest")
).parsedSafe<ResponseHash>()?.embedUrl ?: return@apmap
if (!iframe.contains("youtube")) loadExtractor(iframe, "$mainUrl/", subtitleCallback, callback)
}
return true
}
data class ResponseHash(
@JsonProperty("embed_url") val embedUrl: String,
)
}
class StreamwishCom : Filesim() {
override val name = "Streamwish"
override var mainUrl = "https://streamwish.com"
}
class Ds2play : DoodLaExtractor() {
override var name = "Ds2play"
override var mainUrl = "https://ds2play.com"
}
class Do0od : DoodLaExtractor() {
override var name = "Do0od"
override var mainUrl = "https://do0od.com"
}
class Filelions : Filesim() {
override val name = "Filelions"
override var mainUrl = "https://filelions.live"
}

View File

@ -0,0 +1,18 @@
package com.hexated
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
import com.lagradost.cloudstream3.plugins.Plugin
import android.content.Context
@CloudstreamPlugin
class CinemathekPlugin: Plugin() {
override fun load(context: Context) {
// All providers should be added in this manner. Please don't edit the providers list directly.
registerMainAPI(Cinemathek())
registerExtractorAPI(StreamwishCom())
registerExtractorAPI(Ds2play())
registerExtractorAPI(Do0od())
registerExtractorAPI(Filelions())
}
}

View File

@ -1,5 +1,5 @@
// use an integer for version numbers
version = 5
version = 6
cloudstream {

View File

@ -6,13 +6,14 @@ import com.lagradost.cloudstream3.extractors.Filesim
import com.lagradost.cloudstream3.utils.AppUtils
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.INFER_TYPE
import com.lagradost.cloudstream3.utils.Qualities
import com.lagradost.cloudstream3.utils.getQualityFromName
import com.lagradost.cloudstream3.utils.loadExtractor
import org.jsoup.nodes.Element
import java.net.URI
class DramaSerial : MainAPI() {
override var mainUrl = "https://tv3.dramaserial.id"
private var serverUrl = "http://31.220.73.179/"
private var serverUrl = "https://juraganfilm.info"
override var name = "DramaSerial"
override val hasMainPage = true
override var lang = "id"
@ -72,8 +73,12 @@ class DramaSerial : MainAPI() {
val duration =
document.selectFirst("div.gmr-movie-innermeta span:contains(Duration:)")?.text()
?.filter { it.isDigit() }?.toIntOrNull()
val description = document.select("div.entry-content.entry-content-single div.entry-content.entry-content-single").text().trim()
val type = if(document.select("div.page-links").isNullOrEmpty()) TvType.Movie else TvType.AsianDrama
val description =
document.select("div.entry-content.entry-content-single div.entry-content.entry-content-single")
.text().trim()
val type = if (document.select("div.page-links")
.isNullOrEmpty()
) TvType.Movie else TvType.AsianDrama
if (type == TvType.Movie) {
return newMovieLoadResponse(title, url, TvType.Movie, url) {
@ -84,18 +89,19 @@ class DramaSerial : MainAPI() {
this.duration = duration
}
} else {
val episodes = document.select("div.page-links span.page-link-number").mapNotNull { eps ->
val episode = eps.text().filter { it.isDigit() }.toIntOrNull()
val link = if(episode == 1) {
url
} else {
eps.parent()?.attr("href")
val episodes =
document.select("div.page-links span.page-link-number").mapNotNull { eps ->
val episode = eps.text().filter { it.isDigit() }.toIntOrNull()
val link = if (episode == 1) {
url
} else {
eps.parent()?.attr("href")
}
Episode(
link ?: return@mapNotNull null,
episode = episode,
)
}
Episode(
link ?: return@mapNotNull null,
episode = episode,
)
}
return newTvSeriesLoadResponse(title, url, TvType.AsianDrama, episodes = episodes) {
posterUrl = poster
this.year = year
@ -107,6 +113,7 @@ class DramaSerial : MainAPI() {
}
private suspend fun invokeGetbk(
name: String,
url: String,
callback: (ExtractorLink) -> Unit
) {
@ -115,12 +122,12 @@ class DramaSerial : MainAPI() {
referer = "$serverUrl/"
).document.selectFirst("script:containsData(sources)")?.data() ?: return
val json = "\"sources\":\\s*\\[(.*)]".toRegex().find(script)?.groupValues?.get(1)
val json = "sources:\\s*\\[(.*)]".toRegex().find(script)?.groupValues?.get(1)
AppUtils.tryParseJson<ArrayList<Sources>>("[$json]")?.map {
callback.invoke(
ExtractorLink(
"Getbk",
"Getbk",
name,
name,
it.file ?: return@map,
"$serverUrl/",
getQualityFromName(it.label),
@ -131,6 +138,34 @@ class DramaSerial : MainAPI() {
}
private suspend fun invokeGdrive(
name: String,
url: String,
callback: (ExtractorLink) -> Unit
) {
val embedUrl = app.get(
url,
referer = "$serverUrl/"
).document.selectFirst("iframe")?.attr("src")?.let { fixUrl(it) } ?: return
val req = app.get(embedUrl)
val host = getBaseUrl(embedUrl)
val token = req.document.selectFirst("div#token")?.text() ?: return
callback.invoke(
ExtractorLink(
name,
name,
"$host/hlsplaylist.php?idhls=${token.trim()}.m3u8",
"$host/",
Qualities.Unknown.value,
true
)
)
}
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
@ -142,19 +177,23 @@ class DramaSerial : MainAPI() {
val iframe = document.select("iframe[name=juraganfilm]").attr("src")
app.get(iframe, referer = "$mainUrl/").document.select("div#header-slider ul li")
.apmap { mLink ->
mLink.attr("onclick").substringAfter("frame('").substringBefore("')").let { iLink ->
val iMovie = iLink.substringAfter("movie=").substringBefore("&")
val mIframe = iLink.substringAfter("iframe=")
val iUrl = "$serverUrl/stream/$mIframe.php?movie=$iMovie"
if(mIframe == "getbk") {
invokeGetbk(iUrl, callback)
} else {
val link = app.get(
iUrl,
referer = "$serverUrl/"
).document.selectFirst("iframe")?.attr("src") ?: return@apmap null
loadExtractor(fixUrl(link), "$serverUrl/", subtitleCallback, callback)
val iLink = mLink.attr("onclick").substringAfter("frame('").substringBefore("')")
serverUrl = getBaseUrl(iLink)
val iMovie = iLink.substringAfter("movie=").substringBefore("&")
val mIframe = iLink.substringAfter("iframe=")
val serverName = fixTitle(mIframe)
when (mIframe) {
"getbk" -> {
invokeGetbk(
serverName,
"$serverUrl/stream/$mIframe.php?movie=$iMovie",
callback
)
}
"gdrivehls", "gdriveplayer" -> {
invokeGdrive(serverName, iLink, callback)
}
else -> {}
}
}
@ -162,6 +201,12 @@ class DramaSerial : MainAPI() {
}
private fun getBaseUrl(url: String): String {
return URI(url).let {
"${it.scheme}://${it.host}"
}
}
private data class Sources(
@JsonProperty("file") val file: String? = null,
@JsonProperty("label") val label: String? = null,

View File

@ -1,5 +1,5 @@
// use an integer for version numbers
version = 1
version = 3
cloudstream {

View File

@ -3,13 +3,14 @@ package com.hexated
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.LoadResponse.Companion.addActors
import com.lagradost.cloudstream3.utils.*
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.M3u8Helper
import org.jsoup.nodes.Element
import java.net.URLDecoder
class DubokuProvider : MainAPI() {
override var mainUrl = "https://www.duboku.tv"
private var serverUrl = "https://w.duboku.io"
override var name = "Duboku"
override val hasMainPage = true
override var lang = "zh"
@ -106,27 +107,41 @@ class DubokuProvider : MainAPI() {
callback: (ExtractorLink) -> Unit
): Boolean {
app.get(data).document.select("script").map { script ->
if (script.data().contains("var player_data={")) {
val dataJson =
script.data().substringAfter("var player_data={").substringBefore("}")
tryParseJson<Sources>("{$dataJson}")?.let { source ->
M3u8Helper.generateM3u8(
this.name,
source.url ?: return@map,
referer = "https://w.duboku.io/",
headers = mapOf("Origin" to "https://w.duboku.io")
).forEach(callback)
}
}
}
val dataJson =
app.get(data).document.selectFirst("script:containsData(var player_data={)")?.data()
?.substringAfter("var player_data={")?.substringBefore("}")
?: throw IllegalArgumentException()
val source = tryParseJson<Sources>("{$dataJson}")
callback.invoke(
ExtractorLink(
this.name,
this.name,
"${decode(base64Decode(source?.url ?: return false))}${getSign(source.from, data)}",
"$serverUrl/",
Qualities.Unknown.value,
INFER_TYPE,
headers = mapOf(
"Accept-Language" to "en-US,en;q=0.5",
"Origin" to serverUrl
),
)
)
return true
}
private suspend fun getSign(server: String? = "vidjs24", ref: String): String {
return app.get(
"$serverUrl/static/player/$server.php",
referer = ref
).text.substringAfter("PlayUrl+'").substringBefore("'")
}
private fun decode(input: String): String = URLDecoder.decode(input, "utf-8")
data class Sources(
@JsonProperty("url") val url: String?,
@JsonProperty("from") val from: String?,
)

View File

@ -21,4 +21,6 @@ cloudstream {
// 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("TvSeries", "Movie")
iconUrl = "https://www.google.com/s2/favicons?domain=filmpalast.to&sz=%size%"
}

View File

@ -1,7 +1,7 @@
import org.jetbrains.kotlin.konan.properties.Properties
// use an integer for version numbers
version = 29
version = 31
android {
defaultConfig {

View File

@ -6,8 +6,9 @@ import com.lagradost.cloudstream3.utils.httpsify
import com.lagradost.cloudstream3.utils.loadExtractor
class DutaMovie : Gomov() {
override var mainUrl = "https://viral.dutamovie21.tech"
override var name = "DutaMovie"
override var name = "DutaMovie"
override val mainPage = mainPageOf(
"category/box-office/page/%d/" to "Box Office",
"category/serial-tv/page/%d/" to "Serial TV",

View File

@ -10,7 +10,9 @@ import org.jsoup.nodes.Element
import java.net.URI
open class Gomov : MainAPI() {
override var mainUrl = "https://gomov.info"
private var directUrl: String? = null
override var name = "Gomov"
override val hasMainPage = true

View File

@ -3,7 +3,9 @@ package com.hexated
import com.lagradost.cloudstream3.mainPageOf
class Multiplex : Gomov() {
override var mainUrl = "http://95.111.236.109"
override var name = "Multiplex"
override val mainPage = mainPageOf(
"country/usa/page/%d/" to "Movie",

View File

@ -3,7 +3,9 @@ package com.hexated
import com.lagradost.cloudstream3.mainPageOf
class Ngefilm : Gomov() {
override var mainUrl = "https://ngefilm21.pics"
override var name = "Ngefilm"
override val mainPage = mainPageOf(
"/page/%d/?s&search=advanced&post_type=movie&index&orderby&genre&movieyear&country&quality=" to "Movies Terbaru",

View File

@ -9,7 +9,7 @@ import org.jsoup.nodes.Element
import java.net.URI
class Nodrakorid : Gomov() {
override var mainUrl = "https://no-dra-kor-id.shop"
override var mainUrl = "https://no1.nodrakor.store"
override var name = "Nodrakorid"
override val mainPage = mainPageOf(

View File

@ -6,7 +6,9 @@ import com.lagradost.cloudstream3.TvSeriesLoadResponse
import com.lagradost.cloudstream3.*
class Pusatfilm : Gomov() {
override var mainUrl = "http://37.60.238.37/Genre/pusatfilm21"
override var name = "Pusatfilm"
override val mainPage = mainPageOf(
"film-terbaru/page/%d/" to "Film Terbaru",

View File

@ -24,5 +24,5 @@ cloudstream {
"Movie",
)
iconUrl = "https://www.google.com/s2/favicons?domain=hdrezka19139.org&sz=%size%"
iconUrl = "https://www.google.com/s2/favicons?domain=rezka.ag&sz=%size%"
}

View File

@ -1,5 +1,5 @@
// use an integer for version numbers
version = 3
version = 4
cloudstream {

View File

@ -55,7 +55,7 @@ class Hentaiheaven : MainAPI() {
val link = "$mainUrl/?s=$query&post_type=wp-manga"
val document = app.get(link).document
return document.select("div.c-tabs-item div.row.c-tabs-item__content").mapNotNull {
return document.select("div.c-tabs-item > div.c-tabs-item__content").mapNotNull {
it.toSearchResult()
}
}

View File

@ -35,8 +35,8 @@ open class Kickassanime : MainAPI() {
companion object {
const val kaast = "https://kaast1.com"
private const val consumetAnilist = "https://api.consumet.org/meta/anilist"
private const val consumetMal = "https://api.consumet.org/meta/mal"
private const val consumetAnilist = "https://consumet-instance.vercel.app/meta/anilist"
private const val consumetMal = "https://consumet-instance.vercel.app/meta/mal"
fun getType(t: String): TvType {
return when {
t.contains("Ova", true) -> TvType.OVA
@ -379,4 +379,4 @@ open class Kickassanime : MainAPI() {
@JsonProperty("title") val title: SyncTitle? = null,
)
}
}

26
Kinoger/build.gradle.kts Normal file
View File

@ -0,0 +1,26 @@
// use an integer for version numbers
version = 3
cloudstream {
language = "de"
// 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=kinoger.com&sz=%size%"
}

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.hexated"/>

View File

@ -0,0 +1,157 @@
package com.hexated
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.extractors.Chillx
import com.lagradost.cloudstream3.network.CloudflareKiller
import com.lagradost.cloudstream3.utils.AppUtils
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.ExtractorLinkType
import com.lagradost.cloudstream3.utils.Qualities
import com.lagradost.cloudstream3.utils.loadExtractor
import org.jsoup.nodes.Element
class Kinoger : MainAPI() {
override var name = "Kinoger"
override var mainUrl = "https://kinoger.to"
override var lang = "de"
override val hasMainPage = true
override val supportedTypes = setOf(TvType.TvSeries, TvType.Movie)
override val mainPage = mainPageOf(
"" to "Alle Filme",
"stream/action" to "Action",
"stream/fantasy" to "Fantasy",
"stream/drama" to "Drama",
"stream/mystery" to "Mystery",
"stream/romance" to "Romance",
"stream/animation" to "Animation",
"stream/horror" to "Horror",
"stream/familie" to "Familie",
"stream/komdie" to "Komdie",
)
override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse {
val document = app.get("$mainUrl/${request.data}/page/$page").document
val home = document.select("div#dle-content div.short").mapNotNull {
it.toSearchResult()
}
return newHomePageResponse(request.name, home)
}
private fun getProperLink(uri: String): String {
return if (uri.contains("-episode-")) {
"$mainUrl/series/" + Regex("$mainUrl/(.+)-ep.+").find(uri)?.groupValues?.get(1)
} else {
uri
}
}
private fun Element.toSearchResult(): SearchResponse? {
val href = getProperLink(this.selectFirst("a")?.attr("href") ?: return null)
val title = this.selectFirst("a")?.text() ?: this.selectFirst("img")?.attr("alt")
?: this.selectFirst("a")?.attr("title") ?: return null
val posterUrl = fixUrlNull(
(this.selectFirst("div.content_text img") ?: this.nextElementSibling()?.selectFirst("div.content_text img") ?: this.selectFirst("img"))?.getImageAttr()
)
return newTvSeriesSearchResponse(title, href, TvType.AsianDrama) {
this.posterUrl = posterUrl
}
}
override suspend fun search(query: String): List<SearchResponse> {
return app.get("$mainUrl/?do=search&subaction=search&titleonly=3&story=$query&x=0&y=0&submit=submit").document.select(
"div#dle-content div.titlecontrol"
).mapNotNull { it.toSearchResult() }
}
override suspend fun load(url: String): LoadResponse {
val document = app.get(url).document
val title = document.selectFirst("h1#news-title")?.text() ?: ""
val poster = fixUrlNull(document.selectFirst("div.images-border img")?.getImageAttr())
val description = document.select("div.images-border").text()
val year = """\((\d{4})\)""".toRegex().find(title)?.groupValues?.get(1)?.toIntOrNull()
val tags = document.select("li.category a").map { it.text() }
val recommendations = document.select("ul.ul_related li").mapNotNull {
it.toSearchResult()
}
val script = document.selectFirst("script:containsData(pw.show)")?.data()
val data = script?.substringAfter("[")?.substringBeforeLast("]")?.replace("\'", "\"")
val json = AppUtils.tryParseJson<List<List<String>>>("[$data]")
val type = if(script?.substringBeforeLast(")")?.substringAfterLast(",") == "0.2") TvType.Movie else TvType.TvSeries
val episodes = json?.flatMapIndexed { season: Int, iframes: List<String> ->
iframes.mapIndexed { episode, iframe ->
Episode(
iframe.trim(),
season = season + 1,
episode = episode + 1
)
}
} ?: emptyList()
return newTvSeriesLoadResponse(title, url, type, episodes) {
this.posterUrl = poster
this.year = year
this.plot = description
this.tags = tags
this.recommendations = recommendations
}
}
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
loadCustomExtractor(data, "$mainUrl/", subtitleCallback, callback)
return true
}
private suspend fun loadCustomExtractor(
url: String,
referer: String? = null,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit,
quality: Int? = null,
) {
loadExtractor(url, referer, subtitleCallback) { link ->
if(link.quality == Qualities.Unknown.value) {
callback.invoke(
ExtractorLink(
link.source,
link.name,
link.url,
link.referer,
when (link.type) {
ExtractorLinkType.M3U8 -> link.quality
else -> quality ?: link.quality
},
link.type,
link.headers,
link.extractorData
)
)
}
}
}
private fun Element.getImageAttr(): String? {
return when {
this.hasAttr("data-src") -> this.attr("data-src")
this.hasAttr("data-lazy-src") -> this.attr("data-lazy-src")
this.hasAttr("srcset") -> this.attr("srcset").substringBefore(" ")
else -> this.attr("src")
}
}
}
class Kinogeru : Chillx() {
override val name = "Kinoger"
override val mainUrl = "https://kinoger.ru"
}

View File

@ -0,0 +1,15 @@
package com.hexated
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
import com.lagradost.cloudstream3.plugins.Plugin
import android.content.Context
@CloudstreamPlugin
class KinogerPlugin: Plugin() {
override fun load(context: Context) {
// All providers should be added in this manner. Please don't edit the providers list directly.
registerMainAPI(Kinoger())
registerExtractorAPI(Kinogeru())
}
}

View File

@ -24,5 +24,5 @@ cloudstream {
"Movie",
)
iconUrl = "https://www.google.com/s2/favicons?domain=kisskh.me&sz=%size%"
iconUrl = "https://www.google.com/s2/favicons?domain=kisskh.co&sz=%size%"
}

View File

@ -1,5 +1,5 @@
// use an integer for version numbers
version = 34
version = 39
cloudstream {

View File

@ -3,13 +3,7 @@ package com.hexated
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.LoadResponse.Companion.addAniListId
import com.lagradost.cloudstream3.LoadResponse.Companion.addMalId
import com.lagradost.cloudstream3.network.WebViewResolver
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.Qualities
import com.lagradost.cloudstream3.utils.loadExtractor
import com.lagradost.nicehttp.requestCreator
import okhttp3.Headers
import okhttp3.HttpUrl
import org.jsoup.Jsoup
import org.jsoup.nodes.Element
@ -20,13 +14,10 @@ class KuramanimeProvider : MainAPI() {
override val hasMainPage = true
override var lang = "id"
override val hasDownloadSupport = true
private var params: AuthParams? = null
private var headers: Map<String,String> = mapOf()
private var cookies: Map<String,String> = mapOf()
override val supportedTypes = setOf(
TvType.Anime,
TvType.AnimeMovie,
TvType.OVA
TvType.Anime,
TvType.AnimeMovie,
TvType.OVA
)
companion object {
@ -46,15 +37,15 @@ class KuramanimeProvider : MainAPI() {
}
override val mainPage = mainPageOf(
"$mainUrl/anime/ongoing?order_by=updated&page=" to "Sedang Tayang",
"$mainUrl/anime/finished?order_by=updated&page=" to "Selesai Tayang",
"$mainUrl/properties/season/summer-2022?order_by=most_viewed&page=" to "Dilihat Terbanyak Musim Ini",
"$mainUrl/anime/movie?order_by=updated&page=" to "Film Layar Lebar",
"$mainUrl/anime/ongoing?order_by=updated&page=" to "Sedang Tayang",
"$mainUrl/anime/finished?order_by=updated&page=" to "Selesai Tayang",
"$mainUrl/properties/season/summer-2022?order_by=most_viewed&page=" to "Dilihat Terbanyak Musim Ini",
"$mainUrl/anime/movie?order_by=updated&page=" to "Film Layar Lebar",
)
override suspend fun getMainPage(
page: Int,
request: MainPageRequest
page: Int,
request: MainPageRequest
): HomePageResponse {
val document = app.get(request.data + page).document
@ -102,35 +93,40 @@ class KuramanimeProvider : MainAPI() {
val title = document.selectFirst(".anime__details__title > h3")!!.text().trim()
val poster = document.selectFirst(".anime__details__pic")?.attr("data-setbg")
val tags = document.select("div.anime__details__widget > div > div:nth-child(2) > ul > li:nth-child(1)")
.text().trim().replace("Genre: ", "").split(", ")
val tags =
document.select("div.anime__details__widget > div > div:nth-child(2) > ul > li:nth-child(1)")
.text().trim().replace("Genre: ", "").split(", ")
val year = Regex("\\D").replace(
document.select("div.anime__details__widget > div > div:nth-child(1) > ul > li:nth-child(5)")
.text().trim().replace("Musim: ", ""), ""
document.select("div.anime__details__widget > div > div:nth-child(1) > ul > li:nth-child(5)")
.text().trim().replace("Musim: ", ""), ""
).toIntOrNull()
val status = getStatus(
document.select("div.anime__details__widget > div > div:nth-child(1) > ul > li:nth-child(3)")
.text().trim().replace("Status: ", "")
document.select("div.anime__details__widget > div > div:nth-child(1) > ul > li:nth-child(3)")
.text().trim().replace("Status: ", "")
)
val description = document.select(".anime__details__text > p").text().trim()
val episodes = mutableListOf<Episode>()
for (i in 1..6) {
for (i in 1..10) {
val doc = app.get("$url?page=$i").document
val eps = Jsoup.parse(doc.select("#episodeLists").attr("data-content")).select("a.btn.btn-sm.btn-danger")
.mapNotNull {
val name = it.text().trim()
val episode = Regex("(\\d+[.,]?\\d*)").find(name)?.groupValues?.getOrNull(0)
?.toIntOrNull()
val link = it.attr("href")
Episode(link, episode = episode)
}
if(eps.isEmpty()) break else episodes.addAll(eps)
val eps = Jsoup.parse(doc.select("#episodeLists").attr("data-content"))
.select("a.btn.btn-sm.btn-danger")
.mapNotNull {
val name = it.text().trim()
val episode = Regex("(\\d+[.,]?\\d*)").find(name)?.groupValues?.getOrNull(0)
?.toIntOrNull()
val link = it.attr("href")
Episode(link, episode = episode)
}
if (eps.isEmpty()) break else episodes.addAll(eps)
}
val type = getType(document.selectFirst("div.col-lg-6.col-md-6 ul li:contains(Tipe:) a")?.text()?.lowercase() ?: "tv", episodes.size)
val type = getType(
document.selectFirst("div.col-lg-6.col-md-6 ul li:contains(Tipe:) a")?.text()
?.lowercase() ?: "tv", episodes.size
)
val recommendations = document.select("div#randomList > a").mapNotNull {
val epHref = it.attr("href")
val epTitle = it.select("h5.sidebar-title-h5.px-2.py-2").text()
@ -141,7 +137,7 @@ class KuramanimeProvider : MainAPI() {
}
}
val tracker = APIHolder.getTracker(listOf(title),TrackerType.getTypes(type),year,true)
val tracker = APIHolder.getTracker(listOf(title), TrackerType.getTypes(type), year, true)
return newAnimeLoadResponse(title, url, type) {
engName = title
@ -159,112 +155,14 @@ class KuramanimeProvider : MainAPI() {
}
private suspend fun invokeLocalSource(
url: String,
server: String,
ref: String,
callback: (ExtractorLink) -> Unit
) {
val document = app.get(
url,
referer = ref,
headers = headers,
cookies = cookies
).document
document.select("video#player > source").map {
val link = fixUrl(it.attr("src"))
val quality = it.attr("size").toIntOrNull()
callback.invoke(
ExtractorLink(
fixTitle(server),
fixTitle(server),
link,
referer = "",
quality = quality ?: Qualities.Unknown.value,
headers = mapOf(
"Accept" to "video/webm,video/ogg,video/*;q=0.9,application/ogg;q=0.7,audio/*;q=0.6,*/*;q=0.5",
"Range" to "bytes=0-",
"Sec-Fetch-Dest" to "video",
"Sec-Fetch-Mode" to "no-cors",
),
)
)
}
}
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
val req = app.get(data)
val res = req.document
argamap(
{
val auth = getAuth(data)
headers = auth.authHeader?.associate { it.first to it.second }?.filter { it.key != "Cookie" }!!
cookies = req.cookies
res.select("select#changeServer option").apmap { source ->
val server = source.attr("value")
val query = auth.serverUrl?.queryParameterNames?.map { it } ?: return@apmap
val link = "$data?${query[0]}=${getMisc(auth.authUrl)}&${query[1]}=$server"
if (server.contains(Regex("(?i)kuramadrive|archive"))) {
invokeLocalSource(link, server, data, callback)
} else {
app.get(
link,
referer = data,
headers = headers,
cookies = cookies
).document.select("div.iframe-container iframe").attr("src").let { videoUrl ->
loadExtractor(fixUrl(videoUrl), "$mainUrl/", subtitleCallback, callback)
}
}
}
},
{
res.select("div#animeDownloadLink a").apmap {
loadExtractor(it.attr("href"), "$mainUrl/", subtitleCallback, callback)
}
}
)
return true
}
private suspend fun fetchAuth(url: String) : AuthParams {
val regex = Regex("""$mainUrl/\S+""")
val found = WebViewResolver(
Regex("""$url(?!\?page=)\?"""),
additionalUrls = listOf(regex)
).resolveUsingWebView(
requestCreator(
"GET", url
)
)
val addition = found.second.findLast { it.headers["X-Requested-With"] == "XMLHttpRequest" }
return AuthParams(found.first?.url, addition?.url.toString(), addition?.headers)
}
private suspend fun getAuth(url: String) = params ?: fetchAuth(url).also { params = it }
private suspend fun getMisc(url: String?): String {
val misc = app.get(
"$url",
headers = headers,
cookies = cookies
)
cookies = misc.cookies
return misc.parsed()
}
data class AuthParams (
val serverUrl: HttpUrl?,
val authUrl: String?,
val authHeader: Headers?,
)
}

View File

@ -1,5 +1,5 @@
// use an integer for version numbers
version = 19
version = 20
cloudstream {

View File

@ -16,7 +16,7 @@ import java.net.URI
import java.util.ArrayList
class KuronimeProvider : MainAPI() {
override var mainUrl = "https://tv.kuronime.vip"
override var mainUrl = "https://tv1.kuronime.vip"
private var animekuUrl = "https://animeku.org"
override var name = "Kuronime"
override val hasQuickSearch = true

View File

@ -1,5 +1,5 @@
// use an integer for version numbers
version = 17
version = 20
cloudstream {
@ -23,5 +23,7 @@ cloudstream {
"Movie",
)
iconUrl = "https://www.google.com/s2/favicons?domain=amp.lk21official.mom&sz=%size%"
}

View File

@ -0,0 +1,86 @@
package com.hexated
import com.lagradost.cloudstream3.SubtitleFile
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.extractors.Filesim
import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.INFER_TYPE
import com.lagradost.cloudstream3.utils.M3u8Helper
import com.lagradost.cloudstream3.utils.getQualityFromName
open class Emturbovid : ExtractorApi() {
override val name = "Emturbovid"
override val mainUrl = "https://emturbovid.com"
override val requiresReferer = true
override suspend fun getUrl(
url: String,
referer: String?,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
) {
val response = app.get(url, referer = referer)
val m3u8 = Regex("[\"'](.*?master\\.m3u8.*?)[\"']").find(response.text)?.groupValues?.getOrNull(1)
M3u8Helper.generateM3u8(
name,
m3u8 ?: return,
mainUrl
).forEach(callback)
}
}
open class Hownetwork : ExtractorApi() {
override val name = "Hownetwork"
override val mainUrl = "https://stream.hownetwork.xyz"
override val requiresReferer = true
override suspend fun getUrl(
url: String,
referer: String?,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
) {
val id = url.substringAfter("id=")
val res = app.post(
"$mainUrl/api.php?id=$id",
data = mapOf(
"r" to "https://playeriframe.shop/",
"d" to "stream.hownetwork.xyz",
),
referer = url,
headers = mapOf(
"X-Requested-With" to "XMLHttpRequest"
)
).parsedSafe<Sources>()
res?.data?.map {
callback.invoke(
ExtractorLink(
this.name,
this.name,
it.file,
url,
getQualityFromName(it.label),
INFER_TYPE
)
)
}
}
data class Sources(
val data: ArrayList<Data>
) {
data class Data(
val file: String,
val label: String?,
)
}
}
class Furher : Filesim() {
override val name = "Furher"
override var mainUrl = "https://furher.in"
}

View File

@ -8,29 +8,31 @@ import com.lagradost.cloudstream3.utils.*
import org.jsoup.nodes.Element
class LayarKacaProvider : MainAPI() {
override var mainUrl = "https://amp.lk21official.mom"
private var seriesUrl = "https://tv12.nontondrama.click/"
override var name = "LayarKaca"
override val hasMainPage = true
override var lang = "id"
override val supportedTypes = setOf(
TvType.Movie,
TvType.TvSeries,
TvType.AsianDrama
TvType.Movie,
TvType.TvSeries,
TvType.AsianDrama
)
override val mainPage = mainPageOf(
"$mainUrl/populer/page/" to "Film Terplopuler",
"$mainUrl/rating/page/" to "Film Berdasarkan IMDb Rating",
"$mainUrl/most-commented/page/" to "Film Dengan Komentar Terbanyak",
"$seriesUrl/latest/page/" to "Series Terbaru",
"$seriesUrl/series/asian/page/" to "Film Asian Terbaru",
"$mainUrl/latest/page/" to "Film Upload Terbaru",
"$mainUrl/populer/page/" to "Film Terplopuler",
"$mainUrl/rating/page/" to "Film Berdasarkan IMDb Rating",
"$mainUrl/most-commented/page/" to "Film Dengan Komentar Terbanyak",
"$seriesUrl/latest-series/page/" to "Series Terbaru",
"$seriesUrl/series/asian/page/" to "Film Asian Terbaru",
"$mainUrl/latest/page/" to "Film Upload Terbaru",
)
override suspend fun getMainPage(
page: Int,
request: MainPageRequest
page: Int,
request: MainPageRequest
): HomePageResponse {
val document = app.get(request.data + page).document
val home = document.select("article.mega-item").mapNotNull {
@ -40,6 +42,7 @@ class LayarKacaProvider : MainAPI() {
}
private suspend fun getProperLink(url: String): String? {
if(url.startsWith(seriesUrl)) return url
val res = app.get(url).document
return if (res.select("title").text().contains("- Nontondrama", true)) {
res.selectFirst("div#content a")?.attr("href")
@ -53,10 +56,10 @@ class LayarKacaProvider : MainAPI() {
val href = fixUrl(this.selectFirst("a")!!.attr("href"))
val posterUrl = fixUrlNull(this.selectFirst("img")?.attr("src"))
val type =
if (this.selectFirst("div.last-episode") == null) TvType.Movie else TvType.TvSeries
if (this.selectFirst("div.last-episode") == null) TvType.Movie else TvType.TvSeries
return if (type == TvType.TvSeries) {
val episode = this.selectFirst("div.last-episode span")?.text()?.filter { it.isDigit() }
?.toIntOrNull()
?.toIntOrNull()
newAnimeSearchResponse(title, href, TvType.TvSeries) {
this.posterUrl = posterUrl
addSub(episode)
@ -91,23 +94,23 @@ class LayarKacaProvider : MainAPI() {
val tags = document.select("div.content > div:nth-child(5) > h3 > a").map { it.text() }
val year = Regex("\\d, (\\d+)").find(
document.select("div.content > div:nth-child(7) > h3").text().trim()
document.select("div.content > div:nth-child(7) > h3").text().trim()
)?.groupValues?.get(1).toString().toIntOrNull()
val tvType = if (document.select("div.serial-wrapper")
.isNotEmpty()
.isNotEmpty()
) TvType.TvSeries else TvType.Movie
val description = document.select("div.content > blockquote").text().trim()
val trailer = document.selectFirst("div.action-player li > a.fancybox")?.attr("href")
val rating =
document.selectFirst("div.content > div:nth-child(6) > h3")?.text()?.toRatingInt()
document.selectFirst("div.content > div:nth-child(6) > h3")?.text()?.toRatingInt()
val actors =
document.select("div.col-xs-9.content > div:nth-child(3) > h3 > a").map { it.text() }
document.select("div.col-xs-9.content > div:nth-child(3) > h3 > a").map { it.text() }
val recommendations = document.select("div.row.item-media").map {
val recName = it.selectFirst("h3")?.text()?.trim().toString()
val recHref = it.selectFirst(".content-media > a")!!.attr("href")
val recPosterUrl =
fixUrl(it.selectFirst(".poster-media > a > img")?.attr("src").toString())
fixUrl(it.selectFirst(".poster-media > a > img")?.attr("src").toString())
newTvSeriesSearchResponse(recName, recHref, TvType.TvSeries) {
this.posterUrl = recPosterUrl
}
@ -118,12 +121,12 @@ class LayarKacaProvider : MainAPI() {
val href = fixUrl(it.attr("href"))
val episode = it.text().toIntOrNull()
val season =
it.attr("href").substringAfter("season-").substringBefore("-").toIntOrNull()
it.attr("href").substringAfter("season-").substringBefore("-").toIntOrNull()
Episode(
href,
"Episode $episode",
season,
episode,
href,
"Episode $episode",
season,
episode,
)
}.reversed()
newTvSeriesLoadResponse(title, url, TvType.TvSeries, episodes) {
@ -151,10 +154,10 @@ class LayarKacaProvider : MainAPI() {
}
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
val document = app.get(data).document
@ -172,30 +175,3 @@ class LayarKacaProvider : MainAPI() {
}
}
open class Emturbovid : ExtractorApi() {
override val name = "Emturbovid"
override val mainUrl = "https://emturbovid.com"
override val requiresReferer = true
override suspend fun getUrl(
url: String,
referer: String?,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
) {
val response = app.get(url, referer = referer)
val m3u8 = Regex("[\"'](.*?master\\.m3u8.*?)[\"']").find(response.text)?.groupValues?.getOrNull(1)
M3u8Helper.generateM3u8(
name,
m3u8 ?: return,
mainUrl
).forEach(callback)
}
}
class Furher : Filesim() {
override val name = "Furher"
override var mainUrl = "https://furher.in"
}

View File

@ -12,5 +12,6 @@ class LayarKacaProviderPlugin: Plugin() {
registerMainAPI(LayarKacaProvider())
registerExtractorAPI(Emturbovid())
registerExtractorAPI(Furher())
registerExtractorAPI(Hownetwork())
}
}

View File

@ -268,7 +268,7 @@ class Loklok : MainAPI() {
}
private suspend fun getTracker(title: String?, type: String?, year: Int?): Tracker {
val res = app.get("https://api.consumet.org/meta/anilist/$title")
val res = app.get("https://consumet-instance.vercel.app/meta/anilist/$title")
.parsedSafe<AniSearch>()?.results?.find { media ->
(media.title?.english.equals(title, true) || media.title?.romaji.equals(
title,

View File

@ -1,5 +1,5 @@
// use an integer for version numbers
version = 9
version = 11
cloudstream {

View File

@ -7,6 +7,7 @@ import com.lagradost.cloudstream3.fixTitle
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.Qualities
import com.lagradost.cloudstream3.utils.fixUrl
import com.lagradost.cloudstream3.utils.getAndUnpack
import com.lagradost.cloudstream3.utils.getQualityFromName
@ -32,28 +33,30 @@ open class Streampai : ExtractorApi() {
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
) {
val res = app.get(url, referer = referer).text
val data = getAndUnpack(res)
val res = app.get(url, referer = referer).document
val data = res.selectFirst("script:containsData(player =)")?.data() ?: return
val sources = data.substringAfter("sources: [").substringBefore("]")
.addMarks("src")
.addMarks("type")
.addMarks("size")
.replace("\'", "\"")
val tracks = data.substringAfter("tracks: [").substringBefore("]")
.replace("\'", "\"")
val sources = data.substringAfter("sources:[").substringBefore("]").replace("\'", "\"")
val tracks = data.substringAfter("\"tracks\":[").substringBefore("]")
tryParseJson<List<Responses>>("[$sources]")?.forEach {
callback.invoke(
ExtractorLink(
this.name,
this.name,
fixUrl(it.file),
fixUrl(it.src),
url,
getQualityFromName(it.label),
it.size ?: Qualities.Unknown.value,
headers = mapOf(
"Accept" to "video/webm,video/ogg,video/*;q=0.9,application/ogg;q=0.7,audio/*;q=0.6,*/*;q=0.5",
"Accept-Language" to "en-US,en;q=0.5",
"DNT" to "1",
"Range" to "bytes=0-",
"Sec-Fetch-Dest" to "video",
"Sec-Fetch-Mode" to "no-cors",
)
),
)
)
}
@ -61,17 +64,22 @@ open class Streampai : ExtractorApi() {
tryParseJson<List<Responses>>("[$tracks]")?.forEach {
subtitleCallback.invoke(
SubtitleFile(
fixTitle(it.label ?: ""),
fixUrl(it.file),
fixTitle(it.label ?: return@forEach),
fixUrl(it.src)
)
)
}
}
private fun String.addMarks(str: String): String {
return this.replace(Regex("\"?$str\"?"), "\"$str\"")
}
data class Responses(
@JsonProperty("file") val file: String,
@JsonProperty("src") val src: String,
@JsonProperty("type") val type: String?,
@JsonProperty("label") val label: String?
@JsonProperty("label") val label: String?,
@JsonProperty("size") val size: Int?
)
}

26
Moflix/build.gradle.kts Normal file
View File

@ -0,0 +1,26 @@
// use an integer for version numbers
version = 3
cloudstream {
language = "de"
// All of these properties are optional, you can safely remove them
description = "Include: Cineclix"
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=moflix-stream.xyz&sz=%size%"
}

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.hexated"/>

View File

@ -0,0 +1,14 @@
package com.hexated
import com.lagradost.cloudstream3.mainPageOf
class Cineclix : Moflix() {
override var name = "Cineclix"
override var mainUrl = "https://cineclix.in"
override val mainPage = mainPageOf(
"77/created_at:desc" to "Neuerscheinungen Filme",
"82/created_at:desc" to "Neuerscheinungen Serien",
"77/popularity:desc" to "Filme",
"82/popularity:desc" to "Serien",
)
}

View File

@ -0,0 +1,94 @@
package com.hexated
import com.lagradost.cloudstream3.SubtitleFile
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.*
import java.net.URI
class MoflixLink : MoflixClick() {
override val name = "MoflixLink"
override val mainUrl = "https://moflix-stream.link"
}
class MoflixFans : MoflixClick() {
override val name = "MoflixFans"
override val mainUrl = "https://moflix-stream.fans"
}
class Highstream : MoflixClick() {
override val name = "Highstream"
override val mainUrl = "https://highstream.tv"
}
open class MoflixClick : ExtractorApi() {
override val name = "MoflixClick"
override val mainUrl = "https://moflix-stream.click"
override val requiresReferer = true
override suspend fun getUrl(
url: String,
referer: String?,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
) {
val response = app.get(url, referer = referer)
val script = if (!getPacked(response.text).isNullOrEmpty()) {
getAndUnpack(response.text)
} else {
response.document.selectFirst("script:containsData(sources:)")?.data()
}
val m3u8 =
Regex("file:\\s*\"(.*?m3u8.*?)\"").find(script ?: return)?.groupValues?.getOrNull(1)
callback.invoke(
ExtractorLink(
name,
name,
m3u8 ?: return,
"$mainUrl/",
Qualities.Unknown.value,
INFER_TYPE
)
)
}
}
open class Doodstream : ExtractorApi() {
override val name = "Doodstream"
override val mainUrl = "https://doodstream.com"
override val requiresReferer = false
override suspend fun getUrl(
url: String,
referer: String?,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
) {
val req = app.get(url)
val host = getBaseUrl(req.url)
val response0 = req.text
val md5 = host + (Regex("/pass_md5/[^']*").find(response0)?.value ?: return)
val trueUrl =
app.get(md5, referer = req.url).text + "qWMG3yc6F5?token=" + md5.substringAfterLast("/")
val quality = Regex("\\d{3,4}p").find(
response0.substringAfter("<title>").substringBefore("</title>")
)?.groupValues?.get(0)
callback.invoke(
ExtractorLink(
this.name,
this.name,
trueUrl,
mainUrl,
getQualityFromName(quality),
false
)
)
}
private fun getBaseUrl(url: String): String {
return URI(url).let {
"${it.scheme}://${it.host}"
}
}
}

View File

@ -0,0 +1,341 @@
package com.hexated
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.LoadResponse.Companion.addImdbId
import com.lagradost.cloudstream3.LoadResponse.Companion.addTMDbId
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
import com.lagradost.cloudstream3.utils.*
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
import com.lagradost.cloudstream3.utils.AppUtils.toJson
import org.jsoup.Jsoup
import kotlin.math.roundToInt
open class Moflix : MainAPI() {
override var name = "Moflix"
override var mainUrl = "https://moflix-stream.xyz"
override var lang = "de"
override val hasMainPage = true
override val hasQuickSearch = true
override val supportedTypes = setOf(TvType.TvSeries, TvType.Movie)
companion object {
fun getType(isSeries: Boolean?): TvType {
return when (isSeries) {
true -> TvType.TvSeries
else -> TvType.Movie
}
}
fun getStatus(t: String?): ShowStatus {
return when (t) {
"ongoing" -> ShowStatus.Ongoing
else -> ShowStatus.Completed
}
}
}
override val mainPage = mainPageOf(
"351/channelables.order:asc" to "Kürzlich hinzugefügt",
"345/popularity:desc" to "Movie-Datenbank",
"352/channelables.order:asc" to "Angesagte Serien",
"358/channelables.order:asc" to "Kinder & Familien",
)
override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse {
val query = request.data.split("/")
val home = app.get(
"$mainUrl/api/v1/channel/${query.first()}?returnContentOnly=true&restriction=&order=${query.last()}&paginate=simple&perPage=50&query=&page=$page",
referer = "$mainUrl/"
).parsedSafe<Responses>()?.pagination?.data?.mapNotNull { it.toSearchResponse() }
?: emptyList()
return newHomePageResponse(request.name, home)
}
private fun Data.toSearchResponse(): SearchResponse? {
return newTvSeriesSearchResponse(
this.name ?: return null,
"${this.id}",
TvType.TvSeries,
false
) {
posterUrl = this@toSearchResponse.poster?.compress()
}
}
override suspend fun quickSearch(query: String): List<SearchResponse>? = search(query)
override suspend fun search(query: String): List<SearchResponse>? {
return app.get("$mainUrl/api/v1/search/$query?loader=searchPage", referer = "$mainUrl/")
.parsedSafe<Responses>()?.results?.mapNotNull { it.toSearchResponse() }
}
override suspend fun load(url: String): LoadResponse {
val res = app.get(
"$mainUrl/api/v1/titles/${url.fixId()}?loader=titlePage",
referer = "$mainUrl/"
).parsedSafe<Responses>()
val uri = Jsoup.parse(res?.seo.toString()).selectFirst("link[rel=canonical]")?.attr("href")
val id = res?.title?.id
val title = res?.title?.name ?: ""
val poster = res?.title?.poster
val backdrop = res?.title?.backdrop
val tags = res?.title?.keywords?.mapNotNull { it.displayName }
val year = res?.title?.year
val isSeries = res?.title?.isSeries
val certification = res?.title?.certification
val duration = res?.title?.runtime
val type = getType(isSeries)
val description = res?.title?.description
val trailers = res?.title?.videos?.filter { it.category.equals("trailer", true) }
?.mapNotNull { it.src }
val rating = "${res?.title?.rating}".toRatingInt()
val actors = res?.credits?.actors?.mapNotNull {
ActorData(
Actor(it.name ?: return@mapNotNull null, it.poster),
roleString = it.pivot?.character
)
}
val recommendations = app.get("$mainUrl/api/v1/titles/$id/related", referer = "$mainUrl/")
.parsedSafe<Responses>()?.titles?.mapNotNull { it.toSearchResponse() }
return if (type == TvType.TvSeries) {
val episodes = res?.seasons?.data?.mapNotNull { season ->
app.get(
"$mainUrl/api/v1/titles/${res.title?.id}/seasons/${season.number}?loader=seasonPage",
referer = "$mainUrl/"
).parsedSafe<Responses>()?.episodes?.data?.map { episode ->
val status =
if (episode.status.equals("upcoming", true)) " • [UPCOMING]" else ""
Episode(
LoadData(
id,
episode.seasonNumber,
episode.episodeNumber,
res.title?.isSeries
).toJson(),
episode.name + status,
episode.seasonNumber,
episode.episodeNumber,
episode.poster,
episode.rating?.times(10)?.roundToInt(),
episode.description,
).apply {
this.addDate(episode.releaseDate?.substringBefore("T"))
}
}
}?.flatten() ?: emptyList()
newTvSeriesLoadResponse(title, uri ?: url, TvType.TvSeries, episodes) {
this.posterUrl = poster
this.backgroundPosterUrl = backdrop
this.year = year
this.showStatus = getStatus(res?.title?.status)
this.plot = description
this.tags = tags
this.rating = rating
this.actors = actors
this.duration = duration
this.recommendations = recommendations
this.contentRating = certification
addTrailer(trailers)
addImdbId(res?.title?.imdbId)
addTMDbId(res?.title?.tmdbId)
}
} else {
val urls = res?.title?.videos?.filter { it.category.equals("full", true) }
newMovieLoadResponse(
title,
uri ?: url,
TvType.Movie,
LoadData(isSeries = isSeries, urls = urls)
) {
this.posterUrl = poster
this.backgroundPosterUrl = backdrop
this.year = year
this.comingSoon = res?.title?.status.equals("upcoming", true)
this.plot = description
this.tags = tags
this.rating = rating
this.actors = actors
this.duration = duration
this.recommendations = recommendations
this.contentRating = certification
addTrailer(trailers)
addImdbId(res?.title?.imdbId)
addTMDbId(res?.title?.tmdbId)
}
}
}
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
val json = parseJson<LoadData>(data)
val iframes = if (json.isSeries == true) {
app.get(
"$mainUrl/api/v1/titles/${json.id}/seasons/${json.season}/episodes/${json.episode}?loader=episodePage",
referer = "$mainUrl/"
).parsedSafe<Episodes>()?.episode?.videos?.filter { it.category.equals("full", true) }
} else {
json.urls
}
iframes?.apmap { iframe ->
loadCustomExtractor(
iframe.src ?: return@apmap,
"$mainUrl/",
subtitleCallback,
callback,
iframe.quality?.substringBefore("/")?.filter { it.isDigit() }?.toIntOrNull()
)
}
return true
}
private fun String.fixId(): String {
val chunk = "/titles/"
return if (this.contains(chunk)) this.substringAfter(chunk)
.substringBefore("/") else this.substringAfterLast("/")
}
private suspend fun loadCustomExtractor(
url: String,
referer: String? = null,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit,
quality: Int? = null,
) {
loadExtractor(url, referer, subtitleCallback) { link ->
if (link.quality == Qualities.Unknown.value || !link.isM3u8) {
callback.invoke(
ExtractorLink(
link.source,
link.name,
link.url,
link.referer,
quality ?: link.quality,
link.type,
link.headers,
link.extractorData
)
)
}
}
}
private fun String.compress(): String {
return this.replace("/original/", "/w500/")
}
data class LoadData(
val id: Int? = null,
val season: Int? = null,
val episode: Int? = null,
val isSeries: Boolean? = null,
val urls: List<Videos>? = listOf(),
)
data class Responses(
@JsonProperty("pagination") val pagination: Pagination? = null,
@JsonProperty("title") val title: Title? = null,
@JsonProperty("seo") val seo: String? = null,
@JsonProperty("credits") val credits: Credits? = null,
@JsonProperty("seasons") val seasons: Seasons? = null,
@JsonProperty("episodes") val episodes: Episodes? = null,
@JsonProperty("titles") val titles: ArrayList<Data>? = arrayListOf(),
@JsonProperty("results") val results: ArrayList<Data>? = arrayListOf(),
)
data class Seasons(
@JsonProperty("data") val data: ArrayList<Data>? = arrayListOf(),
) {
data class Data(
@JsonProperty("id") val id: Int? = null,
@JsonProperty("number") val number: Int? = null,
@JsonProperty("poster") val poster: String? = null,
@JsonProperty("release_date") val releaseDate: String? = null,
)
}
data class Episodes(
@JsonProperty("data") val data: ArrayList<Data>? = arrayListOf(),
@JsonProperty("episode") val episode: Data? = null,
) {
data class Data(
@JsonProperty("id") val id: Int? = null,
@JsonProperty("name") val name: String? = null,
@JsonProperty("description") val description: String? = null,
@JsonProperty("season_number") val seasonNumber: Int? = null,
@JsonProperty("episode_number") val episodeNumber: Int? = null,
@JsonProperty("rating") val rating: Float? = null,
@JsonProperty("poster") val poster: String? = null,
@JsonProperty("release_date") val releaseDate: String? = null,
@JsonProperty("status") val status: String? = null,
@JsonProperty("videos") val videos: ArrayList<Videos>? = arrayListOf(),
)
}
data class Pagination(
@JsonProperty("data") val data: ArrayList<Data>? = arrayListOf(),
)
data class Data(
@JsonProperty("id") val id: Any? = null,
@JsonProperty("name") val name: String? = null,
@JsonProperty("poster") val poster: String? = null,
@JsonProperty("backdrop") val backdrop: String? = null,
)
data class Credits(
@JsonProperty("actors") val actors: ArrayList<Actors>? = arrayListOf(),
) {
data class Actors(
@JsonProperty("name") val name: String? = null,
@JsonProperty("poster") val poster: String? = null,
@JsonProperty("pivot") val pivot: Pivot? = null,
) {
data class Pivot(
@JsonProperty("character") val character: String? = null,
)
}
}
data class Videos(
@JsonProperty("category") val category: String? = null,
@JsonProperty("src") val src: String? = null,
@JsonProperty("quality") val quality: String? = null,
)
data class Title(
@JsonProperty("id") val id: Int? = null,
@JsonProperty("name") val name: String? = null,
@JsonProperty("release_date") val releaseDate: String? = null,
@JsonProperty("year") val year: Int? = null,
@JsonProperty("runtime") val runtime: Int? = null,
@JsonProperty("poster") val poster: String? = null,
@JsonProperty("backdrop") val backdrop: String? = null,
@JsonProperty("description") val description: String? = null,
@JsonProperty("certification") val certification: String? = null,
@JsonProperty("rating") val rating: Float? = null,
@JsonProperty("imdb_id") val imdbId: String? = null,
@JsonProperty("tmdb_id") val tmdbId: String? = null,
@JsonProperty("status") val status: String? = null,
@JsonProperty("is_series") val isSeries: Boolean? = null,
@JsonProperty("videos") val videos: ArrayList<Videos>? = arrayListOf(),
@JsonProperty("keywords") val keywords: ArrayList<Keywords>? = arrayListOf(),
) {
data class Keywords(
@JsonProperty("display_name") val displayName: String? = null,
)
}
}

View File

@ -0,0 +1,20 @@
package com.hexated
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
import com.lagradost.cloudstream3.plugins.Plugin
import android.content.Context
@CloudstreamPlugin
class MoflixPlugin: Plugin() {
override fun load(context: Context) {
// All providers should be added in this manner. Please don't edit the providers list directly.
registerMainAPI(Moflix())
registerMainAPI(Cineclix())
registerExtractorAPI(MoflixClick())
registerExtractorAPI(Highstream())
registerExtractorAPI(MoflixFans())
registerExtractorAPI(MoflixLink())
registerExtractorAPI(Doodstream())
}
}

View File

@ -1,5 +1,5 @@
// use an integer for version numbers
version = 55
version = 63
cloudstream {

View File

@ -5,7 +5,7 @@ import com.lagradost.cloudstream3.mainPageOf
class Animesaga : Movierulzhd() {
override var mainUrl = "https://www.animesaga.in"
override var mainUrl = "https://anplay.in"
override var name = "Animesaga"
override val supportedTypes = setOf(
TvType.Anime,
@ -18,6 +18,4 @@ class Animesaga : Movierulzhd() {
"tvshows" to "TV-Shows",
"genre/hindi-dub" to "Hindi Dub",
)
}

View File

@ -68,5 +68,5 @@ open class Akamaicdn : ExtractorApi() {
class AnimesagaStream : Chillx() {
override val name = "AnimesagaStream"
override val mainUrl = "https://stream.animesaga.in"
override val mainUrl = "https://stream.anplay.in"
}

View File

@ -12,14 +12,14 @@ import org.jsoup.Jsoup
class Hdmovie2 : Movierulzhd() {
override var mainUrl = "https://hdmovie2.li"
override var mainUrl = "https://hdmovie2.tax"
override var name = "Hdmovie2"
override val mainPage = mainPageOf(
"trending" to "Trending",
"movies" to "Movies",
"genre/tv-series" to "TV-Series",
"genre/tv-series" to "TV Shows",
"genre/netflix" to "Netflix",
"genre/zee5-tv-series" to "Zee5 TV Series",
"genre/zee5-tv-series" to "Zee5",
)
override suspend fun loadLinks(

View File

@ -11,7 +11,7 @@ import java.net.URI
open class Movierulzhd : MainAPI() {
override var mainUrl = "https://movierulzhd.bet"
override var mainUrl = "https://movierulzhd.cafe"
var directUrl = ""
override var name = "Movierulzhd"
override val hasMainPage = true

View File

@ -24,5 +24,5 @@ cloudstream {
"OVA",
)
iconUrl = "https://www.google.com/s2/favicons?domain=neonime.watch&sz=%size%"
iconUrl = "https://www.google.com/s2/favicons?domain=neonime.ink&sz=%size%"
}

View File

@ -1,5 +1,5 @@
// use an integer for version numbers
version = 7
version = 11
cloudstream {

View File

@ -1,12 +1,8 @@
package com.hexated
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.SubtitleFile
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.INFER_TYPE
import com.lagradost.cloudstream3.utils.Qualities
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.utils.*
open class Mitedrive : ExtractorApi() {
override val name = "Mitedrive"
@ -50,4 +46,50 @@ open class Mitedrive : ExtractorApi() {
@JsonProperty("data") val data: Data? = null,
)
}
open class Berkasdrive : ExtractorApi() {
override val name = "Berkasdrive"
override val mainUrl = "https://dl.berkasdrive.com"
override val requiresReferer = true
override suspend fun getUrl(
url: String,
referer: String?,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
) {
val res = app.get(url, referer = referer).document
val video = res.select("video#player source").attr("src")
callback.invoke(
ExtractorLink(
this.name,
this.name,
video,
"$mainUrl/",
Qualities.Unknown.value,
INFER_TYPE
)
)
}
}
open class Videogami : ExtractorApi() {
override val name = "Videogami"
override val mainUrl = "https://video.nimegami.id"
override val requiresReferer = false
override suspend fun getUrl(
url: String,
referer: String?,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
) {
val id = base64Decode(url.substringAfter("url=")).substringAfterLast("/")
loadExtractor("https://hxfile.co/embed-$id.html", "$mainUrl/", subtitleCallback, callback)
}
}

View File

@ -9,7 +9,6 @@ import com.lagradost.cloudstream3.utils.*
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
import org.jsoup.nodes.Element
import org.jsoup.select.Elements
import java.net.URI
class Nimegami : MainAPI() {
override var mainUrl = "https://nimegami.id"
@ -77,14 +76,18 @@ class Nimegami : MainAPI() {
this.posterUrl = posterUrl
addSub(episode)
}
}
override suspend fun search(query: String): List<SearchResponse> {
return app.get("$mainUrl/?s=$query&post_type=post").document.select("div.archive article")
.mapNotNull {
it.toSearchResult()
}
val searchResponse = mutableListOf<SearchResponse>()
for (i in 1..2) {
val res = app.get("$mainUrl/page/$i/?s=$query&post_type=post").document.select("div.archive article")
.mapNotNull {
it.toSearchResult()
}
searchResponse.addAll(res)
}
return searchResponse
}
override suspend fun load(url: String): LoadResponse {
@ -150,7 +153,7 @@ class Nimegami : MainAPI() {
tryParseJson<ArrayList<Sources>>(base64Decode(data))?.map { sources ->
sources.url?.apmap { url ->
loadFixedExtractor(
url.fixIframe(),
url,
sources.format,
"$mainUrl/",
subtitleCallback,
@ -185,32 +188,10 @@ class Nimegami : MainAPI() {
}
}
private fun getBaseUrl(url: String): String {
return URI(url).let {
"${it.scheme}://${it.host}"
}
}
private fun Elements.getContent(css: String): Elements {
return this.select("tr:contains($css) td:last-child")
}
private fun String.fixIframe(): String {
val url = base64Decode(this.substringAfter("url=").substringAfter("id="))
return when {
url.contains("hxfile") -> {
val host = getBaseUrl(url)
val id = url.substringAfterLast("/")
"$host/embed-$id.html"
}
url.startsWith("https://mitedrive.my.id") -> url.replace(
"https://mitedrive.my.id",
"https://mitedrive.com"
)
else -> fixUrl(url)
}
}
data class Sources(
@JsonProperty("format") val format: String? = null,
@JsonProperty("url") val url: ArrayList<String>? = arrayListOf(),

View File

@ -11,5 +11,7 @@ class NimegamiPlugin: Plugin() {
// All providers should be added in this manner. Please don't edit the providers list directly.
registerMainAPI(Nimegami())
registerExtractorAPI(Mitedrive())
registerExtractorAPI(Berkasdrive())
registerExtractorAPI(Videogami())
}
}

View File

@ -1,5 +1,5 @@
// use an integer for version numbers
version = 19
version = 21
cloudstream {
@ -23,5 +23,5 @@ cloudstream {
"OVA",
)
iconUrl = "https://www.google.com/s2/favicons?domain=nontonanimeid.site&sz=%size%"
iconUrl = "https://www.google.com/s2/favicons?domain=nontonanimeid.org&sz=%size%"
}

View File

@ -0,0 +1,86 @@
package com.hexated
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.APIHolder
import com.lagradost.cloudstream3.SubtitleFile
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.extractors.Hxfile
import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.Qualities
open class Gdplayer : ExtractorApi() {
override val name = "Gdplayer"
override val mainUrl = "https://gdplayer.to"
override val requiresReferer = true
override suspend fun getUrl(
url: String,
referer: String?,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
) {
val res = app.get(url, referer = referer).document
val script = res.selectFirst("script:containsData(player = \"\")")?.data()
val kaken = script?.substringAfter("kaken = \"")?.substringBefore("\"")
val json = app.get(
"$mainUrl/api/?${kaken ?: return}=&_=${APIHolder.unixTimeMS}",
headers = mapOf(
"X-Requested-With" to "XMLHttpRequest"
)
).parsedSafe<Response>()
json?.sources?.map {
callback.invoke(
ExtractorLink(
this.name,
this.name,
it.file ?: return@map,
"",
getQuality(json.title)
)
)
}
}
private fun getQuality(str: String?): Int {
return Regex("(\\d{3,4})[pP]").find(str ?: "")?.groupValues?.getOrNull(1)?.toIntOrNull()
?: Qualities.Unknown.value
}
data class Response(
@JsonProperty("title") val title: String? = null,
@JsonProperty("sources") val sources: ArrayList<Sources>? = null,
) {
data class Sources(
@JsonProperty("file") val file: String? = null,
@JsonProperty("type") val type: String? = null,
)
}
}
class Nontonanimeid : Hxfile() {
override val name = "Nontonanimeid"
override val mainUrl = "https://nontonanimeid.com"
override val requiresReferer = true
}
class EmbedKotakAnimeid : Hxfile() {
override val name = "EmbedKotakAnimeid"
override val mainUrl = "https://embed2.kotakanimeid.com"
override val requiresReferer = true
}
class Kotaksb : Hxfile() {
override val name = "Kotaksb"
override val mainUrl = "https://kotaksb.fun"
override val requiresReferer = true
}
class KotakAnimeidCom : Hxfile() {
override val name = "KotakAnimeid"
override val mainUrl = "https://kotakanimeid.com"
override val requiresReferer = true
}

View File

@ -5,7 +5,7 @@ import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.LoadResponse.Companion.addAniListId
import com.lagradost.cloudstream3.LoadResponse.Companion.addMalId
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
import com.lagradost.cloudstream3.extractors.Hxfile
import com.lagradost.cloudstream3.utils.AppUtils
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.loadExtractor
import org.jsoup.Jsoup
@ -13,7 +13,7 @@ import org.jsoup.nodes.Element
import java.net.URI
class NontonAnimeIDProvider : MainAPI() {
override var mainUrl = "https://nontonanimeid.top"
override var mainUrl = "https://nontonanimeid.org"
override var name = "NontonAnimeID"
override val hasQuickSearch = false
override val hasMainPage = true
@ -29,8 +29,8 @@ class NontonAnimeIDProvider : MainAPI() {
companion object {
fun getType(t: String): TvType {
return when {
t.contains("TV",true) -> TvType.Anime
t.contains("Movie",true) -> TvType.AnimeMovie
t.contains("TV", true) -> TvType.Anime
t.contains("Movie", true) -> TvType.AnimeMovie
else -> TvType.OVA
}
}
@ -50,7 +50,7 @@ class NontonAnimeIDProvider : MainAPI() {
"popular-series/" to "Popular Series",
)
override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse {
override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse {
val document = app.get("$mainUrl/${request.data}").document
val home = document.select(".animeseries").mapNotNull {
it.toSearchResult()
@ -61,7 +61,7 @@ class NontonAnimeIDProvider : MainAPI() {
private fun Element.toSearchResult(): AnimeSearchResponse {
val href = fixUrl(this.selectFirst("a")!!.attr("href"))
val title = this.selectFirst(".title")?.text() ?: ""
val posterUrl = fixUrlNull(this.selectFirst("img")?.attr("data-src"))
val posterUrl = fixUrlNull(this.selectFirst("img")?.getImageAttr())
return newAnimeSearchResponse(title, href, TvType.Anime) {
this.posterUrl = posterUrl
@ -76,7 +76,7 @@ class NontonAnimeIDProvider : MainAPI() {
return document.select(".result > ul > li").mapNotNull {
val title = it.selectFirst("h2")!!.text().trim()
val poster = it.selectFirst("img")!!.attr("src")
val poster = it.selectFirst("img")?.getImageAttr()
val tvType = getType(
it.selectFirst(".boxinfores > span.typeseries")!!.text().toString()
)
@ -100,8 +100,9 @@ class NontonAnimeIDProvider : MainAPI() {
mainUrl = getBaseUrl(req.url)
val document = req.document
val title = document.selectFirst("h1.entry-title.cs")!!.text().removeSurrounding("Nonton Anime", "Sub Indo").trim()
val poster = document.selectFirst(".poster > img")?.attr("data-src")
val title = document.selectFirst("h1.entry-title.cs")!!.text()
.removeSurrounding("Nonton Anime", "Sub Indo").trim()
val poster = document.selectFirst(".poster > img")?.getImageAttr()
val tags = document.select(".tagline > a").map { it.text() }
val year = Regex("\\d, (\\d*)").find(
@ -150,14 +151,14 @@ class NontonAnimeIDProvider : MainAPI() {
val recommendations = document.select(".result > li").mapNotNull {
val epHref = it.selectFirst("a")!!.attr("href")
val epTitle = it.selectFirst("h3")!!.text()
val epPoster = it.select(".top > img").attr("data-src")
val epPoster = it.selectFirst(".top > img")?.getImageAttr()
newAnimeSearchResponse(epTitle, epHref, TvType.Anime) {
this.posterUrl = epPoster
addDubStatus(dubExist = false, subExist = true)
}
}
val tracker = APIHolder.getTracker(listOf(title),TrackerType.getTypes(type),year,true)
val tracker = APIHolder.getTracker(listOf(title), TrackerType.getTypes(type), year, true)
return newAnimeLoadResponse(title, url, type) {
engName = title
@ -186,6 +187,12 @@ class NontonAnimeIDProvider : MainAPI() {
val document = app.get(data).document
val nonce =
document.select("script#ajax_video-js-extra").attr("src").substringAfter("base64,")
.let {
AppUtils.parseJson<Map<String, String>>(base64Decode(it).substringAfter("="))["nonce"]
}
document.select(".container1 > ul > li:not(.boxtab)").apmap {
val dataPost = it.attr("data-post")
val dataNume = it.attr("data-nume")
@ -198,13 +205,13 @@ class NontonAnimeIDProvider : MainAPI() {
"post" to dataPost,
"nume" to dataNume,
"type" to dataType,
"nonce" to "e4dd8e45c2"
"nonce" to "$nonce"
),
referer = data,
headers = mapOf("X-Requested-With" to "XMLHttpRequest")
).document.selectFirst("iframe")?.attr("src")
loadExtractor(iframe ?: return@apmap , "$mainUrl/", subtitleCallback, callback)
loadExtractor(iframe ?: return@apmap, "$mainUrl/", subtitleCallback, callback)
}
return true
@ -215,6 +222,16 @@ class NontonAnimeIDProvider : MainAPI() {
"${it.scheme}://${it.host}"
}
}
private fun Element.getImageAttr(): String? {
return when {
this.hasAttr("data-src") -> this.attr("abs:data-src")
this.hasAttr("data-lazy-src") -> this.attr("abs:data-lazy-src")
this.hasAttr("srcset") -> this.attr("abs:srcset").substringBefore(" ")
else -> this.attr("abs:src")
}
}
private data class EpResponse(
@JsonProperty("posts") val posts: String?,
@JsonProperty("max_page") val max_page: Int?,
@ -223,21 +240,3 @@ class NontonAnimeIDProvider : MainAPI() {
)
}
class KotakAnimeid2 : Hxfile() {
override val name = "KotakAnimeid2"
override val mainUrl = "https://embed2.kotakanimeid.com"
override val requiresReferer = true
}
class KotakAnimeidCom : Hxfile() {
override val name = "KotakAnimeid"
override val mainUrl = "https://nontonanimeid.com"
override val requiresReferer = true
}
class EmbedKotakAnimeid : Hxfile() {
override val name = "EmbedKotakAnimeid"
override val mainUrl = "https://embed2.kotakanimeid.com"
override val requiresReferer = true
}

View File

@ -10,8 +10,10 @@ class NontonAnimeIDProviderPlugin: Plugin() {
override fun load(context: Context) {
// All providers should be added in this manner. Please don't edit the providers list directly.
registerMainAPI(NontonAnimeIDProvider())
registerExtractorAPI(KotakAnimeid2())
registerExtractorAPI(KotakAnimeidCom())
registerExtractorAPI(Nontonanimeid())
registerExtractorAPI(EmbedKotakAnimeid())
registerExtractorAPI(KotakAnimeidCom())
registerExtractorAPI(Gdplayer())
registerExtractorAPI(Kotaksb())
}
}

View File

@ -1,5 +1,5 @@
// use an integer for version numbers
version = 24
version = 30
cloudstream {

View File

@ -27,10 +27,7 @@ open class Qiwi : ExtractorApi() {
"$mainUrl/",
getIndexQuality(title),
headers = mapOf(
"Accept" to "video/webm,video/ogg,video/*;q=0.9,application/ogg;q=0.7,audio/*;q=0.6,*/*;q=0.5",
"Range" to "bytes=0-",
"Sec-Fetch-Dest" to "video",
"Sec-Fetch-Mode" to "no-cors",
)
)
)

View File

@ -7,7 +7,7 @@ import com.lagradost.cloudstream3.utils.*
import org.jsoup.nodes.Element
class OploverzProvider : MainAPI() {
override var mainUrl = "https://oploverz.red"
override var mainUrl = "https://oploverz.gold"
override var name = "Oploverz"
override val hasMainPage = true
override var lang = "id"
@ -20,7 +20,6 @@ class OploverzProvider : MainAPI() {
)
companion object {
const val acefile = "https://acefile.co"
fun getType(t: String): TvType {
return if (t.contains("OVA", true) || t.contains("Special")) TvType.OVA
else if (t.contains("Movie", true)) TvType.AnimeMovie
@ -170,14 +169,14 @@ class OploverzProvider : MainAPI() {
headers = mapOf("X-Requested-With" to "XMLHttpRequest")
).document.select("iframe").attr("src")
loadExtractor(fixedIframe(iframe), "$mainUrl/", subtitleCallback, callback)
loadExtractor(fixUrl(iframe), "$mainUrl/", subtitleCallback, callback)
}
},
{
document.select("div#download tr").map { el ->
document.select("div#download tr").apmap { el ->
el.select("a").apmap {
loadFixedExtractor(fixedIframe(it.attr("href")), el.select("strong").text(), "$mainUrl/", subtitleCallback, callback)
loadFixedExtractor(fixUrl(it.attr("href")), el.select("strong").text(), "$mainUrl/", subtitleCallback, callback)
}
}
}
@ -217,12 +216,4 @@ class OploverzProvider : MainAPI() {
}
}
private fun fixedIframe(url: String): String {
val id = Regex("""(?:/f/|/file/)(\w+)""").find(url)?.groupValues?.getOrNull(1)
return when {
url.startsWith(acefile) -> "${acefile}/player/$id"
else -> fixUrl(url)
}
}
}

View File

@ -1,5 +1,5 @@
// use an integer for version numbers
version = 6
version = 7
cloudstream {
@ -24,5 +24,5 @@ cloudstream {
"Movie",
)
iconUrl = "https://www.google.com/s2/favicons?domain=phimmoichilla.net&sz=%size%"
iconUrl = "https://www.google.com/s2/favicons?domain=phimmoichillk.net&sz=%size%"
}

View File

@ -9,7 +9,7 @@ import java.net.URI
import java.net.URLDecoder
class PhimmoichillProvider : MainAPI() {
override var mainUrl = "https://phimmoichillh.net"
override var mainUrl = "https://phimmoichillk.net"
private var directUrl = mainUrl
override var name = "Phimmoichill"
override val hasMainPage = true

View File

@ -0,0 +1,27 @@
// use an integer for version numbers
version = 3
cloudstream {
language = "id"
// 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(
"AsianDrama",
"TvSeries",
"Movie",
)
iconUrl = "https://raveeflix.my.id/favicon.ico"
}

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.hexated"/>

View File

@ -0,0 +1,227 @@
package com.hexated
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.LoadResponse.Companion.addActors
import com.lagradost.cloudstream3.utils.*
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
import com.lagradost.cloudstream3.utils.AppUtils.toJson
import org.jsoup.nodes.Element
import org.jsoup.select.Elements
class Raveeflix : MainAPI() {
override var mainUrl = "https://raveeflix.my.id"
override var name = "Raveeflix"
override val hasMainPage = true
override var lang = "id"
override val supportedTypes =
setOf(
TvType.Movie,
TvType.TvSeries,
TvType.AsianDrama,
)
override val mainPage =
mainPageOf(
"categories/trending" to "Trending",
"movies" to "Movies",
"tv" to "Tv-Shows",
"drakor" to "Drakor",
"categories/anime" to "Anime",
)
override suspend fun getMainPage(
page: Int,
request: MainPageRequest,
): HomePageResponse {
val pages = if (page > 1) "page/$page/" else ""
val document = app.get("$mainUrl/${request.data}/$pages").document
val home = document.select("section.w-full a.min-w-full").mapNotNull { it.toSearchResult() }
return newHomePageResponse(
HomePageList(
request.name, home, true
)
)
}
private fun Element.toSearchResult(): SearchResponse? {
val title = this.selectFirst("div.text-xl")?.text() ?: return null
val href = fixUrl(this.attr("href"))
val posterUrl = this.selectFirst("div.thumbnail_card")?.attr("style")?.getPoster()
return newMovieSearchResponse(title, Media(href, posterUrl).toJson(), TvType.Movie, false) {
this.posterUrl = posterUrl
}
}
override suspend fun search(query: String): List<SearchResponse>? {
val res =
app.get("$mainUrl/index.json").text.let { AppUtils.tryParseJson<ArrayList<Index>>(it) }
return res?.filter {
it.title?.contains(
query,
true
) == true && !it.section.equals("Categories", true) && !it.section.equals(
"Tags",
true
) && it.permalink?.contains("/episode") == false
}?.mapNotNull {
newMovieSearchResponse(
it.title ?: return@mapNotNull null,
Media(
fixUrl(
it.permalink?.substringBefore("episode")?.substringBefore("season")
?: return@mapNotNull null
)
).toJson(),
TvType.Movie,
false,
)
}
}
override suspend fun load(url: String): LoadResponse {
val media = parseJson<Media>(url)
val document = app.get(media.url).document
val title = document.selectFirst("h1.text-4xl")?.text() ?: "No Title"
val poster = media.poster ?: document.selectFirst("div.thumbnail_card, div.w-full.thumbnail_card_related")
?.attr("style")?.getPoster()
val type =
if (document.select("mux-player").isNullOrEmpty()) TvType.TvSeries else TvType.Movie
val tags =
if (type == TvType.TvSeries) {
document.selectFirst("div.movie-details > p:nth-child(1)")
?.ownText()?.split(",")
?.map { it.trim() }
} else {
document.select("span.mr-2")
.map { it.text() }.distinct()
}
val year =
document.selectFirst("div.movie-details > p:nth-child(2), div.max-w-prose.mb-20 > ul > li:nth-child(2) span")
?.ownText()?.substringAfter(",")?.toIntOrNull()
val description =
document.selectFirst("div.lead.text-neutral-500, span#storyline")
?.text()?.trim()
val rating =
document.selectFirst("span#rating")?.text()
?.toRatingInt()
val actors =
document.select("span#cast").text().split(", ")
.map { it.trim() }
val recommendations =
document.select("section.w-full a.min-w-full").mapNotNull { it.toSearchResult() }
return if (type == TvType.TvSeries) {
val sectionSelector = "div.relative > section.w-full a.min-w-full"
val section = document.select(sectionSelector)
val hasMultipleSeason = section.any { it.attr("href").contains("/season-") }
val episodes = if (hasMultipleSeason) {
section.apmap { ss ->
fetchEpisodesFromPages(
ss.attr("href"),
5,
sectionSelector,
true,
ss.selectFirst("div.text-xl")?.text()?.filter { it.isDigit() }
?.toIntOrNull()
)
}.toMutableList().flatten()
} else {
fetchEpisodesFromPages(media.url, 5, sectionSelector, false)
}
newTvSeriesLoadResponse(title, url, TvType.TvSeries, episodes.reversed()) {
this.posterUrl = poster
this.year = year
this.seasonNames
this.plot = description
this.tags = tags
this.rating = rating
addActors(actors)
this.recommendations = recommendations
}
} else {
newMovieLoadResponse(title, url, TvType.Movie, media.url) {
this.posterUrl = poster
this.year = year
this.plot = description
this.tags = tags
this.rating = rating
addActors(actors)
this.recommendations = recommendations
}
}
}
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit,
): Boolean {
val video = app.get(data).document.select("mux-player").attr("src")
callback.invoke(
ExtractorLink(
name,
name,
video,
"",
Qualities.Unknown.value,
)
)
return true
}
private suspend fun fetchEpisodesFromPages(
baseUrl: String,
maxPages: Int,
sectionSelector: String,
hasMultipleSeasons: Boolean,
season: Int? = null
): MutableList<Episode> {
val epsData = mutableListOf<Episode>()
for (index in 1..maxPages) {
val pageUrl = if (index == 1) baseUrl else "${baseUrl.removeSuffix("/")}/page/$index/"
val episodeVo = app.get(fixUrl(pageUrl)).document.select(sectionSelector)
.getEpisodes(if (hasMultipleSeasons) season else null)
if (episodeVo.isEmpty()) break
epsData.addAll(episodeVo)
}
return epsData
}
private fun Elements.getEpisodes(season: Int? = 1): List<Episode> {
return this.mapNotNull { eps ->
val name = eps.selectFirst("div.text-xl")?.text() ?: return@mapNotNull null
val href = fixUrl(eps.attr("href"))
val posterUrl =
eps.selectFirst("div.thumbnail_card")?.attr("style")?.getPoster()
Episode(
href,
name,
posterUrl = posterUrl,
season = season
)
}
}
private fun String.getPoster(): String? {
return fixUrlNull(
this.substringAfter("(")
.substringBefore(")"),
)
}
data class Media(val url: String, val poster: String? = null)
data class Index(
@JsonProperty("title") val title: String? = null,
@JsonProperty("permalink") val permalink: String? = null,
@JsonProperty("section") val section: String? = null,
)
}

View File

@ -0,0 +1,14 @@
package com.hexated
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
import com.lagradost.cloudstream3.plugins.Plugin
import android.content.Context
@CloudstreamPlugin
class RaveeflixPlugin: Plugin() {
override fun load(context: Context) {
// All providers should be added in this manner. Please don't edit the providers list directly.
registerMainAPI(Raveeflix())
}
}

View File

@ -1,5 +1,5 @@
// use an integer for version numbers
version = 8
version = 9
cloudstream {

View File

@ -3,7 +3,9 @@ package com.hexated
import com.lagradost.cloudstream3.TvType
class Cgvindo : RebahinProvider() {
override var mainUrl = "http://51.68.185.138/"
override var name = "Cgvindo"
}

View File

@ -1,5 +1,5 @@
// use an integer for version numbers
version = 17
version = 22
cloudstream {

View File

@ -10,7 +10,7 @@ import com.lagradost.cloudstream3.utils.loadExtractor
import org.jsoup.nodes.Element
class Samehadaku : MainAPI() {
override var mainUrl = "https://samehadaku.digital/"
override var mainUrl = "https://samehadaku.show"
override var name = "Samehadaku"
override val hasMainPage = true
override var lang = "id"
@ -20,10 +20,7 @@ class Samehadaku : MainAPI() {
TvType.AnimeMovie,
TvType.OVA
)
companion object {
const val acefile = "https://acefile.co"
fun getType(t: String): TvType {
return if (t.contains("OVA", true) || t.contains("Special", true)) TvType.OVA
else if (t.contains("Movie", true)) TvType.AnimeMovie
@ -63,9 +60,9 @@ class Samehadaku : MainAPI() {
if (request.name == "Episode Terbaru") {
val home = app.get(request.data + page).document.selectFirst("div.post-show")?.select("ul li")
?.mapNotNull {
it.toSearchResult()
} ?: throw ErrorLoadingException("No Media Found")
?.mapNotNull {
it.toSearchResult()
} ?: throw ErrorLoadingException("No Media Found")
items.add(HomePageList(request.name, home, true))
}
@ -158,37 +155,11 @@ class Samehadaku : MainAPI() {
val document = app.get(data).document
argamap(
{
document.select("div#server ul li div").apmap {
val dataPost = it.attr("data-post")
val dataNume = it.attr("data-nume")
val dataType = it.attr("data-type")
val iframe = app.post(
url = "$mainUrl/wp-admin/admin-ajax.php",
data = mapOf(
"action" to "player_ajax",
"post" to dataPost,
"nume" to dataNume,
"type" to dataType
),
referer = data,
headers = mapOf("X-Requested-With" to "XMLHttpRequest"),
).document.select("iframe").attr("src")
loadFixedExtractor(fixedIframe(iframe), it.text(), "$mainUrl/", subtitleCallback, callback)
}
},
{
document.select("div#downloadb li").map { el ->
el.select("a").apmap {
loadFixedExtractor(fixedIframe(it.attr("href")), el.select("strong").text(), "$mainUrl/", subtitleCallback, callback)
}
}
document.select("div#downloadb li").map { el ->
el.select("a").apmap {
loadFixedExtractor(fixUrl(it.attr("href")), el.select("strong").text(), "$mainUrl/", subtitleCallback, callback)
}
)
}
return true
}
@ -217,21 +188,14 @@ class Samehadaku : MainAPI() {
}
private fun String.fixQuality() : Int {
return when(this) {
"MP4HD" -> Qualities.P720.value
return when(this.uppercase()) {
"4K" -> Qualities.P2160.value
"FULLHD" -> Qualities.P1080.value
"MP4HD" -> Qualities.P720.value
else -> this.filter { it.isDigit() }.toIntOrNull() ?: Qualities.Unknown.value
}
}
private fun fixedIframe(url: String): String {
val id = Regex("""(?:/f/|/file/)(\w+)""").find(url)?.groupValues?.getOrNull(1)
return when {
url.startsWith(acefile) -> "${acefile}/player/$id"
else -> fixUrl(url)
}
}
private fun String.removeBloat(): String {
return this.replace(Regex("(Nonton)|(Anime)|(Subtitle\\sIndonesia)"), "").trim()
}

View File

@ -1,7 +1,7 @@
import org.jetbrains.kotlin.konan.properties.Properties
// use an integer for version numbers
version = 206
version = 225
android {
defaultConfig {
@ -9,7 +9,7 @@ android {
properties.load(project.rootProject.file("local.properties").inputStream())
buildConfigField("String", "TMDB_API", "\"${properties.getProperty("TMDB_API")}\"")
buildConfigField("String", "OMOVIES_API", "\"${properties.getProperty("OMOVIES_API")}\"")
buildConfigField("String", "GHOSTX_API", "\"${properties.getProperty("GHOSTX_API")}\"")
buildConfigField("String", "CINEMATV_API", "\"${properties.getProperty("CINEMATV_API")}\"")
buildConfigField("String", "SFMOVIES_API", "\"${properties.getProperty("SFMOVIES_API")}\"")
buildConfigField("String", "ZSHOW_API", "\"${properties.getProperty("ZSHOW_API")}\"")
@ -42,5 +42,5 @@ cloudstream {
"Movie",
)
iconUrl = "https://raw.githubusercontent.com/hexated/cloudstream-extensions-hexated/master/SoraStream/Icon.png"
iconUrl = "https://cdn.discordapp.com/attachments/1109266606292488297/1193122096159674448/2-modified.png"
}

View File

@ -10,8 +10,8 @@ import com.lagradost.cloudstream3.SubtitleFile
import com.lagradost.cloudstream3.apmap
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.base64Decode
import com.lagradost.cloudstream3.extractors.Pixeldrain
import com.lagradost.cloudstream3.extractors.Vidplay
import com.lagradost.cloudstream3.extractors.Jeniusplay
import com.lagradost.cloudstream3.extractors.PixelDrain
import com.lagradost.cloudstream3.utils.*
import java.math.BigInteger
import java.security.MessageDigest
@ -124,8 +124,8 @@ open class Playm4u : ExtractorApi() {
return "$this\\s*=\\s*[\"'](\\S+)[\"'];".toRegex().find(data)?.groupValues?.get(1) ?: ""
}
private fun String.toLanguage() : String {
return if(this == "EN") "English" else this
private fun String.toLanguage(): String {
return if (this == "EN") "English" else this
}
data class Source(
@ -209,7 +209,10 @@ open class VCloud : ExtractorApi() {
)
).document.select("p.text-success ~ a").apmap {
val link = it.attr("href")
if (link.contains("workers.dev") || it.text().contains("[Server : 1]") || link.contains("/dl.php?")) {
if (link.contains("workers.dev") || it.text().contains("[Server : 1]") || link.contains(
"/dl.php?"
)
) {
callback.invoke(
ExtractorLink(
this.name,
@ -221,7 +224,7 @@ open class VCloud : ExtractorApi() {
)
)
} else {
val direct = if(link.contains("gofile.io")) app.get(link).url else link
val direct = if (link.contains("gofile.io")) app.get(link).url else link
loadExtractor(direct, referer, subtitleCallback, callback)
}
}
@ -247,19 +250,20 @@ open class Streamruby : ExtractorApi() {
callback: (ExtractorLink) -> Unit
) {
val id = "/e/(\\w+)".toRegex().find(url)?.groupValues?.get(1) ?: return
val response = app.post("$mainUrl/dl", data = mapOf(
"op" to "embed",
"file_code" to id,
"auto" to "1",
"referer" to "",
), referer = referer)
val response = app.post(
"$mainUrl/dl", data = mapOf(
"op" to "embed",
"file_code" to id,
"auto" to "1",
"referer" to "",
), referer = referer
)
val script = if (!getPacked(response.text).isNullOrEmpty()) {
getAndUnpack(response.text)
} else {
response.document.selectFirst("script:containsData(sources:)")?.data()
}
val m3u8 =
Regex("file:\\s*\"(.*?m3u8.*?)\"").find(script ?: return)?.groupValues?.getOrNull(1)
val m3u8 = Regex("file:\\s*\"(.*?m3u8.*?)\"").find(script ?: return)?.groupValues?.getOrNull(1)
M3u8Helper.generateM3u8(
name,
m3u8 ?: return,
@ -282,17 +286,25 @@ open class Uploadever : ExtractorApi() {
) {
var res = app.get(url, referer = referer).document
val formUrl = res.select("form").attr("action")
var formData = res.select("form input").associate { it.attr("name") to it.attr("value") }.filterKeys { it != "go" }
var formData = res.select("form input").associate { it.attr("name") to it.attr("value") }
.filterKeys { it != "go" }
.toMutableMap()
val formReq = app.post(formUrl, data = formData)
res = formReq.document
val captchaKey = res.select("script[src*=https://www.google.com/recaptcha/api.js?render=]").attr("src").substringAfter("render=")
val captchaKey =
res.select("script[src*=https://www.google.com/recaptcha/api.js?render=]").attr("src")
.substringAfter("render=")
val token = getCaptchaToken(url, captchaKey, referer = "$mainUrl/")
formData = res.select("form#down input").associate { it.attr("name") to it.attr("value") }.toMutableMap()
formData = res.select("form#down input").associate { it.attr("name") to it.attr("value") }
.toMutableMap()
formData["adblock_detected"] = "0"
formData["referer"] = url
res = app.post(formReq.url, data = formData + mapOf("g-recaptcha-response" to "$token"), cookies = formReq.cookies).document
res = app.post(
formReq.url,
data = formData + mapOf("g-recaptcha-response" to "$token"),
cookies = formReq.cookies
).document
val video = res.select("div.download-button a.btn.btn-dow.recaptchav2").attr("href")
callback.invoke(
@ -325,31 +337,130 @@ open class Netembed : ExtractorApi() {
val script = getAndUnpack(response.text)
val m3u8 = Regex("((https:|http:)//.*\\.m3u8)").find(script)?.groupValues?.getOrNull(1) ?: return
if(m3u8.startsWith("https://www.febbox.com")) {
callback.invoke(
ExtractorLink(
this.name,
this.name,
m3u8,
"$mainUrl/",
getQuality(m3u8),
INFER_TYPE
)
)
M3u8Helper.generateM3u8(this.name, m3u8, "$mainUrl/").forEach(callback)
}
}
open class Ridoo : ExtractorApi() {
override val name = "Ridoo"
override var mainUrl = "https://ridoo.net"
override val requiresReferer = true
open val defaulQuality = Qualities.P1080.value
override suspend fun getUrl(
url: String,
referer: String?,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
) {
val response = app.get(url, referer = referer)
val script = if (!getPacked(response.text).isNullOrEmpty()) {
getAndUnpack(response.text)
} else {
M3u8Helper.generateM3u8(
response.document.selectFirst("script:containsData(sources:)")?.data()
}
val m3u8 = Regex("file:\\s*\"(.*?m3u8.*?)\"").find(script ?: return)?.groupValues?.getOrNull(1)
val quality = "qualityLabels.*\"(\\d{3,4})[pP]\"".toRegex().find(script)?.groupValues?.get(1)
callback.invoke(
ExtractorLink(
this.name,
m3u8,
"$mainUrl/",
).forEach(callback)
this.name,
m3u8 ?: return,
mainUrl,
quality?.toIntOrNull() ?: defaulQuality,
INFER_TYPE
)
)
}
}
open class Gdmirrorbot : ExtractorApi() {
override val name = "Gdmirrorbot"
override val mainUrl = "https://gdmirrorbot.nl"
override val requiresReferer = true
override suspend fun getUrl(
url: String,
referer: String?,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
) {
app.get(url, referer = referer).document.select("ul#videoLinks li").apmap {
loadExtractor(it.attr("data-link"), "$mainUrl/", subtitleCallback, callback)
}
}
private suspend fun getQuality(url: String) : Int {
val res = app.get(url, referer = "$mainUrl/").text
val regex = "#quality:\\s*(\\S+)".toRegex().find(res)?.groupValues?.get(1)
return getQualityFromName(regex)
}
open class Streamvid : ExtractorApi() {
override val name = "Streamvid"
override val mainUrl = "https://streamvid.net"
override val requiresReferer = true
override suspend fun getUrl(
url: String,
referer: String?,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
) {
val response = app.get(url, referer = referer)
val script = if (!getPacked(response.text).isNullOrEmpty()) {
getAndUnpack(response.text)
} else {
response.document.selectFirst("script:containsData(sources:)")?.data()
}
val m3u8 =
Regex("src:\\s*\"(.*?m3u8.*?)\"").find(script ?: return)?.groupValues?.getOrNull(1)
M3u8Helper.generateM3u8(
name,
m3u8 ?: return,
mainUrl
).forEach(callback)
}
}
open class Embedrise : ExtractorApi() {
override val name = "Embedrise"
override val mainUrl = "https://embedrise.com"
override val requiresReferer = true
override suspend fun getUrl(
url: String,
referer: String?,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
) {
val res = app.get(url, referer = referer).document
val title = res.select("title").text()
val video = res.select("video#player source").attr("src")
callback.invoke(
ExtractorLink(
this.name,
this.name,
video,
"$mainUrl/",
getIndexQuality(title),
INFER_TYPE
)
)
}
}
class FilemoonNl : Ridoo() {
override val name = "FilemoonNl"
override var mainUrl = "https://filemoon.nl"
override val defaulQuality = Qualities.Unknown.value
}
class Alions : Ridoo() {
override val name = "Alions"
override var mainUrl = "https://alions.pro"
override val defaulQuality = Qualities.Unknown.value
}
class Streamwish : Filesim() {
@ -357,9 +468,9 @@ class Streamwish : Filesim() {
override var mainUrl = "https://streamwish.to"
}
class Wishfast : Filesim() {
override val name = "Wishfast"
override var mainUrl = "https://wishfast.top"
class UqloadsXyz : Filesim() {
override val name = "Uqloads"
override var mainUrl = "https://uqloads.xyz"
}
class FilelionsTo : Filesim() {
@ -367,7 +478,7 @@ class FilelionsTo : Filesim() {
override var mainUrl = "https://filelions.to"
}
class Pixeldra : Pixeldrain() {
class Pixeldra : PixelDrain() {
override val mainUrl = "https://pixeldra.in"
}
@ -386,7 +497,7 @@ class Animefever : Filesim() {
override var mainUrl = "https://animefever.fun"
}
class Multimovies : Filesim() {
class Multimovies : Ridoo() {
override val name = "Multimovies"
override var mainUrl = "https://multimovies.cloud"
}
@ -405,12 +516,13 @@ class Embedwish : Filesim() {
override val name = "Embedwish"
override var mainUrl = "https://embedwish.com"
}
class Vidplay2 : Vidplay() {
override val mainUrl = "https://vidplay.online"
}
class Flaswish : Filesim() {
class Flaswish : Ridoo() {
override val name = "Flaswish"
override var mainUrl = "https://flaswish.com"
override val defaulQuality = Qualities.Unknown.value
}
class Comedyshow : Jeniusplay() {
override val mainUrl = "https://comedyshow.to"
override val name = "Comedyshow"
}

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,17 @@
package com.hexated
import com.fasterxml.jackson.annotation.JsonProperty
import org.jsoup.Jsoup
import org.jsoup.nodes.Document
data class CrunchyrollAccessToken(
val accessToken: String? = null,
val tokenType: String? = null,
val bucket: String? = null,
val policy: String? = null,
val signature: String? = null,
val key_pair_id: String? = null,
)
data class FDMovieIFrame(
val link: String,
@ -9,32 +20,60 @@ data class FDMovieIFrame(
val type: String,
)
data class AniIds(
var id: Int? = null,
var idMal: Int? = null
)
data class AniIds(var id: Int? = null, var idMal: Int? = null)
data class TmdbDate(
val today: String,
val nextWeek: String,
)
data class AniwaveResponse(
val result: String
) {
fun asJsoup(): Document {
return Jsoup.parse(result)
}
}
data class AniwaveServer(
val result: Result
) {
data class Result(
val url: String
) {
fun decrypt(): String {
return AniwaveUtils.decodeVrf(url)
}
}
}
data class MoflixResponse(
@JsonProperty("title") val title: Episode? = null,
@JsonProperty("episode") val episode: Episode? = null,
) {
data class Episode(
@JsonProperty("id") val id: Int? = null,
@JsonProperty("videos") val videos: ArrayList<Videos>? = arrayListOf(),
) {
data class Videos(
@JsonProperty("name") val name: String? = null,
@JsonProperty("category") val category: String? = null,
@JsonProperty("src") val src: String? = null,
@JsonProperty("quality") val quality: String? = null,
)
}
}
data class AniMedia(
@JsonProperty("id") var id: Int? = null,
@JsonProperty("idMal") var idMal: Int? = null
)
data class AniPage(
@JsonProperty("media") var media: java.util.ArrayList<AniMedia> = arrayListOf()
)
data class AniPage(@JsonProperty("media") var media: java.util.ArrayList<AniMedia> = arrayListOf())
data class AniData(
@JsonProperty("Page") var Page: AniPage? = AniPage()
)
data class AniData(@JsonProperty("Page") var Page: AniPage? = AniPage())
data class AniSearch(
@JsonProperty("data") var data: AniData? = AniData()
)
data class AniSearch(@JsonProperty("data") var data: AniData? = AniData())
data class GpressSources(
@JsonProperty("src") val src: String,
@ -152,16 +191,6 @@ data class JikanResponse(
@JsonProperty("data") val data: JikanData? = null,
)
data class CinemaTvSubtitles(
@JsonProperty("language") val language: String? = null,
@JsonProperty("file") val file: Any? = null,
)
data class CinemaTvResponse(
@JsonProperty("streams") val streams: HashMap<String, String>? = null,
@JsonProperty("subtitles") val subtitles: ArrayList<CinemaTvSubtitles>? = arrayListOf(),
)
data class VidsrctoResult(
@JsonProperty("id") val id: String? = null,
@JsonProperty("title") val title: String? = null,
@ -188,25 +217,24 @@ data class AnilistExternalLinks(
@JsonProperty("type") var type: String? = null,
)
data class AnilistMedia(
@JsonProperty("externalLinks") var externalLinks: ArrayList<AnilistExternalLinks> = arrayListOf()
)
data class AnilistMedia(@JsonProperty("externalLinks") var externalLinks: ArrayList<AnilistExternalLinks> = arrayListOf())
data class AnilistData(
@JsonProperty("Media") var Media: AnilistMedia? = AnilistMedia()
)
data class AnilistData(@JsonProperty("Media") var Media: AnilistMedia? = AnilistMedia())
data class AnilistResponses(
@JsonProperty("data") var data: AnilistData? = AnilistData()
)
data class AnilistResponses(@JsonProperty("data") var data: AnilistData? = AnilistData())
data class CrunchyrollToken(
@JsonProperty("access_token") val accessToken: String? = null,
@JsonProperty("expires_in") val expiresIn: Int? = null,
@JsonProperty("token_type") val tokenType: String? = null,
@JsonProperty("scope") val scope: String? = null,
@JsonProperty("country") val country: String? = null
)
@JsonProperty("cms") val cms: Cms? = null,
) {
data class Cms(
@JsonProperty("bucket") var bucket: String? = null,
@JsonProperty("policy") var policy: String? = null,
@JsonProperty("signature") var signature: String? = null,
@JsonProperty("key_pair_id") var key_pair_id: String? = null,
)
}
data class CrunchyrollVersions(
@JsonProperty("audio_locale") val audio_locale: String? = null,
@ -221,25 +249,25 @@ data class CrunchyrollData(
@JsonProperty("episode_number") val episode_number: Int? = null,
@JsonProperty("versions") val versions: ArrayList<CrunchyrollVersions>? = null,
@JsonProperty("streams_link") val streams_link: String? = null,
@JsonProperty("adaptive_hls") val adaptive_hls: HashMap<String, HashMap<String, String>>? = hashMapOf(),
@JsonProperty("vo_adaptive_hls") val vo_adaptive_hls: HashMap<String, HashMap<String, String>>? = hashMapOf(),
)
data class CrunchyrollResponses(
@JsonProperty("data") val data: ArrayList<CrunchyrollData>? = arrayListOf(),
)
data class CrunchyrollMeta(
@JsonProperty("subtitles") val subtitles: HashMap<String, HashMap<String, String>>? = hashMapOf(),
)
data class CrunchyrollSourcesResponses(
@JsonProperty("data") val data: ArrayList<CrunchyrollData>? = arrayListOf(),
@JsonProperty("meta") val meta: CrunchyrollMeta? = null,
)
@JsonProperty("streams") val streams: Streams? = Streams(),
@JsonProperty("subtitles") val subtitles: HashMap<String, HashMap<String, String>>? = hashMapOf(),
) {
data class Streams(
@JsonProperty("adaptive_hls") val adaptive_hls: HashMap<String, HashMap<String, String>>? = hashMapOf(),
@JsonProperty("vo_adaptive_hls") val vo_adaptive_hls: HashMap<String, HashMap<String, String>>? = hashMapOf(),
)
}
data class MALSyncSites(
@JsonProperty("Zoro") val zoro: HashMap<String?, HashMap<String, String?>>? = hashMapOf(),
@JsonProperty("9anime") val nineAnime: HashMap<String?, HashMap<String, String?>>? = hashMapOf(),
)
data class MALSyncResponses(
@ -263,26 +291,26 @@ data class GokuServer(
@JsonProperty("data") val data: GokuData? = GokuData(),
)
data class NavyEpisodeFolder(
data class AllMovielandEpisodeFolder(
@JsonProperty("title") val title: String? = null,
@JsonProperty("id") val id: String? = null,
@JsonProperty("file") val file: String? = null,
)
data class NavySeasonFolder(
data class AllMovielandSeasonFolder(
@JsonProperty("episode") val episode: String? = null,
@JsonProperty("id") val id: String? = null,
@JsonProperty("folder") val folder: ArrayList<NavyEpisodeFolder>? = arrayListOf(),
@JsonProperty("folder") val folder: ArrayList<AllMovielandEpisodeFolder>? = arrayListOf(),
)
data class NavyServer(
data class AllMovielandServer(
@JsonProperty("title") val title: String? = null,
@JsonProperty("id") val id: String? = null,
@JsonProperty("file") val file: String? = null,
@JsonProperty("folder") val folder: ArrayList<NavySeasonFolder>? = arrayListOf(),
@JsonProperty("folder") val folder: ArrayList<AllMovielandSeasonFolder>? = arrayListOf(),
)
data class NavyPlaylist(
data class AllMovielandPlaylist(
@JsonProperty("file") val file: String? = null,
@JsonProperty("key") val key: String? = null,
@JsonProperty("href") val href: String? = null,
@ -334,26 +362,6 @@ data class EMovieTraks(
@JsonProperty("label") val label: String? = null,
)
data class BlackvidSubtitles(
@JsonProperty("language") val language: String? = null,
@JsonProperty("url") val url: String? = null,
)
data class BlackvidSource(
@JsonProperty("quality") var quality: String? = null,
@JsonProperty("url") var url: String? = null,
)
data class BlackvidSources(
@JsonProperty("label") var label: String? = null,
@JsonProperty("sources") var sources: ArrayList<BlackvidSource> = arrayListOf()
)
data class BlackvidResponses(
@JsonProperty("sources") var sources: ArrayList<BlackvidSources> = arrayListOf(),
@JsonProperty("subtitles") var subtitles: ArrayList<BlackvidSubtitles> = arrayListOf()
)
data class ShowflixResultsMovies(
@JsonProperty("movieName") val movieName: String? = null,
@JsonProperty("streamwish") val streamwish: String? = null,
@ -426,15 +434,6 @@ data class SmashySources(
@JsonProperty("subtitleUrls") var subtitleUrls: String? = null,
)
data class SmashyDSources(
@JsonProperty("sourceUrls") var sourceUrls: ArrayList<SmashyDSourcesUrls>? = arrayListOf(),
)
data class SmashyDSourcesUrls(
@JsonProperty("file") var file: String? = null,
@JsonProperty("title") var title: String? = null,
)
data class AoneroomResponse(
@JsonProperty("data") val data: Data? = null,
) {
@ -461,4 +460,24 @@ data class AoneroomResponse(
)
}
}
}
data class CinemaTvResponse(
@JsonProperty("streams") val streams: HashMap<String, String>? = null,
@JsonProperty("subtitles") val subtitles: ArrayList<Subtitles>? = arrayListOf(),
) {
data class Subtitles(
@JsonProperty("language") val language: String? = null,
@JsonProperty("file") val file: Any? = null,
)
}
data class NepuSearch(
@JsonProperty("data") val data: ArrayList<Data>? = arrayListOf(),
) {
data class Data(
@JsonProperty("url") val url: String? = null,
@JsonProperty("name") val name: String? = null,
@JsonProperty("type") val type: String? = null,
)
}

View File

@ -2,11 +2,9 @@ package com.hexated
import com.fasterxml.jackson.annotation.JsonProperty
import com.hexated.SoraExtractor.invoke2embed
import com.hexated.SoraExtractor.invokeAllMovieland
import com.hexated.SoraExtractor.invokeAnimes
import com.hexated.SoraExtractor.invokeAoneroom
import com.hexated.SoraExtractor.invokeBlackvid
import com.hexated.SoraExtractor.invokeBollyMaza
import com.hexated.SoraExtractor.invokeDbgo
import com.hexated.SoraExtractor.invokeFilmxy
import com.hexated.SoraExtractor.invokeKimcartoon
import com.hexated.SoraExtractor.invokeVidSrc
@ -20,13 +18,10 @@ import com.hexated.SoraExtractor.invokeDramaday
import com.hexated.SoraExtractor.invokeDreamfilm
import com.hexated.SoraExtractor.invokeFDMovies
import com.hexated.SoraExtractor.invokeFlixon
import com.hexated.SoraExtractor.invokeGMovies
import com.hexated.SoraExtractor.invokeGoku
import com.hexated.SoraExtractor.invokeKisskh
import com.hexated.SoraExtractor.invokeLing
import com.hexated.SoraExtractor.invokeM4uhd
import com.hexated.SoraExtractor.invokeMoviezAdd
import com.hexated.SoraExtractor.invokeNavy
import com.hexated.SoraExtractor.invokeNinetv
import com.hexated.SoraExtractor.invokeNowTv
import com.hexated.SoraExtractor.invokeRStream
@ -35,21 +30,24 @@ import com.hexated.SoraExtractor.invokeSmashyStream
import com.hexated.SoraExtractor.invokeDumpStream
import com.hexated.SoraExtractor.invokeEmovies
import com.hexated.SoraExtractor.invokeHdmovies4u
import com.hexated.SoraExtractor.invokeMoment
import com.hexated.SoraExtractor.invokeMultimovies
import com.hexated.SoraExtractor.invokeNetmovies
import com.hexated.SoraExtractor.invokeSFMovies
import com.hexated.SoraExtractor.invokeShowflix
import com.hexated.SoraExtractor.invokeTvMovies
import com.hexated.SoraExtractor.invokeUhdmovies
import com.hexated.SoraExtractor.invokeVegamovies
import com.hexated.SoraExtractor.invokeVidsrcto
import com.hexated.SoraExtractor.invokeCinemaTv
import com.hexated.SoraExtractor.invokeOmovies
import com.hexated.SoraExtractor.invokeMoflix
import com.hexated.SoraExtractor.invokeGhostx
import com.hexated.SoraExtractor.invokeNepu
import com.hexated.SoraExtractor.invokeWatchCartoon
import com.hexated.SoraExtractor.invokeWatchsomuch
import com.hexated.SoraExtractor.invokeZoechip
import com.hexated.SoraExtractor.invokeZshow
import com.lagradost.cloudstream3.LoadResponse.Companion.addImdbId
import com.lagradost.cloudstream3.LoadResponse.Companion.addTMDbId
import com.lagradost.cloudstream3.network.CloudflareKiller
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
import com.lagradost.cloudstream3.utils.AppUtils.toJson
import com.lagradost.cloudstream3.utils.ExtractorLink
@ -67,6 +65,8 @@ open class SoraStream : TmdbProvider() {
TvType.Anime,
)
val wpRedisInterceptor by lazy { CloudflareKiller() }
/** AUTHOR : Hexated & Sora */
companion object {
/** TOOLS */
@ -81,16 +81,16 @@ open class SoraStream : TmdbProvider() {
/** ALL SOURCES */
const val twoEmbedAPI = "https://www.2embed.cc"
const val vidSrcAPI = "https://vidsrc.me"
const val dbgoAPI = "https://dbgo.fun"
const val dreamfilmAPI = "https://dreamfilmsw.net"
const val noverseAPI = "https://www.nollyverse.com"
const val filmxyAPI = "https://www.filmxy.vip"
const val kimcartoonAPI = "https://kimcartoon.li"
const val aniwatchAPI = "https://aniwatch.to"
const val aniwaveAPI = "https://aniwave.to"
const val crunchyrollAPI = "https://beta-api.crunchyroll.com"
const val kissKhAPI = "https://kisskh.co"
const val lingAPI = "https://ling-online.net"
const val m4uhdAPI = "https://ww2.m4ufree.com"
const val m4uhdAPI = "https://m4umv.org"
const val rStreamAPI = "https://remotestream.cc"
const val flixonAPI = "https://flixon.lol"
const val smashyStreamAPI = "https://embed.smashystream.com"
@ -100,31 +100,30 @@ open class SoraStream : TmdbProvider() {
const val nowTvAPI = "https://myfilestorage.xyz"
const val gokuAPI = "https://goku.sx"
const val zshowAPI = BuildConfig.ZSHOW_API
const val ridomoviesAPI = "https://ridomovies.pw"
const val navyAPI = "https://navy-issue-i-239.site"
const val ridomoviesAPI = "https://ridomovies.tv"
const val emoviesAPI = "https://emovies.si"
const val multimoviesAPI = "https://multimovies.top"
const val multimovies2API = "https://multimovies.click"
const val netmoviesAPI = "https://netmovies.to"
const val momentAPI = "https://izzillent-dickstonyx-i-262.site"
const val allmovielandAPI = "https://allmovieland.fun"
const val doomoviesAPI = "https://doomovies.net"
const val vidsrctoAPI = "https://vidsrc.to"
const val dramadayAPI = "https://dramaday.me"
const val animetoshoAPI = "https://animetosho.org"
const val watchflxAPI = "https://watchflx.tv"
const val blackvidAPI = "https://prod.api.blackvid.space"
const val showflixAPI = "https://showflix.space"
const val showflixAPI = "https://showflix.lol"
const val aoneroomAPI = "https://api3.aoneroom.com"
const val fdMoviesAPI = "https://freedrivemovie.lol"
const val uhdmoviesAPI = "https://uhdmovies.zip"
const val gMoviesAPI = "https://gdrivemovies.xyz"
const val hdmovies4uAPI = "https://hdmovies4u.band"
const val vegaMoviesAPI = "https://vegamovies.dad"
const val dotmoviesAPI = "https://dotmovies.bet"
const val mMoviesAPI = "https://multimovies.uno"
const val watchCartoonAPI = "https://www1.watchcartoononline.bz"
const val moflixAPI = "https://moflix-stream.xyz"
const val moviefictionAPI = "https://moviefiction.com"
const val zoechipAPI = "https://zoechip.org"
const val nepuAPI = "https://nepu.to"
const val fdMoviesAPI = "https://freedrivemovie.com"
const val uhdmoviesAPI = "https://uhdmovies.asia"
const val hdmovies4uAPI = "https://hdmovies4u.day"
const val vegaMoviesAPI = "https://vegamovies.ong"
const val dotmoviesAPI = "https://dotmovies.one"
const val tvMoviesAPI = "https://www.tvseriesnmovies.com"
const val moviezAddAPI = "https://ww3.moviezaddiction.click"
const val bollyMazaAPI = "https://ww3.bollymaza.click"
const val dahmerMoviesAPI = "https://odd-bird-1319.zwuhygoaqe.workers.dev"
fun getType(t: String?): TvType {
@ -176,16 +175,12 @@ open class SoraStream : TmdbProvider() {
return if (link.startsWith("/")) "https://image.tmdb.org/t/p/original/$link" else link
}
override suspend fun getMainPage(
page: Int,
request: MainPageRequest
): HomePageResponse {
override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse {
val adultQuery =
if (settingsForProvider.enableAdult) "" else "&without_keywords=190370|13059|226161|195669"
val type = if (request.data.contains("/movie")) "movie" else "tv"
val home = app.get("${request.data}$adultQuery&page=$page")
.parsedSafe<Results>()?.results
?.mapNotNull { media ->
.parsedSafe<Results>()?.results?.mapNotNull { media ->
media.toSearchResponse(type)
} ?: throw ErrorLoadingException("Invalid Json reponse")
return newHomePageResponse(request.name, home)
@ -204,11 +199,10 @@ open class SoraStream : TmdbProvider() {
override suspend fun quickSearch(query: String): List<SearchResponse>? = search(query)
override suspend fun search(query: String): List<SearchResponse>? {
return app.get(
"$tmdbAPI/search/multi?api_key=$apiKey&language=en-US&query=$query&page=1&include_adult=${settingsForProvider.enableAdult}"
).parsedSafe<Results>()?.results?.mapNotNull { media ->
media.toSearchResponse()
}
return app.get("$tmdbAPI/search/multi?api_key=$apiKey&language=en-US&query=$query&page=1&include_adult=${settingsForProvider.enableAdult}")
.parsedSafe<Results>()?.results?.mapNotNull { media ->
media.toSearchResponse()
}
}
override suspend fun load(url: String): LoadResponse? {
@ -243,17 +237,15 @@ open class SoraStream : TmdbProvider() {
val actors = res.credits?.cast?.mapNotNull { cast ->
ActorData(
Actor(
cast.name ?: cast.originalName ?: return@mapNotNull null,
getImageUrl(cast.profilePath)
),
roleString = cast.character
cast.name ?: cast.originalName
?: return@mapNotNull null, getImageUrl(cast.profilePath)
), roleString = cast.character
)
} ?: return null
val recommendations =
res.recommendations?.results?.mapNotNull { media -> media.toSearchResponse() }
val trailer = res.videos?.results?.map { "https://www.youtube.com/watch?v=${it.key}" }
?.randomOrNull()
return if (type == TvType.TvSeries) {
val lastSeason = res.last_episode_to_air?.season_number
@ -277,12 +269,13 @@ open class SoraStream : TmdbProvider() {
epsTitle = eps.name,
jpTitle = res.alternative_titles?.results?.find { it.iso_3166_1 == "JP" }?.title,
date = season.airDate,
airedDate = res.releaseDate ?: res.firstAirDate,
airedDate = res.releaseDate
?: res.firstAirDate,
isAsian = isAsian,
isBollywood = isBollywood,
isCartoon = isCartoon
).toJson(),
name = eps.name + if (isUpcoming(eps.airDate)) " - [UPCOMING]" else "",
name = eps.name + if (isUpcoming(eps.airDate)) " [UPCOMING]" else "",
season = eps.seasonNumber,
episode = eps.episodeNumber,
posterUrl = getImageUrl(eps.stillPath),
@ -303,7 +296,7 @@ open class SoraStream : TmdbProvider() {
this.backgroundPosterUrl = bgPoster
this.year = year
this.plot = res.overview
this.tags = if (isAnime) keywords else genres
this.tags = keywords.takeIf { !it.isNullOrEmpty() } ?: genres
this.rating = rating
this.showStatus = getStatus(res.status)
this.recommendations = recommendations
@ -328,7 +321,8 @@ open class SoraStream : TmdbProvider() {
orgTitle = orgTitle,
isAnime = isAnime,
jpTitle = res.alternative_titles?.results?.find { it.iso_3166_1 == "JP" }?.title,
airedDate = res.releaseDate ?: res.firstAirDate,
airedDate = res.releaseDate
?: res.firstAirDate,
isAsian = isAsian,
isBollywood = isBollywood
).toJson(),
@ -339,7 +333,7 @@ open class SoraStream : TmdbProvider() {
this.year = year
this.plot = res.overview
this.duration = res.runtime
this.tags = if (isAnime) keywords else genres
this.tags = keywords.takeIf { !it.isNullOrEmpty() } ?: genres
this.rating = rating
this.recommendations = recommendations
this.actors = actors
@ -361,15 +355,6 @@ open class SoraStream : TmdbProvider() {
val res = parseJson<LinkData>(data)
argamap(
{
if (!res.isAnime) invokeBlackvid(
res.id,
res.season,
res.episode,
subtitleCallback,
callback
)
},
{
invokeDumpStream(
res.title,
@ -392,19 +377,12 @@ open class SoraStream : TmdbProvider() {
)
},
{
invokeVidSrc(res.id, res.season, res.episode, subtitleCallback, callback)
},
{
invokeDbgo(res.imdbId, res.season, res.episode, subtitleCallback, callback)
invokeVidSrc(res.id, res.season, res.episode, callback)
},
{
if (!res.isAnime) invokeAoneroom(
res.title,
res.airedYear ?: res.year,
res.season,
res.episode,
subtitleCallback,
callback
res.title, res.airedYear
?: res.year, res.season, res.episode, subtitleCallback, callback
)
},
{
@ -449,6 +427,16 @@ open class SoraStream : TmdbProvider() {
callback
)
},
{
if (!res.isAnime && res.isCartoon) invokeWatchCartoon(
res.title,
res.year,
res.season,
res.episode,
subtitleCallback,
callback
)
},
{
if (!res.isAnime) invokeVidsrcto(
res.imdbId,
@ -470,7 +458,7 @@ open class SoraStream : TmdbProvider() {
)
},
{
if (!res.isAnime) invokeOmovies (
if (!res.isAnime) invokeGhostx(
res.title,
res.year,
res.season,
@ -480,12 +468,8 @@ open class SoraStream : TmdbProvider() {
},
{
if (!res.isAnime) invokeLing(
res.title,
res.airedYear ?: res.year,
res.season,
res.episode,
subtitleCallback,
callback
res.title, res.airedYear
?: res.year, res.season, res.episode, subtitleCallback, callback
)
},
{
@ -498,58 +482,17 @@ open class SoraStream : TmdbProvider() {
)
},
{
if (!res.isAnime) invokeGMovies(
res.title,
res.year,
res.season,
res.episode,
subtitleCallback,
callback
)
},
{
if (!res.isAnime) invokeFDMovies(
res.title,
res.season,
res.episode,
callback
)
if (!res.isAnime) invokeFDMovies(res.title, res.season, res.episode, callback)
},
{
if (!res.isAnime) invokeM4uhd(
res.title,
res.airedYear ?: res.year,
res.season,
res.episode,
subtitleCallback,
callback
res.title, res.airedYear
?: res.year, res.season, res.episode, subtitleCallback, callback
)
},
{
if (!res.isAnime) invokeTvMovies(res.title, res.season, res.episode, callback)
},
{
if (!res.isAnime) invokeMoviezAdd(
moviezAddAPI,
"MoviezAdd",
res.title,
res.year,
res.season,
res.episode,
callback
)
},
{
if (!res.isAnime && res.isBollywood) invokeBollyMaza(
bollyMazaAPI,
"BollyMaza",
res.title,
res.year,
res.season,
res.episode,
callback
)
},
{
if (!res.isAnime) invokeRStream(res.id, res.season, res.episode, callback)
},
@ -564,7 +507,7 @@ open class SoraStream : TmdbProvider() {
},
{
if (!res.isAnime) invokeSmashyStream(
res.imdbId,
res.id,
res.season,
res.episode,
subtitleCallback,
@ -589,19 +532,21 @@ open class SoraStream : TmdbProvider() {
)
},
{
invokeDahmerMovies(
res.title,
res.year,
res.season,
res.episode,
callback
)
invokeDahmerMovies(res.title, res.year, res.season, res.episode, callback)
},
{
invokeCinemaTv(
res.imdbId, res.title, res.airedYear
?: res.year, res.season, res.episode, subtitleCallback, callback
)
},
{
if (!res.isAnime) invokeNowTv(res.id, res.imdbId, res.season, res.episode, callback)
},
{
if (!res.isAnime) invokeRidomovies(
res.id,
res.imdbId,
res.title,
res.airedYear ?: res.year,
res.season,
res.episode,
subtitleCallback,
@ -609,20 +554,7 @@ open class SoraStream : TmdbProvider() {
)
},
{
if (!res.isAnime) invokeNowTv(res.id, res.imdbId, res.season, res.episode, callback)
},
{
if (!res.isAnime && res.season == null) invokeRidomovies(
res.id,
res.imdbId,
callback
)
},
{
invokeNavy(res.imdbId, res.season, res.episode, callback)
},
{
invokeMoment(res.imdbId, res.season, res.episode, callback)
if (!res.isAnime) invokeAllMovieland(res.imdbId, res.season, res.episode, callback)
},
{
if (!res.isAnime) invokeEmovies(
@ -657,10 +589,24 @@ open class SoraStream : TmdbProvider() {
)
},
{
if(res.isBollywood) invokeMultimovies(multimoviesAPI, res.title, res.season, res.episode, subtitleCallback, callback)
if (res.isBollywood) invokeMultimovies(
multimoviesAPI,
res.title,
res.season,
res.episode,
subtitleCallback,
callback
)
},
{
if(res.isBollywood) invokeMultimovies(multimovies2API, res.title, res.season, res.episode, subtitleCallback, callback)
if (res.isBollywood) invokeMultimovies(
multimovies2API,
res.title,
res.season,
res.episode,
subtitleCallback,
callback
)
},
{
invokeNetmovies(
@ -690,7 +636,13 @@ open class SoraStream : TmdbProvider() {
)
},
{
if (!res.isAnime) invoke2embed(res.imdbId, res.season, res.episode, subtitleCallback, callback)
if (!res.isAnime) invoke2embed(
res.imdbId,
res.season,
res.episode,
subtitleCallback,
callback
)
},
{
if (!res.isAnime) invokeHdmovies4u(
@ -723,8 +675,13 @@ open class SoraStream : TmdbProvider() {
)
},
{
if (!res.isAnime) invokeSFMovies(
res.id,
if (!res.isAnime) invokeMoflix(res.id, res.season, res.episode, callback)
},
{
if (!res.isAnime) invokeZoechip(res.title, res.year, res.season, res.episode, callback)
},
{
if (!res.isAnime) invokeNepu(
res.title,
res.airedYear ?: res.year,
res.season,

View File

@ -1,10 +1,9 @@
package com.hexated
import com.hexated.SoraExtractor.invoke2embed
import com.hexated.SoraExtractor.invokeAllMovieland
import com.hexated.SoraExtractor.invokeAnimes
import com.hexated.SoraExtractor.invokeAoneroom
import com.hexated.SoraExtractor.invokeBlackvid
import com.hexated.SoraExtractor.invokeDbgo
import com.hexated.SoraExtractor.invokeDoomovies
import com.hexated.SoraExtractor.invokeDramaday
import com.hexated.SoraExtractor.invokeDreamfilm
@ -15,7 +14,6 @@ import com.hexated.SoraExtractor.invokeKimcartoon
import com.hexated.SoraExtractor.invokeKisskh
import com.hexated.SoraExtractor.invokeLing
import com.hexated.SoraExtractor.invokeM4uhd
import com.hexated.SoraExtractor.invokeNavy
import com.hexated.SoraExtractor.invokeNinetv
import com.hexated.SoraExtractor.invokeNowTv
import com.hexated.SoraExtractor.invokeRStream
@ -23,16 +21,19 @@ import com.hexated.SoraExtractor.invokeRidomovies
import com.hexated.SoraExtractor.invokeSmashyStream
import com.hexated.SoraExtractor.invokeDumpStream
import com.hexated.SoraExtractor.invokeEmovies
import com.hexated.SoraExtractor.invokeMoment
import com.hexated.SoraExtractor.invokeMultimovies
import com.hexated.SoraExtractor.invokeNetmovies
import com.hexated.SoraExtractor.invokeSFMovies
import com.hexated.SoraExtractor.invokeShowflix
import com.hexated.SoraExtractor.invokeVidSrc
import com.hexated.SoraExtractor.invokeVidsrcto
import com.hexated.SoraExtractor.invokeCinemaTv
import com.hexated.SoraExtractor.invokeOmovies
import com.hexated.SoraExtractor.invokeMoflix
import com.hexated.SoraExtractor.invokeGhostx
import com.hexated.SoraExtractor.invokeMoviefiction
import com.hexated.SoraExtractor.invokeNepu
import com.hexated.SoraExtractor.invokeWatchCartoon
import com.hexated.SoraExtractor.invokeWatchsomuch
import com.hexated.SoraExtractor.invokeZoechip
import com.hexated.SoraExtractor.invokeZshow
import com.lagradost.cloudstream3.SubtitleFile
import com.lagradost.cloudstream3.argamap
@ -53,13 +54,7 @@ class SoraStreamLite : SoraStream() {
argamap(
{
if (!res.isAnime) invokeBlackvid(
res.id,
res.season,
res.episode,
subtitleCallback,
callback
)
if (!res.isAnime) invokeMoflix(res.id, res.season, res.episode, callback)
},
{
if (!res.isAnime) invokeWatchsomuch(
@ -100,10 +95,17 @@ class SoraStreamLite : SoraStream() {
)
},
{
invokeVidSrc(res.id, res.season, res.episode, subtitleCallback, callback)
invokeVidSrc(res.id, res.season, res.episode, callback)
},
{
invokeDbgo(res.imdbId, res.season, res.episode, subtitleCallback, callback)
if (!res.isAnime && res.isCartoon) invokeWatchCartoon(
res.title,
res.year,
res.season,
res.episode,
subtitleCallback,
callback
)
},
{
if (res.isAnime) invokeAnimes(
@ -136,7 +138,7 @@ class SoraStreamLite : SoraStream() {
)
},
{
if (!res.isAnime) invokeOmovies(
if (!res.isAnime) invokeGhostx(
res.title,
res.year,
res.season,
@ -155,7 +157,7 @@ class SoraStreamLite : SoraStream() {
},
{
if (!res.isAnime) invokeSmashyStream(
res.imdbId,
res.id,
res.season,
res.episode,
subtitleCallback,
@ -184,39 +186,32 @@ class SoraStreamLite : SoraStream() {
},
{
if (!res.isAnime) invokeLing(
res.title,
res.airedYear ?: res.year,
res.season,
res.episode,
subtitleCallback,
callback
res.title, res.airedYear
?: res.year, res.season, res.episode, subtitleCallback, callback
)
},
{
if(!res.isAnime) invokeM4uhd(
res.title,
res.airedYear ?: res.year,
res.season,
res.episode,
subtitleCallback,
callback
if (!res.isAnime) invokeM4uhd(
res.title, res.airedYear
?: res.year, res.season, res.episode, subtitleCallback, callback
)
},
{
if (!res.isAnime) invokeRStream(res.id, res.season, res.episode, callback)
},
{
if (!res.isAnime) invokeFlixon(res.id, res.imdbId, res.season, res.episode, callback)
if (!res.isAnime) invokeFlixon(
res.id,
res.imdbId,
res.season,
res.episode,
callback
)
},
{
invokeCinemaTv(
res.imdbId,
res.title,
res.airedYear ?: res.year,
res.season,
res.episode,
subtitleCallback,
callback
res.imdbId, res.title, res.airedYear
?: res.year, res.season, res.episode, subtitleCallback, callback
)
},
{
@ -224,21 +219,17 @@ class SoraStreamLite : SoraStream() {
},
{
if (!res.isAnime) invokeAoneroom(
res.title,
res.airedYear ?: res.year,
res.season,
res.episode,
subtitleCallback,
callback
res.title, res.airedYear
?: res.year, res.season, res.episode, subtitleCallback, callback
)
},
{
invokeNavy(res.imdbId, res.season, res.episode, callback)
},
{
if (!res.isAnime && res.season == null) invokeRidomovies(
if (!res.isAnime) invokeRidomovies(
res.id,
res.imdbId,
res.season,
res.episode,
subtitleCallback,
callback
)
},
@ -253,10 +244,24 @@ class SoraStreamLite : SoraStream() {
)
},
{
if(res.isBollywood) invokeMultimovies(multimoviesAPI, res.title, res.season, res.episode, subtitleCallback, callback)
if (res.isBollywood) invokeMultimovies(
multimoviesAPI,
res.title,
res.season,
res.episode,
subtitleCallback,
callback
)
},
{
if(res.isBollywood) invokeMultimovies(multimovies2API, res.title, res.season, res.episode, subtitleCallback, callback)
if (res.isBollywood) invokeMultimovies(
multimovies2API,
res.title,
res.season,
res.episode,
subtitleCallback,
callback
)
},
{
invokeNetmovies(
@ -269,7 +274,7 @@ class SoraStreamLite : SoraStream() {
)
},
{
invokeMoment(res.imdbId, res.season, res.episode, callback)
if (!res.isAnime) invokeAllMovieland(res.imdbId, res.season, res.episode, callback)
},
{
if (!res.isAnime && res.season == null) invokeDoomovies(
@ -279,7 +284,7 @@ class SoraStreamLite : SoraStream() {
)
},
{
if(res.isAsian) invokeDramaday(
if (res.isAsian) invokeDramaday(
res.title,
res.year,
res.season,
@ -289,7 +294,7 @@ class SoraStreamLite : SoraStream() {
)
},
{
if(!res.isAnime) invoke2embed(
if (!res.isAnime) invoke2embed(
res.imdbId,
res.season,
res.episode,
@ -318,8 +323,16 @@ class SoraStreamLite : SoraStream() {
)
},
{
if(!res.isAnime) invokeSFMovies(
res.id,
if (!res.isAnime) invokeZoechip(
res.title,
res.year,
res.season,
res.episode,
callback
)
},
{
if (!res.isAnime) invokeNepu(
res.title,
res.airedYear ?: res.year,
res.season,

View File

@ -25,10 +25,16 @@ class SoraStreamPlugin: Plugin() {
registerExtractorAPI(Streamwish())
registerExtractorAPI(FilelionsTo())
registerExtractorAPI(Embedwish())
registerExtractorAPI(Wishfast())
registerExtractorAPI(UqloadsXyz())
registerExtractorAPI(Uploadever())
registerExtractorAPI(Netembed())
registerExtractorAPI(Vidplay2())
registerExtractorAPI(Flaswish())
registerExtractorAPI(Comedyshow())
registerExtractorAPI(Ridoo())
registerExtractorAPI(Streamvid())
registerExtractorAPI(Embedrise())
registerExtractorAPI(Gdmirrorbot())
registerExtractorAPI(FilemoonNl())
registerExtractorAPI(Alions())
}
}

View File

@ -9,7 +9,6 @@ import com.hexated.SoraStream.Companion.gdbot
import com.hexated.SoraStream.Companion.hdmovies4uAPI
import com.hexated.SoraStream.Companion.malsyncAPI
import com.hexated.SoraStream.Companion.tvMoviesAPI
import com.hexated.SoraStream.Companion.watchflxAPI
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.APIHolder.getCaptchaToken
import com.lagradost.cloudstream3.APIHolder.unixTimeMS
@ -19,16 +18,12 @@ import com.lagradost.cloudstream3.utils.AppUtils.toJson
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
import com.lagradost.nicehttp.NiceResponse
import com.lagradost.nicehttp.RequestBodyTypes
import com.lagradost.nicehttp.Requests.Companion.await
import com.lagradost.nicehttp.requestCreator
import kotlinx.coroutines.delay
import okhttp3.FormBody
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response
import org.jsoup.nodes.Document
import java.math.BigInteger
import java.net.*
@ -38,7 +33,6 @@ import java.security.spec.PKCS8EncodedKeySpec
import java.security.spec.X509EncodedKeySpec
import java.text.SimpleDateFormat
import java.util.*
import java.util.concurrent.TimeUnit
import javax.crypto.Cipher
import javax.crypto.spec.GCMParameterSpec
import javax.crypto.spec.IvParameterSpec
@ -46,7 +40,6 @@ import javax.crypto.spec.SecretKeySpec
import kotlin.collections.ArrayList
import kotlin.math.min
var watchflxCookies: Map<String, String>? = null
var filmxyCookies: Map<String, String>? = null
var sfServer: String? = null
@ -330,10 +323,8 @@ suspend fun getDrivebotLink(url: String?): String? {
?.data()?.substringAfter("window.open('")?.substringBefore("')")
}
suspend fun extractOiya(url: String, quality: String): String? {
val doc = app.get(url).document
return doc.selectFirst("div.wp-block-button a:matches((?i)$quality)")?.attr("href")
?: doc.selectFirst("div.wp-block-button a")?.attr("href")
suspend fun extractOiya(url: String): String? {
return app.get(url).document.selectFirst("div.wp-block-button a")?.attr("href")
}
fun deobfstr(hash: String, index: String): String {
@ -404,6 +395,7 @@ suspend fun invokeSmashyFfix(
name: String,
url: String,
ref: String,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit,
) {
val json = app.get(url, referer = ref, headers = mapOf("X-Requested-With" to "XMLHttpRequest"))
@ -416,23 +408,41 @@ suspend fun invokeSmashyFfix(
).forEach(callback)
}
json?.subtitleUrls?.split(",")?.map { sub ->
val lang = "\\[(.*)]".toRegex().find(sub)?.groupValues?.get(1)
val subUrl = sub.replace("[$lang]", "").trim()
subtitleCallback.invoke(
SubtitleFile(
lang ?: return@map,
subUrl
)
)
}
}
suspend fun invokeSmashyD(
suspend fun invokeSmashySu(
name: String,
url: String,
ref: String,
callback: (ExtractorLink) -> Unit,
) {
val json = app.get(url, referer = ref, headers = mapOf("X-Requested-With" to "XMLHttpRequest"))
.parsedSafe<SmashyDSources>()
json?.sourceUrls?.apmap {
M3u8Helper.generateM3u8(
"Smashy [Player D ${it.title}]",
it.file ?: return@apmap,
""
).forEach(callback)
.parsedSafe<SmashySources>()
json?.sourceUrls?.firstOrNull()?.removeSuffix(",")?.split(",")?.forEach { links ->
val quality = Regex("\\[(\\S+)]").find(links)?.groupValues?.getOrNull(1) ?: return@forEach
val trimmedLink = links.removePrefix("[$quality]").trim()
callback.invoke(
ExtractorLink(
"Smashy [$name]",
"Smashy [$name]",
trimmedLink,
"",
getQualityFromName(quality),
INFER_TYPE
)
)
}
}
suspend fun getDumpIdAndType(title: String?, year: Int?, season: Int?): Pair<String?, Int?> {
@ -588,10 +598,11 @@ suspend fun bypassFdAds(url: String?): String? {
}
suspend fun bypassHrefli(url: String): String? {
fun Document.getFormUrl() : String {
fun Document.getFormUrl(): String {
return this.select("form#landing").attr("action")
}
fun Document.getFormData() : Map<String,String> {
fun Document.getFormData(): Map<String, String> {
return this.select("form#landing input").associate { it.attr("name") to it.attr("value") }
}
@ -605,7 +616,8 @@ suspend fun bypassHrefli(url: String): String? {
formData = res.getFormData()
res = app.post(formUrl, data = formData).document
val skToken = res.selectFirst("script:containsData(?go=)")?.data()?.substringAfter("?go=")?.substringBefore("\"") ?: return null
val skToken = res.selectFirst("script:containsData(?go=)")?.data()?.substringAfter("?go=")
?.substringBefore("\"") ?: return null
val driveUrl = app.get(
"$host?go=$skToken", cookies = mapOf(
skToken to "${formData["_wp_http2"]}"
@ -699,30 +711,13 @@ suspend fun fetchFilmxyCookies(url: String): Map<String, String> {
return cookieJar.plus(defaultCookies)
}
suspend fun getWatchflxCookies() =
watchflxCookies ?: fetchWatchflxCookies().also { watchflxCookies = it }
suspend fun fetchWatchflxCookies(): Map<String, String> {
session.get(watchflxAPI)
val cookies = session.baseClient.cookieJar.loadForRequest(watchflxAPI.toHttpUrl())
.associate { it.name to it.value }
val loginUrl = "$watchflxAPI/cookie-based-login"
session.post(
loginUrl, data = mapOf(
"continue_as_temp" to "true"
), cookies = cookies, headers = mapOf("X-Requested-With" to "XMLHttpRequest")
)
return session.baseClient.cookieJar.loadForRequest(loginUrl.toHttpUrl())
.associate { it.name to it.value }
}
fun Document.findTvMoviesIframe(): String? {
return this.selectFirst("script:containsData(var seconds)")?.data()?.substringAfter("href='")
?.substringBefore("'>")
}
//modified code from https://github.com/jmir1/aniyomi-extensions/blob/master/src/all/kamyroll/src/eu/kanade/tachiyomi/animeextension/all/kamyroll/AccessTokenInterceptor.kt
suspend fun getCrunchyrollToken(): Map<String, String> {
suspend fun getCrunchyrollToken(): CrunchyrollAccessToken {
val client = app.baseClient.newBuilder()
.proxy(Proxy(Proxy.Type.SOCKS, InetSocketAddress("cr-unblocker.us.to", 1080)))
.build()
@ -748,9 +743,18 @@ suspend fun getCrunchyrollToken(): Map<String, String> {
)
)
val response = tryParseJson<CrunchyrollToken>(client.newCall(request).execute().body.string())
return mapOf("Authorization" to "${response?.tokenType} ${response?.accessToken}")
val token = tryParseJson<CrunchyrollToken>(client.newCall(request).execute().body.string())
val headers = mapOf("Authorization" to "${token?.tokenType} ${token?.accessToken}")
val cms =
app.get("$crunchyrollAPI/index/v2", headers = headers).parsedSafe<CrunchyrollToken>()?.cms
return CrunchyrollAccessToken(
token?.accessToken,
token?.tokenType,
cms?.bucket,
cms?.policy,
cms?.signature,
cms?.key_pair_id,
)
}
suspend fun getCrunchyrollId(aniId: String?): String? {
@ -784,7 +788,7 @@ suspend fun getCrunchyrollId(aniId: String?): String? {
return (externalLinks?.find { it.site == "VRV" }
?: externalLinks?.find { it.site == "Crunchyroll" })?.url?.let {
Regex("series/(\\w+)/?").find(it)?.groupValues?.get(1)
app.get(it).url.substringAfter("/series/").substringBefore("/")
}
}
@ -797,6 +801,10 @@ suspend fun getCrunchyrollIdFromMalSync(aniId: String?): String? {
?: regex.find("$crunchyroll")?.groupValues?.getOrNull(1)
}
suspend fun String.haveDub(referer: String) : Boolean {
return app.get(this,referer=referer).text.contains("TYPE=AUDIO")
}
suspend fun convertTmdbToAnimeId(
title: String?,
date: String?,
@ -1031,7 +1039,7 @@ fun decodeIndexJson(json: String): String {
return base64Decode(slug.substring(0, slug.length - 20))
}
fun String.decodePrimewireXor(key: String): String {
fun String.xorDecrypt(key: String): String {
val sb = StringBuilder()
var i = 0
while (i < this.length) {
@ -1057,9 +1065,9 @@ fun vidsrctoDecrypt(text: String): String {
}
fun String?.createSlug(): String? {
return this?.replace(Regex("[^\\w\\s-]"), "")
?.replace(" ", "-")
?.replace(Regex("( )|( -)|(- )|(--)"), "-")
return this?.filter { it.isWhitespace() || it.isLetterOrDigit() }
?.trim()
?.replace("\\s+".toRegex(), "-")
?.lowercase()
}
@ -1145,14 +1153,6 @@ fun getVipLanguage(str: String): String {
}
}
fun getDbgoLanguage(str: String): String {
return when (str) {
"Русский" -> "Russian"
"Українська" -> "Ukrainian"
else -> str
}
}
fun fixCrunchyrollLang(language: String?): String? {
return SubtitleHelper.fromTwoLettersToLanguage(language ?: return null)
?: SubtitleHelper.fromTwoLettersToLanguage(language.substringBefore("-"))
@ -1209,37 +1209,6 @@ fun base64DecodeAPI(api: String): String {
return api.chunked(4).map { base64Decode(it) }.reversed().joinToString("")
}
fun decryptStreamUrl(data: String): String {
fun getTrash(arr: List<String>, item: Int): List<String> {
val trash = ArrayList<List<String>>()
for (i in 1..item) {
trash.add(arr)
}
return trash.reduce { acc, list ->
val temp = ArrayList<String>()
acc.forEach { ac ->
list.forEach { li ->
temp.add(ac.plus(li))
}
}
return@reduce temp
}
}
val trashList = listOf("@", "#", "!", "^", "$")
val trashSet = getTrash(trashList, 2) + getTrash(trashList, 3)
var trashString = data.replace("#2", "").split("//_//").joinToString("")
trashSet.forEach {
val temp = base64Encode(it.toByteArray())
trashString = trashString.replace(temp, "")
}
return base64Decode(trashString)
}
fun fixUrl(url: String, domain: String): String {
if (url.startsWith("http")) {
return url
@ -1283,23 +1252,53 @@ private enum class Symbol(val decimalValue: Int) {
}
}
suspend fun request(
url: String,
allowRedirects: Boolean = true,
timeout: Long = 60L
): Response {
val client = OkHttpClient().newBuilder()
.connectTimeout(timeout, TimeUnit.SECONDS)
.readTimeout(timeout, TimeUnit.SECONDS)
.writeTimeout(timeout, TimeUnit.SECONDS)
.followRedirects(allowRedirects)
.followSslRedirects(allowRedirects)
.build()
// steal from https://github.com/aniyomiorg/aniyomi-extensions/blob/master/src/en/aniwave/src/eu/kanade/tachiyomi/animeextension/en/nineanime/AniwaveUtils.kt
// credits to @samfundev
object AniwaveUtils {
val request: Request = Request.Builder()
.url(url)
.build()
return client.newCall(request).await()
fun encodeVrf(input: String): String {
val rc4Key = SecretKeySpec("ysJhV6U27FVIjjuk".toByteArray(), "RC4")
val cipher = Cipher.getInstance("RC4")
cipher.init(Cipher.DECRYPT_MODE, rc4Key, cipher.parameters)
var vrf = cipher.doFinal(input.toByteArray())
vrf = Base64.encode(vrf, Base64.URL_SAFE or Base64.NO_WRAP)
vrf = Base64.encode(vrf, Base64.DEFAULT or Base64.NO_WRAP)
vrf = vrfShift(vrf)
vrf = Base64.encode(vrf, Base64.DEFAULT)
vrf = rot13(vrf)
val stringVrf = vrf.toString(Charsets.UTF_8)
return encode(stringVrf)
}
fun decodeVrf(input: String): String {
var vrf = input.toByteArray()
vrf = Base64.decode(vrf, Base64.URL_SAFE)
val rc4Key = SecretKeySpec("hlPeNwkncH0fq9so".toByteArray(), "RC4")
val cipher = Cipher.getInstance("RC4")
cipher.init(Cipher.DECRYPT_MODE, rc4Key, cipher.parameters)
vrf = cipher.doFinal(vrf)
return decode(vrf.toString(Charsets.UTF_8))
}
private fun rot13(vrf: ByteArray): ByteArray {
for (i in vrf.indices) {
val byte = vrf[i]
if (byte in 'A'.code..'Z'.code) {
vrf[i] = ((byte - 'A'.code + 13) % 26 + 'A'.code).toByte()
} else if (byte in 'a'.code..'z'.code) {
vrf[i] = ((byte - 'a'.code + 13) % 26 + 'a'.code).toByte()
}
}
return vrf
}
private fun vrfShift(vrf: ByteArray): ByteArray {
for (i in vrf.indices) {
val shift = arrayOf(-3, 3, -4, 2, -2, 5, 4, 5)[i % 8]
vrf[i] = vrf[i].plus(shift).toByte()
}
return vrf
}
}
object DumpUtils {

View File

@ -1,6 +1,16 @@
// use an integer for version numbers
version = 12
import org.jetbrains.kotlin.konan.properties.Properties
// use an integer for version numbers
version = 13
android {
defaultConfig {
val properties = Properties()
properties.load(project.rootProject.file("local.properties").inputStream())
buildConfigField("String", "TMDB_API", "\"${properties.getProperty("TMDB_API")}\"")
}
}
cloudstream {
language = "en"

View File

@ -10,24 +10,23 @@ import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
import com.lagradost.cloudstream3.utils.*
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
import com.lagradost.cloudstream3.utils.AppUtils.toJson
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
import java.net.URI
private const val TRACKER_LIST_URL =
"https://raw.githubusercontent.com/ngosang/trackerslist/master/trackers_best.txt"
class StremioC : MainAPI() {
override var mainUrl = "https://stremio.github.io/stremio-static-addon-example"
override var name = "StremioC"
override val supportedTypes = setOf(TvType.Others)
override val hasMainPage = true
private val cinemataUrl = "https://v3-cinemeta.strem.io"
override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse? {
companion object {
private const val cinemataUrl = "https://v3-cinemeta.strem.io"
private const val TRACKER_LIST_URL = "https://raw.githubusercontent.com/ngosang/trackerslist/master/trackers_best.txt"
}
override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse {
mainUrl = mainUrl.fixSourceUrl()
val res = tryParseJson<Manifest>(request("${mainUrl}/manifest.json").body.string()) ?: return null
val res = app.get("${mainUrl}/manifest.json").parsedSafe<Manifest>()
val lists = mutableListOf<HomePageList>()
res.catalogs.apmap { catalog ->
res?.catalogs?.apmap { catalog ->
catalog.toHomePageList(this).let {
if (it.list.isNotEmpty()) lists.add(it)
}
@ -38,11 +37,11 @@ class StremioC : MainAPI() {
)
}
override suspend fun search(query: String): List<SearchResponse>? {
override suspend fun search(query: String): List<SearchResponse> {
mainUrl = mainUrl.fixSourceUrl()
val res = tryParseJson<Manifest>(request("${mainUrl}/manifest.json").body.string()) ?: return null
val res = app.get("${mainUrl}/manifest.json").parsedSafe<Manifest>()
val list = mutableListOf<SearchResponse>()
res.catalogs.apmap { catalog ->
res?.catalogs?.apmap { catalog ->
list.addAll(catalog.search(query, this))
}
return list.distinct()
@ -64,10 +63,13 @@ class StremioC : MainAPI() {
callback: (ExtractorLink) -> Unit
): Boolean {
val loadData = parseJson<LoadData>(data)
val request = request("${mainUrl}/stream/${loadData.type}/${loadData.id}.json")
if (request.code.isSuccessful()) {
val res = tryParseJson<StreamsResponse>(request.body.string()) ?: return false
res.streams.forEach { stream ->
val request = app.get(
"${mainUrl}/stream/${loadData.type}/${loadData.id}.json",
timeout = 120L
)
if (request.isSuccessful) {
val res = request.parsedSafe<StreamsResponse>()
res?.streams?.forEach { stream ->
stream.runCallback(subtitleCallback, callback)
}
} else {
@ -103,15 +105,14 @@ class StremioC : MainAPI() {
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
) {
val sites =
AcraApplication.getKey<Array<CustomSite>>(USER_PROVIDER_API)?.toMutableList()
?: mutableListOf()
val sites = AcraApplication.getKey<Array<CustomSite>>(USER_PROVIDER_API)?.toMutableList()
?: mutableListOf()
sites.filter { it.parentJavaClass == "StremioX" }.apmap { site ->
val request = request("${site.url.fixSourceUrl()}/stream/${type}/${id}.json").body.string()
val res =
tryParseJson<StreamsResponse>(request)
?: return@apmap
res.streams.forEach { stream ->
val res = app.get(
"${site.url.fixSourceUrl()}/stream/${type}/${id}.json",
timeout = 120L
).parsedSafe<StreamsResponse>()
res?.streams?.forEach { stream ->
stream.runCallback(subtitleCallback, callback)
}
}
@ -151,11 +152,11 @@ class StremioC : MainAPI() {
suspend fun search(query: String, provider: StremioC): List<SearchResponse> {
val entries = mutableListOf<SearchResponse>()
types.forEach { type ->
val json = request("${provider.mainUrl}/catalog/${type}/${id}/search=${query}.json").body.string()
val res =
tryParseJson<CatalogResponse>(json)
?: return@forEach
res.metas?.forEach { entry ->
val res = app.get(
"${provider.mainUrl}/catalog/${type}/${id}/search=${query}.json",
timeout = 120L
).parsedSafe<CatalogResponse>()
res?.metas?.forEach { entry ->
entries.add(entry.toSearchResponse(provider))
}
}
@ -165,11 +166,11 @@ class StremioC : MainAPI() {
suspend fun toHomePageList(provider: StremioC): HomePageList {
val entries = mutableListOf<SearchResponse>()
types.forEach { type ->
val json = request("${provider.mainUrl}/catalog/${type}/${id}.json").body.string()
val res =
tryParseJson<CatalogResponse>(json)
?: return@forEach
res.metas?.forEach { entry ->
val res = app.get(
"${provider.mainUrl}/catalog/${type}/${id}.json",
timeout = 120L
).parsedSafe<CatalogResponse>()
res?.metas?.forEach { entry ->
entries.add(entry.toSearchResponse(provider))
}
}
@ -186,6 +187,7 @@ class StremioC : MainAPI() {
val source: String?,
val type: String?
)
private data class CatalogEntry(
@JsonProperty("name") val name: String,
@JsonProperty("id") val id: String,
@ -226,7 +228,7 @@ class StremioC : MainAPI() {
year = yearNum?.toIntOrNull()
tags = genre ?: genres
addActors(cast)
addTrailer(trailersSources?.map { "https://www.youtube.com/watch?v=${it.source}" }?.randomOrNull())
addTrailer(trailersSources?.map { "https://www.youtube.com/watch?v=${it.source}" })
addImdbId(imdbId)
}
} else {
@ -245,7 +247,8 @@ class StremioC : MainAPI() {
year = yearNum?.toIntOrNull()
tags = genre ?: genres
addActors(cast)
addTrailer(trailersSources?.map { "https://www.youtube.com/watch?v=${it.source}" }?.randomOrNull())
addTrailer(trailersSources?.map { "https://www.youtube.com/watch?v=${it.source}" }
?.randomOrNull())
addImdbId(imdbId)
}
}
@ -285,13 +288,14 @@ class StremioC : MainAPI() {
)
private data class ProxyHeaders(
val request: Map<String,String>?,
val request: Map<String, String>?,
)
private data class BehaviorHints(
val proxyHeaders: ProxyHeaders?,
val headers: Map<String,String>?,
val headers: Map<String, String>?,
)
private data class Stream(
val name: String?,
val title: String?,
@ -312,12 +316,13 @@ class StremioC : MainAPI() {
callback.invoke(
ExtractorLink(
name ?: "",
fixRDSourceName(name, title),
fixSourceName(name, title),
url,
"",
getQualityFromName(description),
headers = behaviorHints?.proxyHeaders?.request ?: behaviorHints?.headers ?: mapOf(),
isM3u8 = URI(url).path.endsWith(".m3u8")
getQuality(listOf(description,title,name)),
headers = behaviorHints?.proxyHeaders?.request ?: behaviorHints?.headers
?: mapOf(),
type = INFER_TYPE
)
)
subtitles.map { sub ->

View File

@ -10,26 +10,21 @@ import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
import com.lagradost.cloudstream3.utils.*
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
import com.lagradost.cloudstream3.utils.AppUtils.toJson
import java.net.URI
import java.util.ArrayList
import kotlin.math.roundToInt
import com.lagradost.cloudstream3.metaproviders.TmdbProvider
open class StremioX : TmdbProvider() {
class StremioX : TmdbProvider() {
override var mainUrl = "https://torrentio.strem.fun"
override var name = "StremioX"
override val hasMainPage = true
override val hasQuickSearch = true
override val supportedTypes = setOf(
TvType.Others,
)
override val supportedTypes = setOf(TvType.Others)
companion object {
const val TRACKER_LIST_URL =
"https://raw.githubusercontent.com/ngosang/trackerslist/master/trackers_best.txt"
const val TRACKER_LIST_URL = "https://raw.githubusercontent.com/ngosang/trackerslist/master/trackers_best.txt"
private const val tmdbAPI = "https://api.themoviedb.org/3"
private val apiKey =
base64DecodeAPI("ZTM=NTg=MjM=MjM=ODc=MzI=OGQ=MmE=Nzk=Nzk=ZjI=NTA=NDY=NDA=MzA=YjA=") // PLEASE DON'T STEAL
private const val apiKey = BuildConfig.TMDB_API
fun getType(t: String?): TvType {
return when (t) {
@ -44,11 +39,6 @@ open class StremioX : TmdbProvider() {
else -> ShowStatus.Completed
}
}
private fun base64DecodeAPI(api: String): String {
return api.chunked(4).map { base64Decode(it) }.reversed().joinToString("")
}
}
override val mainPage = mainPageOf(
@ -159,7 +149,7 @@ open class StremioX : TmdbProvider() {
eps.seasonNumber,
eps.episodeNumber
).toJson(),
name = eps.name + if (isUpcoming(eps.airDate)) " - [UPCOMING]" else "",
name = eps.name + if (isUpcoming(eps.airDate)) " [UPCOMING]" else "",
season = eps.seasonNumber,
episode = eps.episodeNumber,
posterUrl = getImageUrl(eps.stillPath),
@ -177,7 +167,7 @@ open class StremioX : TmdbProvider() {
this.backgroundPosterUrl = bgPoster
this.year = year
this.plot = res.overview
this.tags = if (isAnime) keywords else genres
this.tags = keywords.takeIf { !it.isNullOrEmpty() } ?: genres
this.rating = rating
this.showStatus = getStatus(res.status)
this.recommendations = recommendations
@ -200,7 +190,7 @@ open class StremioX : TmdbProvider() {
this.year = year
this.plot = res.overview
this.duration = res.runtime
this.tags = if (isAnime) keywords else genres
this.tags = keywords.takeIf { !it.isNullOrEmpty() } ?: genres
this.rating = rating
this.recommendations = recommendations
this.actors = actors
@ -243,13 +233,13 @@ open class StremioX : TmdbProvider() {
callback: (ExtractorLink) -> Unit
) {
val fixMainUrl = mainUrl.fixSourceUrl()
val url = if(season == null) {
val url = if (season == null) {
"$fixMainUrl/stream/movie/$imdbId.json"
} else {
"$fixMainUrl/stream/series/$imdbId:$season:$episode.json"
}
val res = AppUtils.tryParseJson<StreamsResponse>(request(url).body.string()) ?: return
res.streams.forEach { stream ->
val res = app.get(url, timeout = 120L).parsedSafe<StreamsResponse>()
res?.streams?.forEach { stream ->
stream.runCallback(subtitleCallback, callback)
}
}
@ -262,12 +252,12 @@ open class StremioX : TmdbProvider() {
)
private data class ProxyHeaders(
val request: Map<String,String>?,
val request: Map<String, String>?,
)
private data class BehaviorHints(
val proxyHeaders: ProxyHeaders?,
val headers: Map<String,String>?,
val headers: Map<String, String>?,
)
private data class Stream(
@ -290,12 +280,13 @@ open class StremioX : TmdbProvider() {
callback.invoke(
ExtractorLink(
name ?: "",
fixRDSourceName(name, title),
fixSourceName(name, title),
url,
"",
getQualityFromName(description),
headers = behaviorHints?.proxyHeaders?.request ?: behaviorHints?.headers ?: mapOf(),
isM3u8 = URI(url).path.endsWith(".m3u8")
getQuality(listOf(description,title,name)),
headers = behaviorHints?.proxyHeaders?.request ?: behaviorHints?.headers
?: mapOf(),
type = INFER_TYPE
)
)
subtitles.map { sub ->

View File

@ -3,10 +3,9 @@ package com.hexated
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.SubtitleFile
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.base64Encode
import com.lagradost.cloudstream3.utils.SubtitleHelper
const val openSubAPI = "https://opensubtitles.strem.io/stremio/v1"
const val openSubAPI = "https://opensubtitles-v3.strem.io"
const val watchSomuchAPI = "https://watchsomuch.tv"
object SubsExtractors {
@ -16,22 +15,20 @@ object SubsExtractors {
episode: Int? = null,
subtitleCallback: (SubtitleFile) -> Unit,
) {
val id = if(season == null) {
imdbId
val slug = if(season == null) {
"movie/$imdbId"
} else {
"$imdbId $season $episode"
"series/$imdbId:$season:$episode"
}
val data = base64Encode("""{"id":1,"jsonrpc":"2.0","method":"subtitles.find","params":[null,{"query":{"itemHash":"$id"}}]}""".toByteArray())
app.get("${openSubAPI}/q.json?b=$data").parsedSafe<OsResult>()?.result?.all?.map { sub ->
app.get("${openSubAPI}/subtitles/$slug.json", timeout = 120L).parsedSafe<OsResult>()?.subtitles?.map { sub ->
subtitleCallback.invoke(
SubtitleFile(
SubtitleHelper.fromThreeLettersToLanguage(sub.lang ?: "") ?: sub.lang
?: "",
?: return@map,
sub.url ?: return@map
)
)
}
}
suspend fun invokeWatchsomuch(
@ -45,7 +42,7 @@ object SubsExtractors {
"${watchSomuchAPI}/Watch/ajMovieTorrents.aspx", data = mapOf(
"index" to "0",
"mid" to "$id",
"wsk" to "f6ea6cde-e42b-4c26-98d3-b4fe48cdd4fb",
"wsk" to "30fb68aa-1c71-4b8c-b5d4-4ca9222cfb45",
"lid" to "",
"liu" to ""
), headers = mapOf("X-Requested-With" to "XMLHttpRequest")
@ -81,12 +78,8 @@ object SubsExtractors {
@JsonProperty("lang") val lang: String? = null,
)
data class OsAll(
@JsonProperty("all") val all: ArrayList<OsSubtitles>? = arrayListOf(),
)
data class OsResult(
@JsonProperty("result") val result: OsAll? = null,
@JsonProperty("subtitles") val subtitles: ArrayList<OsSubtitles>? = arrayListOf(),
)
data class WatchsomuchTorrents(

View File

@ -1,53 +1,35 @@
package com.hexated
import com.lagradost.cloudstream3.APIHolder
import com.lagradost.cloudstream3.APIHolder.unixTimeMS
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.nicehttp.Requests.Companion.await
import okhttp3.OkHttpClient
import okhttp3.Request
import com.lagradost.cloudstream3.utils.getQualityFromName
import okhttp3.Interceptor
import okhttp3.Response
import java.text.SimpleDateFormat
import java.util.Locale
import java.util.concurrent.TimeUnit
const val defaultTimeOut = 30L
suspend fun request(
url: String,
allowRedirects: Boolean = true,
timeout: Long = defaultTimeOut
): Response {
val client = OkHttpClient().newBuilder()
.connectTimeout(timeout, TimeUnit.SECONDS)
.readTimeout(timeout, TimeUnit.SECONDS)
.writeTimeout(timeout, TimeUnit.SECONDS)
.followRedirects(allowRedirects)
.followSslRedirects(allowRedirects)
.build()
val request: Request = Request.Builder()
.url(url)
.build()
return client.newCall(request).await()
}
fun Int.isSuccessful() : Boolean {
return this in 200..299
}
fun String.fixSourceUrl(): String {
return this.replace("/manifest.json", "").replace("stremio://", "https://")
}
fun fixRDSourceName(name: String?, title: String?): String {
fun fixSourceName(name: String?, title: String?): String {
return when {
name?.contains("[RD+]", true) == true -> "[RD+] $title"
name?.contains("[RD download]", true) == true -> "[RD] $title"
name?.contains("[RD download]", true) == true -> "[RD download] $title"
!name.isNullOrEmpty() && !title.isNullOrEmpty() -> "$name $title"
else -> title ?: name ?: ""
}
}
fun getQuality(qualities: List<String?>): Int {
fun String.getQuality(): String? {
return Regex("(\\d{3,4}[pP])").find(this)?.groupValues?.getOrNull(1)
}
val quality = qualities.firstNotNullOfOrNull { it?.getQuality() }
return getQualityFromName(quality)
}
fun getEpisodeSlug(
season: Int? = null,
episode: Int? = null,

View File

@ -0,0 +1,42 @@
import org.jetbrains.kotlin.konan.properties.Properties
// use an integer for version numbers
version = 3
android {
defaultConfig {
val properties = Properties()
properties.load(project.rootProject.file("local.properties").inputStream())
buildConfigField("String", "SUPERSTREAM_FIRST_API", "\"${properties.getProperty("SUPERSTREAM_FIRST_API")}\"")
buildConfigField("String", "SUPERSTREAM_SECOND_API", "\"${properties.getProperty("SUPERSTREAM_SECOND_API")}\"")
buildConfigField("String", "SUPERSTREAM_THIRD_API", "\"${properties.getProperty("SUPERSTREAM_THIRD_API")}\"")
buildConfigField("String", "SUPERSTREAM_FOURTH_API", "\"${properties.getProperty("SUPERSTREAM_FOURTH_API")}\"")
}
}
cloudstream {
language = "en"
// All of these properties are optional, you can safely remove them
// description = "Lorem Ipsum"
authors = listOf("Blatzar")
/**
* Status int as the following:
* 0: Down
* 1: Ok
* 2: Slow
* 3: Beta only
* */
status = 1 // will be 3 if unspecified
tvTypes = listOf(
"AsianDrama",
"Anime",
"TvSeries",
"Movie",
)
iconUrl = "https://cdn.discordapp.com/attachments/1109266606292488297/1196694385061003334/icon.png"
}

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.hexated"/>

View File

@ -0,0 +1,243 @@
package com.hexated
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
import com.lagradost.cloudstream3.utils.*
import java.net.URL
object Extractors : Superstream() {
suspend fun invokeInternalSource(
id: Int? = null,
type: Int? = null,
season: Int? = null,
episode: Int? = null,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit,
) {
fun LinkList.toExtractorLink(): ExtractorLink? {
if (this.path.isNullOrBlank()) return null
return ExtractorLink(
"Internal",
"Internal [${this.size}]",
this.path.replace("\\/", ""),
"",
getQualityFromName(this.quality),
)
}
// No childmode when getting links
// New api does not return video links :(
val query = if (type == ResponseTypes.Movies.value) {
"""{"childmode":"0","uid":"","app_version":"11.5","appid":"$appId","module":"Movie_downloadurl_v3","channel":"Website","mid":"$id","lang":"","expired_date":"${getExpiryDate()}","platform":"android","oss":"1","group":""}"""
} else {
"""{"childmode":"0","app_version":"11.5","module":"TV_downloadurl_v3","channel":"Website","episode":"$episode","expired_date":"${getExpiryDate()}","platform":"android","tid":"$id","oss":"1","uid":"","appid":"$appId","season":"$season","lang":"en","group":""}"""
}
val linkData = queryApiParsed<LinkDataProp>(query, false)
linkData.data?.list?.forEach {
callback.invoke(it.toExtractorLink() ?: return@forEach)
}
// Should really run this query for every link :(
val fid = linkData.data?.list?.firstOrNull { it.fid != null }?.fid
val subtitleQuery = if (type == ResponseTypes.Movies.value) {
"""{"childmode":"0","fid":"$fid","uid":"","app_version":"11.5","appid":"$appId","module":"Movie_srt_list_v2","channel":"Website","mid":"$id","lang":"en","expired_date":"${getExpiryDate()}","platform":"android"}"""
} else {
"""{"childmode":"0","fid":"$fid","app_version":"11.5","module":"TV_srt_list_v2","channel":"Website","episode":"$episode","expired_date":"${getExpiryDate()}","platform":"android","tid":"$id","uid":"","appid":"$appId","season":"$season","lang":"en"}"""
}
val subtitles = queryApiParsed<SubtitleDataProp>(subtitleQuery).data
subtitles?.list?.forEach { subs ->
val sub = subs.subtitles.maxByOrNull { it.support_total ?: 0 }
subtitleCallback.invoke(
SubtitleFile(
sub?.language ?: sub?.lang ?: return@forEach,
sub?.filePath ?: return@forEach
)
)
}
}
suspend fun invokeExternalSource(
mediaId: Int? = null,
type: Int? = null,
season: Int? = null,
episode: Int? = null,
callback: (ExtractorLink) -> Unit,
) {
val (seasonSlug, episodeSlug) = getEpisodeSlug(season, episode)
val shareKey = app.get("$fourthAPI/index/share_link?id=${mediaId}&type=$type")
.parsedSafe<ExternalResponse>()?.data?.link?.substringAfterLast("/") ?: return
val headers = mapOf("Accept-Language" to "en")
val shareRes = app.get("$thirdAPI/file/file_share_list?share_key=$shareKey", headers = headers)
.parsedSafe<ExternalResponse>()?.data ?: return
val fids = if (season == null) {
shareRes.file_list
} else {
val parentId = shareRes.file_list?.find { it.file_name.equals("season $season", true) }?.fid
app.get("$thirdAPI/file/file_share_list?share_key=$shareKey&parent_id=$parentId&page=1", headers = headers)
.parsedSafe<ExternalResponse>()?.data?.file_list?.filter {
it.file_name?.contains("s${seasonSlug}e${episodeSlug}", true) == true
}
} ?: return
fids.apmapIndexed { index, fileList ->
val player = app.get("$thirdAPI/file/player?fid=${fileList.fid}&share_key=$shareKey").text
val sources = "sources\\s*=\\s*(.*);".toRegex().find(player)?.groupValues?.get(1)
val qualities = "quality_list\\s*=\\s*(.*);".toRegex().find(player)?.groupValues?.get(1)
listOf(sources, qualities).forEach {
AppUtils.tryParseJson<ArrayList<ExternalSources>>(it)?.forEach org@{ source ->
val format = if (source.type == "video/mp4") ExtractorLinkType.VIDEO else ExtractorLinkType.M3U8
val label = if (format == ExtractorLinkType.M3U8) "Hls" else "Mp4"
if(!(source.label == "AUTO" || format == ExtractorLinkType.VIDEO)) return@org
callback.invoke(
ExtractorLink(
"External",
"External $label [Server ${index + 1}]",
(source.m3u8_url ?: source.file)?.replace("\\/", "/") ?: return@org,
"",
getIndexQuality(if (format == ExtractorLinkType.M3U8) fileList.file_name else source.label),
type = format,
)
)
}
}
}
}
suspend fun invokeWatchsomuch(
imdbId: String? = null,
season: Int? = null,
episode: Int? = null,
subtitleCallback: (SubtitleFile) -> Unit,
) {
val id = imdbId?.removePrefix("tt")
val epsId = app.post(
"$watchSomuchAPI/Watch/ajMovieTorrents.aspx",
data = mapOf(
"index" to "0",
"mid" to "$id",
"wsk" to "30fb68aa-1c71-4b8c-b5d4-4ca9222cfb45",
"lid" to "",
"liu" to ""
), headers = mapOf("X-Requested-With" to "XMLHttpRequest")
).parsedSafe<WatchsomuchResponses>()?.movie?.torrents?.let { eps ->
if (season == null) {
eps.firstOrNull()?.id
} else {
eps.find { it.episode == episode && it.season == season }?.id
}
} ?: return
val (seasonSlug, episodeSlug) = getEpisodeSlug(
season,
episode
)
val subUrl = if (season == null) {
"$watchSomuchAPI/Watch/ajMovieSubtitles.aspx?mid=$id&tid=$epsId&part="
} else {
"$watchSomuchAPI/Watch/ajMovieSubtitles.aspx?mid=$id&tid=$epsId&part=S${seasonSlug}E${episodeSlug}"
}
app.get(subUrl)
.parsedSafe<WatchsomuchSubResponses>()?.subtitles
?.map { sub ->
subtitleCallback.invoke(
SubtitleFile(
sub.label ?: "",
fixUrl(sub.url ?: return@map null, watchSomuchAPI)
)
)
}
}
suspend fun invokeOpenSubs(
imdbId: String? = null,
season: Int? = null,
episode: Int? = null,
subtitleCallback: (SubtitleFile) -> Unit,
) {
val slug = if(season == null) {
"movie/$imdbId"
} else {
"series/$imdbId:$season:$episode"
}
app.get("${openSubAPI}/subtitles/$slug.json", timeout = 120L).parsedSafe<OsResult>()?.subtitles?.map { sub ->
subtitleCallback.invoke(
SubtitleFile(
SubtitleHelper.fromThreeLettersToLanguage(sub.lang ?: "") ?: sub.lang
?: return@map,
sub.url ?: return@map
)
)
}
}
suspend fun invokeVidsrcto(
imdbId: String?,
season: Int?,
episode: Int?,
subtitleCallback: (SubtitleFile) -> Unit,
) {
val url = if (season == null) {
"$vidsrctoAPI/embed/movie/$imdbId"
} else {
"$vidsrctoAPI/embed/tv/$imdbId/$season/$episode"
}
val mediaId = app.get(url).document.selectFirst("ul.episodes li a")?.attr("data-id") ?: return
val subtitles = app.get("$vidsrctoAPI/ajax/embed/episode/$mediaId/subtitles").text
AppUtils.tryParseJson<List<VidsrcSubtitles>>(subtitles)?.map {
subtitleCallback.invoke(
SubtitleFile(
it.label ?: "",
it.file ?: return@map
)
)
}
}
private fun fixUrl(url: String, domain: String): String {
if (url.startsWith("http")) {
return url
}
if (url.isEmpty()) {
return ""
}
val startsWithNoHttp = url.startsWith("//")
if (startsWithNoHttp) {
return "https:$url"
} else {
if (url.startsWith('/')) {
return domain + url
}
return "$domain/$url"
}
}
private fun getIndexQuality(str: String?): Int {
return Regex("(\\d{3,4})[pP]").find(str ?: "")?.groupValues?.getOrNull(1)?.toIntOrNull()
?: Qualities.Unknown.value
}
private fun getEpisodeSlug(
season: Int? = null,
episode: Int? = null,
): Pair<String, String> {
return if (season == null && episode == null) {
"" to ""
} else {
(if (season!! < 10) "0$season" else "$season") to (if (episode!! < 10) "0$episode" else "$episode")
}
}
}

View File

@ -0,0 +1,823 @@
package com.hexated
import android.util.Base64
import com.fasterxml.jackson.annotation.JsonProperty
import com.hexated.Extractors.invokeExternalSource
import com.hexated.Extractors.invokeInternalSource
import com.hexated.Extractors.invokeOpenSubs
import com.hexated.Extractors.invokeVidsrcto
import com.hexated.Extractors.invokeWatchsomuch
import com.hexated.Superstream.CipherUtils.getVerify
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.APIHolder.capitalize
import com.lagradost.cloudstream3.APIHolder.unixTime
import com.lagradost.cloudstream3.LoadResponse.Companion.addImdbId
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
import com.lagradost.cloudstream3.utils.AppUtils.toJson
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.nicehttp.NiceResponse
import okhttp3.Interceptor
import okhttp3.Response
import java.nio.charset.StandardCharsets
import java.security.MessageDigest
import java.security.NoSuchAlgorithmException
import javax.crypto.Cipher
import javax.crypto.Cipher.DECRYPT_MODE
import javax.crypto.Cipher.ENCRYPT_MODE
import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.SecretKeySpec
import kotlin.math.roundToInt
open class Superstream : MainAPI() {
private val timeout = 60L
override var name = "SuperStream"
override val hasMainPage = true
override val hasChromecastSupport = true
override val supportedTypes = setOf(
TvType.Movie,
TvType.TvSeries,
TvType.Anime,
TvType.AnimeMovie,
)
enum class ResponseTypes(val value: Int) {
Series(2),
Movies(1);
fun toTvType(): TvType {
return if (this == Series) TvType.TvSeries else TvType.Movie
}
companion object {
fun getResponseType(value: Int?): ResponseTypes {
return values().firstOrNull { it.value == value } ?: Movies
}
}
}
override val instantLinkLoading = true
private val interceptor = UserAgentInterceptor()
private val headers = mapOf(
"Platform" to "android",
"Accept" to "charset=utf-8",
)
private class UserAgentInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
return chain.proceed(
chain.request()
.newBuilder()
.removeHeader("user-agent")
.build()
)
}
}
// Random 32 length string
private fun randomToken(): String {
return (0..31).joinToString("") {
(('0'..'9') + ('a'..'f')).random().toString()
}
}
private val token = randomToken()
private object CipherUtils {
private const val ALGORITHM = "DESede"
private const val TRANSFORMATION = "DESede/CBC/PKCS5Padding"
fun encrypt(str: String, key: String, iv: String): String? {
return try {
val cipher: Cipher = Cipher.getInstance(TRANSFORMATION)
val bArr = ByteArray(24)
val bytes: ByteArray = key.toByteArray()
var length = if (bytes.size <= 24) bytes.size else 24
System.arraycopy(bytes, 0, bArr, 0, length)
while (length < 24) {
bArr[length] = 0
length++
}
cipher.init(
ENCRYPT_MODE,
SecretKeySpec(bArr, ALGORITHM),
IvParameterSpec(iv.toByteArray())
)
String(Base64.encode(cipher.doFinal(str.toByteArray()), 2), StandardCharsets.UTF_8)
} catch (e: Exception) {
e.printStackTrace()
null
}
}
// Useful for deobfuscation
fun decrypt(str: String, key: String, iv: String): String? {
return try {
val cipher: Cipher = Cipher.getInstance(TRANSFORMATION)
val bArr = ByteArray(24)
val bytes: ByteArray = key.toByteArray()
var length = if (bytes.size <= 24) bytes.size else 24
System.arraycopy(bytes, 0, bArr, 0, length)
while (length < 24) {
bArr[length] = 0
length++
}
cipher.init(
DECRYPT_MODE,
SecretKeySpec(bArr, ALGORITHM),
IvParameterSpec(iv.toByteArray())
)
val inputStr = Base64.decode(str.toByteArray(), Base64.DEFAULT)
cipher.doFinal(inputStr).decodeToString()
} catch (e: Exception) {
e.printStackTrace()
null
}
}
fun md5(str: String): String? {
return MD5Util.md5(str)?.let { HexDump.toHexString(it).lowercase() }
}
fun getVerify(str: String?, str2: String, str3: String): String? {
if (str != null) {
return md5(md5(str2) + str3 + str)
}
return null
}
}
private object HexDump {
private val HEX_DIGITS = charArrayOf(
'0',
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
'A',
'B',
'C',
'D',
'E',
'F'
)
@JvmOverloads
fun toHexString(bArr: ByteArray, i: Int = 0, i2: Int = bArr.size): String {
val cArr = CharArray(i2 * 2)
var i3 = 0
for (i4 in i until i + i2) {
val b = bArr[i4].toInt()
val i5 = i3 + 1
val cArr2 = HEX_DIGITS
cArr[i3] = cArr2[b ushr 4 and 15]
i3 = i5 + 1
cArr[i5] = cArr2[b and 15]
}
return String(cArr)
}
}
private object MD5Util {
fun md5(str: String): ByteArray? {
return md5(str.toByteArray())
}
fun md5(bArr: ByteArray?): ByteArray? {
return try {
val digest = MessageDigest.getInstance("MD5")
digest.update(bArr ?: return null)
digest.digest()
} catch (e: NoSuchAlgorithmException) {
e.printStackTrace()
null
}
}
}
suspend fun queryApi(query: String, useAlternativeApi: Boolean): NiceResponse {
val encryptedQuery = CipherUtils.encrypt(query, key, iv)!!
val appKeyHash = CipherUtils.md5(appKey)!!
val newBody =
"""{"app_key":"$appKeyHash","verify":"${
getVerify(
encryptedQuery,
appKey,
key
)
}","encrypt_data":"$encryptedQuery"}"""
val base64Body = String(Base64.encode(newBody.toByteArray(), Base64.DEFAULT))
val data = mapOf(
"data" to base64Body,
"appid" to "27",
"platform" to "android",
"version" to appVersionCode,
// Probably best to randomize this
"medium" to "Website&token$token"
)
val url = if (useAlternativeApi) secondAPI else firstAPI
return app.post(
url,
headers = headers,
data = data,
timeout = timeout,
interceptor = interceptor
)
}
suspend inline fun <reified T : Any> queryApiParsed(
query: String,
useAlternativeApi: Boolean = true
): T {
return queryApi(query, useAlternativeApi).parsed()
}
fun getExpiryDate(): Long {
// Current time + 12 hours
return unixTime + 60 * 60 * 12
}
private data class PostJSON(
@JsonProperty("id") val id: Int? = null,
@JsonProperty("title") val title: String? = null,
@JsonProperty("poster") val poster: String? = null,
@JsonProperty("poster_2") val poster2: String? = null,
@JsonProperty("box_type") val boxType: Int? = null,
@JsonProperty("imdb_rating") val imdbRating: String? = null,
@JsonProperty("quality_tag") val quality_tag: String? = null,
)
private data class ListJSON(
@JsonProperty("code") val code: Int? = null,
@JsonProperty("type") val type: String? = null,
@JsonProperty("name") val name: String? = null,
@JsonProperty("box_type") val boxType: Int? = null,
@JsonProperty("list") val list: ArrayList<PostJSON> = arrayListOf(),
)
private data class DataJSON(
@JsonProperty("data") val data: ArrayList<ListJSON> = arrayListOf()
)
// We do not want content scanners to notice this scraping going on so we've hidden all constants
// The source has its origins in China so I added some extra security with banned words
// Mayhaps a tiny bit unethical, but this source is just too good :)
// If you are copying this code please use precautions so they do not change their api.
// Free Tibet, The Tienanmen Square protests of 1989
private val iv = base64Decode("d0VpcGhUbiE=")
private val key = base64Decode("MTIzZDZjZWRmNjI2ZHk1NDIzM2FhMXc2")
private val firstAPI = BuildConfig.SUPERSTREAM_FIRST_API
// Another url because the first one sucks at searching
// This one was revealed to me in a dream
private val secondAPI = BuildConfig.SUPERSTREAM_SECOND_API
val thirdAPI = BuildConfig.SUPERSTREAM_THIRD_API
val fourthAPI = BuildConfig.SUPERSTREAM_FOURTH_API
val watchSomuchAPI = "https://watchsomuch.tv"
val openSubAPI = "https://opensubtitles-v3.strem.io"
val vidsrctoAPI = "https://vidsrc.to"
private val appKey = base64Decode("bW92aWVib3g=")
val appId = base64Decode("Y29tLnRkby5zaG93Ym94")
private val appIdSecond = base64Decode("Y29tLm1vdmllYm94cHJvLmFuZHJvaWQ=")
private val appVersion = "11.5"
private val appVersionCode = "129"
override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse {
val hideNsfw = if (settingsForProvider.enableAdult) 0 else 1
val data = queryApiParsed<DataJSON>(
"""{"childmode":"$hideNsfw","app_version":"$appVersion","appid":"$appIdSecond","module":"Home_list_type_v5","channel":"Website","page":"$page","lang":"en","type":"all","pagelimit":"10","expired_date":"${getExpiryDate()}","platform":"android"}
""".trimIndent()
)
// Cut off the first row (featured)
val pages = data.data.let { it.subList(minOf(it.size, 1), it.size) }
.mapNotNull {
var name = it.name
if (name.isNullOrEmpty()) name = "Featured"
val postList = it.list.mapNotNull second@{ post ->
val type = if (post.boxType == 1) TvType.Movie else TvType.TvSeries
newMovieSearchResponse(
name = post.title ?: return@second null,
url = LoadData(post.id ?: return@mapNotNull null, post.boxType).toJson(),
type = type,
fix = false
) {
posterUrl = post.poster ?: post.poster2
quality = getQualityFromString(post.quality_tag ?: "")
}
}
if (postList.isEmpty()) return@mapNotNull null
HomePageList(name, postList)
}
return HomePageResponse(pages, hasNext = !pages.any { it.list.isEmpty() })
}
private data class Data(
@JsonProperty("id") val id: Int? = null,
@JsonProperty("mid") val mid: Int? = null,
@JsonProperty("box_type") val boxType: Int? = null,
@JsonProperty("title") val title: String? = null,
@JsonProperty("poster_org") val posterOrg: String? = null,
@JsonProperty("poster") val poster: String? = null,
@JsonProperty("cats") val cats: String? = null,
@JsonProperty("year") val year: Int? = null,
@JsonProperty("imdb_rating") val imdbRating: String? = null,
@JsonProperty("quality_tag") val qualityTag: String? = null,
) {
fun toSearchResponse(api: MainAPI): MovieSearchResponse? {
return api.newMovieSearchResponse(
this.title ?: "",
LoadData(
this.id ?: this.mid ?: return null,
this.boxType ?: ResponseTypes.Movies.value
).toJson(),
ResponseTypes.getResponseType(this.boxType).toTvType(),
false
) {
posterUrl = this@Data.posterOrg ?: this@Data.poster
year = this@Data.year
quality = getQualityFromString(this@Data.qualityTag?.replace("-", "") ?: "")
}
}
}
private data class MainDataList(
@JsonProperty("list") val list: ArrayList<Data> = arrayListOf()
)
private data class MainData(
@JsonProperty("data") val data: MainDataList
)
override suspend fun search(query: String): List<SearchResponse> {
val hideNsfw = if (settingsForProvider.enableAdult) 0 else 1
val apiQuery =
// Originally 8 pagelimit
"""{"childmode":"$hideNsfw","app_version":"$appVersion","appid":"$appIdSecond","module":"Search4","channel":"Website","page":"1","lang":"en","type":"all","keyword":"$query","pagelimit":"20","expired_date":"${getExpiryDate()}","platform":"android"}"""
val searchResponse = queryApiParsed<MainData>(apiQuery, true).data.list.mapNotNull {
it.toSearchResponse(this)
}
return searchResponse
}
private data class LoadData(
val id: Int,
val type: Int?
)
private data class MovieData(
@JsonProperty("id") val id: Int? = null,
@JsonProperty("title") val title: String? = null,
@JsonProperty("director") val director: String? = null,
@JsonProperty("writer") val writer: String? = null,
@JsonProperty("actors") val actors: String? = null,
@JsonProperty("runtime") val runtime: Int? = null,
@JsonProperty("poster") val poster: String? = null,
@JsonProperty("description") val description: String? = null,
@JsonProperty("cats") val cats: String? = null,
@JsonProperty("year") val year: Int? = null,
@JsonProperty("imdb_id") val imdbId: String? = null,
@JsonProperty("imdb_rating") val imdbRating: String? = null,
@JsonProperty("trailer") val trailer: String? = null,
@JsonProperty("released") val released: String? = null,
@JsonProperty("content_rating") val contentRating: String? = null,
@JsonProperty("tmdb_id") val tmdbId: Int? = null,
@JsonProperty("tomato_meter") val tomatoMeter: Int? = null,
@JsonProperty("poster_org") val posterOrg: String? = null,
@JsonProperty("trailer_url") val trailerUrl: String? = null,
@JsonProperty("imdb_link") val imdbLink: String? = null,
@JsonProperty("box_type") val boxType: Int? = null,
@JsonProperty("recommend") val recommend: List<Data> = listOf(),
)
private data class MovieDataProp(
@JsonProperty("data") val data: MovieData? = MovieData()
)
private data class SeriesDataProp(
@JsonProperty("code") val code: Int? = null,
@JsonProperty("msg") val msg: String? = null,
@JsonProperty("data") val data: SeriesData? = SeriesData()
)
private data class SeriesSeasonProp(
@JsonProperty("code") val code: Int? = null,
@JsonProperty("msg") val msg: String? = null,
@JsonProperty("data") val data: ArrayList<SeriesEpisode>? = arrayListOf()
)
// data class PlayProgress (
//
// @JsonProperty("over" ) val over : Int? = null,
// @JsonProperty("seconds" ) val seconds : Int? = null,
// @JsonProperty("mp4_id" ) val mp4Id : Int? = null,
// @JsonProperty("last_time" ) val lastTime : Int? = null
//
//)
private data class SeriesEpisode(
@JsonProperty("id") val id: Int? = null,
@JsonProperty("tid") val tid: Int? = null,
@JsonProperty("mb_id") val mbId: Int? = null,
@JsonProperty("imdb_id") val imdbId: String? = null,
@JsonProperty("imdb_id_status") val imdbIdStatus: Int? = null,
@JsonProperty("srt_status") val srtStatus: Int? = null,
@JsonProperty("season") val season: Int? = null,
@JsonProperty("episode") val episode: Int? = null,
@JsonProperty("state") val state: Int? = null,
@JsonProperty("title") val title: String? = null,
@JsonProperty("thumbs") val thumbs: String? = null,
@JsonProperty("thumbs_bak") val thumbsBak: String? = null,
@JsonProperty("thumbs_original") val thumbsOriginal: String? = null,
@JsonProperty("poster_imdb") val posterImdb: Int? = null,
@JsonProperty("synopsis") val synopsis: String? = null,
@JsonProperty("runtime") val runtime: Int? = null,
@JsonProperty("view") val view: Int? = null,
@JsonProperty("download") val download: Int? = null,
@JsonProperty("source_file") val sourceFile: Int? = null,
@JsonProperty("code_file") val codeFile: Int? = null,
@JsonProperty("add_time") val addTime: Int? = null,
@JsonProperty("update_time") val updateTime: Int? = null,
@JsonProperty("released") val released: String? = null,
@JsonProperty("released_timestamp") val releasedTimestamp: Long? = null,
@JsonProperty("audio_lang") val audioLang: String? = null,
@JsonProperty("quality_tag") val qualityTag: String? = null,
@JsonProperty("3d") val _3d: Int? = null,
@JsonProperty("remark") val remark: String? = null,
@JsonProperty("pending") val pending: String? = null,
@JsonProperty("imdb_rating") val imdbRating: String? = null,
@JsonProperty("display") val display: Int? = null,
@JsonProperty("sync") val sync: Int? = null,
@JsonProperty("tomato_meter") val tomatoMeter: Int? = null,
@JsonProperty("tomato_meter_count") val tomatoMeterCount: Int? = null,
@JsonProperty("tomato_audience") val tomatoAudience: Int? = null,
@JsonProperty("tomato_audience_count") val tomatoAudienceCount: Int? = null,
@JsonProperty("thumbs_min") val thumbsMin: String? = null,
@JsonProperty("thumbs_org") val thumbsOrg: String? = null,
@JsonProperty("imdb_link") val imdbLink: String? = null,
// @JsonProperty("quality_tags") val qualityTags: ArrayList<String> = arrayListOf(),
// @JsonProperty("play_progress" ) val playProgress : PlayProgress? = PlayProgress()
)
private data class SeriesLanguage(
@JsonProperty("title") val title: String? = null,
@JsonProperty("lang") val lang: String? = null
)
private data class SeriesData(
@JsonProperty("id") val id: Int? = null,
@JsonProperty("mb_id") val mbId: Int? = null,
@JsonProperty("title") val title: String? = null,
@JsonProperty("display") val display: Int? = null,
@JsonProperty("state") val state: Int? = null,
@JsonProperty("vip_only") val vipOnly: Int? = null,
@JsonProperty("code_file") val codeFile: Int? = null,
@JsonProperty("director") val director: String? = null,
@JsonProperty("writer") val writer: String? = null,
@JsonProperty("actors") val actors: String? = null,
@JsonProperty("add_time") val addTime: Int? = null,
@JsonProperty("poster") val poster: String? = null,
@JsonProperty("poster_imdb") val posterImdb: Int? = null,
@JsonProperty("banner_mini") val bannerMini: String? = null,
@JsonProperty("description") val description: String? = null,
@JsonProperty("imdb_id") val imdbId: String? = null,
@JsonProperty("cats") val cats: String? = null,
@JsonProperty("year") val year: Int? = null,
@JsonProperty("collect") val collect: Int? = null,
@JsonProperty("view") val view: Int? = null,
@JsonProperty("download") val download: Int? = null,
@JsonProperty("update_time") val updateTime: String? = null,
@JsonProperty("released") val released: String? = null,
@JsonProperty("released_timestamp") val releasedTimestamp: Int? = null,
@JsonProperty("episode_released") val episodeReleased: String? = null,
@JsonProperty("episode_released_timestamp") val episodeReleasedTimestamp: Int? = null,
@JsonProperty("max_season") val maxSeason: Int? = null,
@JsonProperty("max_episode") val maxEpisode: Int? = null,
@JsonProperty("remark") val remark: String? = null,
@JsonProperty("imdb_rating") val imdbRating: String? = null,
@JsonProperty("content_rating") val contentRating: String? = null,
@JsonProperty("tmdb_id") val tmdbId: Int? = null,
@JsonProperty("tomato_url") val tomatoUrl: String? = null,
@JsonProperty("tomato_meter") val tomatoMeter: Int? = null,
@JsonProperty("tomato_meter_count") val tomatoMeterCount: Int? = null,
@JsonProperty("tomato_meter_state") val tomatoMeterState: String? = null,
@JsonProperty("reelgood_url") val reelgoodUrl: String? = null,
@JsonProperty("audience_score") val audienceScore: Int? = null,
@JsonProperty("audience_score_count") val audienceScoreCount: Int? = null,
@JsonProperty("no_tomato_url") val noTomatoUrl: Int? = null,
@JsonProperty("order_year") val orderYear: Int? = null,
@JsonProperty("episodate_id") val episodateId: String? = null,
@JsonProperty("weights_day") val weightsDay: Double? = null,
@JsonProperty("poster_min") val posterMin: String? = null,
@JsonProperty("poster_org") val posterOrg: String? = null,
@JsonProperty("banner_mini_min") val bannerMiniMin: String? = null,
@JsonProperty("banner_mini_org") val bannerMiniOrg: String? = null,
@JsonProperty("trailer_url") val trailerUrl: String? = null,
@JsonProperty("years") val years: ArrayList<Int> = arrayListOf(),
@JsonProperty("season") val season: ArrayList<Int> = arrayListOf(),
@JsonProperty("history") val history: ArrayList<String> = arrayListOf(),
@JsonProperty("imdb_link") val imdbLink: String? = null,
@JsonProperty("episode") val episode: ArrayList<SeriesEpisode> = arrayListOf(),
// @JsonProperty("is_collect") val isCollect: Int? = null,
@JsonProperty("language") val language: ArrayList<SeriesLanguage> = arrayListOf(),
@JsonProperty("box_type") val boxType: Int? = null,
@JsonProperty("year_year") val yearYear: String? = null,
@JsonProperty("season_episode") val seasonEpisode: String? = null
)
override suspend fun load(url: String): LoadResponse {
val loadData = parseJson<LoadData>(url)
// val module = if(type === "TvType.Movie") "Movie_detail" else "*tv series module*"
val isMovie = loadData.type == ResponseTypes.Movies.value
val hideNsfw = if (settingsForProvider.enableAdult) 0 else 1
if (isMovie) { // 1 = Movie
val apiQuery =
"""{"childmode":"$hideNsfw","uid":"","app_version":"$appVersion","appid":"$appIdSecond","module":"Movie_detail","channel":"Website","mid":"${loadData.id}","lang":"en","expired_date":"${getExpiryDate()}","platform":"android","oss":"","group":""}"""
val data = (queryApiParsed<MovieDataProp>(apiQuery)).data
?: throw RuntimeException("API error")
return newMovieLoadResponse(
data.title ?: "",
url,
TvType.Movie,
LinkData(
data.id ?: throw RuntimeException("No movie ID"),
ResponseTypes.Movies.value,
null,
null,
data.id,
data.imdbId
),
) {
this.recommendations =
data.recommend.mapNotNull { it.toSearchResponse(this@Superstream) }
this.posterUrl = data.posterOrg ?: data.poster
this.year = data.year
this.plot = data.description
this.tags = data.cats?.split(",")?.map { it.capitalize() }
this.rating = data.imdbRating?.split("/")?.get(0)?.toIntOrNull()
addTrailer(data.trailerUrl)
this.addImdbId(data.imdbId)
}
} else { // 2 Series
val apiQuery =
"""{"childmode":"$hideNsfw","uid":"","app_version":"$appVersion","appid":"$appIdSecond","module":"TV_detail_1","display_all":"1","channel":"Website","lang":"en","expired_date":"${getExpiryDate()}","platform":"android","tid":"${loadData.id}"}"""
val data = (queryApiParsed<SeriesDataProp>(apiQuery)).data
?: throw RuntimeException("API error")
val episodes = data.season.mapNotNull {
val seasonQuery =
"""{"childmode":"$hideNsfw","app_version":"$appVersion","year":"0","appid":"$appIdSecond","module":"TV_episode","display_all":"1","channel":"Website","season":"$it","lang":"en","expired_date":"${getExpiryDate()}","platform":"android","tid":"${loadData.id}"}"""
(queryApiParsed<SeriesSeasonProp>(seasonQuery)).data
}.flatten()
return newTvSeriesLoadResponse(
data.title ?: "",
url,
TvType.TvSeries,
episodes.mapNotNull {
Episode(
LinkData(
it.tid ?: it.id ?: return@mapNotNull null,
ResponseTypes.Series.value,
it.season,
it.episode,
data.id,
data.imdbId
).toJson(),
it.title,
it.season,
it.episode,
it.thumbs ?: it.thumbsBak ?: it.thumbsMin ?: it.thumbsOriginal
?: it.thumbsOrg,
it.imdbRating?.toDoubleOrNull()?.times(10)?.roundToInt(),
it.synopsis,
it.releasedTimestamp
)
}
) {
this.year = data.year
this.plot = data.description
this.posterUrl = data.posterOrg ?: data.poster
this.rating = data.imdbRating?.split("/")?.get(0)?.toIntOrNull()
this.tags = data.cats?.split(",")?.map { it.capitalize() }
this.addImdbId(data.imdbId)
}
}
}
private data class LinkData(
val id: Int,
val type: Int,
val season: Int?,
val episode: Int?,
val mediaId: Int?,
val imdbId: String?,
)
data class LinkDataProp(
@JsonProperty("code") val code: Int? = null,
@JsonProperty("msg") val msg: String? = null,
@JsonProperty("data") val data: ParsedLinkData? = ParsedLinkData()
)
data class LinkList(
@JsonProperty("path") val path: String? = null,
@JsonProperty("quality") val quality: String? = null,
@JsonProperty("real_quality") val realQuality: String? = null,
@JsonProperty("format") val format: String? = null,
@JsonProperty("size") val size: String? = null,
@JsonProperty("size_bytes") val sizeBytes: Long? = null,
@JsonProperty("count") val count: Int? = null,
@JsonProperty("dateline") val dateline: Long? = null,
@JsonProperty("fid") val fid: Int? = null,
@JsonProperty("mmfid") val mmfid: Int? = null,
@JsonProperty("h265") val h265: Int? = null,
@JsonProperty("hdr") val hdr: Int? = null,
@JsonProperty("filename") val filename: String? = null,
@JsonProperty("original") val original: Int? = null,
@JsonProperty("colorbit") val colorbit: Int? = null,
@JsonProperty("success") val success: Int? = null,
@JsonProperty("timeout") val timeout: Int? = null,
@JsonProperty("vip_link") val vipLink: Int? = null,
@JsonProperty("fps") val fps: Int? = null,
@JsonProperty("bitstream") val bitstream: String? = null,
@JsonProperty("width") val width: Int? = null,
@JsonProperty("height") val height: Int? = null
)
data class ParsedLinkData(
@JsonProperty("seconds") val seconds: Int? = null,
@JsonProperty("quality") val quality: ArrayList<String> = arrayListOf(),
@JsonProperty("list") val list: ArrayList<LinkList> = arrayListOf()
)
data class SubtitleDataProp(
@JsonProperty("code") val code: Int? = null,
@JsonProperty("msg") val msg: String? = null,
@JsonProperty("data") val data: PrivateSubtitleData? = PrivateSubtitleData()
)
data class Subtitles(
@JsonProperty("sid") val sid: Int? = null,
@JsonProperty("mid") val mid: String? = null,
@JsonProperty("file_path") val filePath: String? = null,
@JsonProperty("lang") val lang: String? = null,
@JsonProperty("language") val language: String? = null,
@JsonProperty("delay") val delay: Int? = null,
@JsonProperty("point") val point: String? = null,
@JsonProperty("order") val order: Int? = null,
@JsonProperty("support_total") val support_total: Int? = null,
@JsonProperty("admin_order") val adminOrder: Int? = null,
@JsonProperty("myselect") val myselect: Int? = null,
@JsonProperty("add_time") val addTime: Long? = null,
@JsonProperty("count") val count: Int? = null
)
data class SubtitleList(
@JsonProperty("language") val language: String? = null,
@JsonProperty("subtitles") val subtitles: ArrayList<Subtitles> = arrayListOf()
)
data class PrivateSubtitleData(
@JsonProperty("select") val select: ArrayList<String> = arrayListOf(),
@JsonProperty("list") val list: ArrayList<SubtitleList> = arrayListOf()
)
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
val parsed = parseJson<LinkData>(data)
argamap(
{
invokeVidsrcto(
parsed.imdbId,
parsed.season,
parsed.episode,
subtitleCallback
)
},
{
invokeExternalSource(
parsed.mediaId,
parsed.type,
parsed.season,
parsed.episode,
callback
)
},
{
invokeInternalSource(
parsed.id,
parsed.type,
parsed.season,
parsed.episode,
subtitleCallback,
callback
)
},
{
invokeOpenSubs(
parsed.imdbId,
parsed.season,
parsed.episode,
subtitleCallback
)
},
{
invokeWatchsomuch(
parsed.imdbId,
parsed.season,
parsed.episode,
subtitleCallback
)
}
)
return true
}
data class ExternalResponse(
@JsonProperty("data") val data: Data? = null,
) {
data class Data(
@JsonProperty("link") val link: String? = null,
@JsonProperty("file_list") val file_list: ArrayList<FileList>? = arrayListOf(),
) {
data class FileList(
@JsonProperty("fid") val fid: Long? = null,
@JsonProperty("file_name") val file_name: String? = null,
@JsonProperty("oss_fid") val oss_fid: Long? = null,
)
}
}
data class ExternalSources(
@JsonProperty("m3u8_url") val m3u8_url: String? = null,
@JsonProperty("file") val file: String? = null,
@JsonProperty("label") val label: String? = null,
@JsonProperty("type") val type: String? = null,
)
data class WatchsomuchTorrents(
@JsonProperty("id") val id: Int? = null,
@JsonProperty("movieId") val movieId: Int? = null,
@JsonProperty("season") val season: Int? = null,
@JsonProperty("episode") val episode: Int? = null,
)
data class WatchsomuchMovies(
@JsonProperty("torrents") val torrents: ArrayList<WatchsomuchTorrents>? = arrayListOf(),
)
data class WatchsomuchResponses(
@JsonProperty("movie") val movie: WatchsomuchMovies? = null,
)
data class WatchsomuchSubtitles(
@JsonProperty("url") val url: String? = null,
@JsonProperty("label") val label: String? = null,
)
data class WatchsomuchSubResponses(
@JsonProperty("subtitles") val subtitles: ArrayList<WatchsomuchSubtitles>? = arrayListOf(),
)
data class OsSubtitles(
@JsonProperty("url") val url: String? = null,
@JsonProperty("lang") val lang: String? = null,
)
data class OsResult(
@JsonProperty("subtitles") val subtitles: ArrayList<OsSubtitles>? = arrayListOf(),
)
data class VidsrcSubtitles(
@JsonProperty("label") val label: String? = null,
@JsonProperty("file") val file: String? = null,
)
}

View File

@ -0,0 +1,14 @@
package com.hexated
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
import com.lagradost.cloudstream3.plugins.Plugin
import android.content.Context
@CloudstreamPlugin
class SuperstreamPlugin: Plugin() {
override fun load(context: Context) {
// All providers should be added in this manner. Please don't edit the providers list directly.
registerMainAPI(Superstream())
}
}

Some files were not shown because too many files have changed in this diff Show More