mirror of
https://github.com/recloudstream/cloudstream-extensions.git
synced 2024-08-15 03:03:54 +00:00
Rename and fix Crunchyroll
This commit is contained in:
parent
0b88ee63e4
commit
4c3d741a7c
6 changed files with 112 additions and 108 deletions
|
@ -1,5 +1,5 @@
|
||||||
// use an integer for version numbers
|
// use an integer for version numbers
|
||||||
version = 2
|
version = 3
|
||||||
|
|
||||||
|
|
||||||
cloudstream {
|
cloudstream {
|
|
@ -0,0 +1,46 @@
|
||||||
|
package com.lagradost
|
||||||
|
|
||||||
|
import com.lagradost.nicehttp.Requests
|
||||||
|
import okhttp3.*
|
||||||
|
import okhttp3.internal.parseCookie
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An HTTP session manager.
|
||||||
|
*
|
||||||
|
* This class simply keeps cookies across requests. No security about which site should use which cookies.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
class CustomSession(
|
||||||
|
client: OkHttpClient
|
||||||
|
) : Requests() {
|
||||||
|
var cookies = mutableMapOf<String, Cookie>()
|
||||||
|
|
||||||
|
init {
|
||||||
|
this.baseClient = client
|
||||||
|
.newBuilder()
|
||||||
|
.addInterceptor {
|
||||||
|
val time = System.currentTimeMillis()
|
||||||
|
val request = it.request()
|
||||||
|
request.headers.forEach { header ->
|
||||||
|
if (header.first.equals("cookie", ignoreCase = true)) {
|
||||||
|
val cookie = parseCookie(time, request.url, header.second) ?: return@forEach
|
||||||
|
cookies += cookie.name to cookie
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it.proceed(request)
|
||||||
|
}
|
||||||
|
.cookieJar(CustomCookieJar())
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class CustomCookieJar : CookieJar {
|
||||||
|
override fun loadForRequest(url: HttpUrl): List<Cookie> {
|
||||||
|
return this@CustomSession.cookies.values.toList()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun saveFromResponse(url: HttpUrl, cookies: List<Cookie>) {
|
||||||
|
this@CustomSession.cookies += cookies.map { it.name to it }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,14 +2,15 @@ package com.lagradost
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty
|
import com.fasterxml.jackson.annotation.JsonProperty
|
||||||
import com.lagradost.cloudstream3.*
|
import com.lagradost.cloudstream3.*
|
||||||
|
import com.lagradost.cloudstream3.APIHolder.capitalize
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
|
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
|
||||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||||
import com.lagradost.cloudstream3.utils.M3u8Helper
|
import com.lagradost.cloudstream3.utils.M3u8Helper
|
||||||
import com.lagradost.cloudstream3.utils.Qualities
|
import com.lagradost.cloudstream3.utils.Qualities
|
||||||
import com.lagradost.cloudstream3.utils.getQualityFromName
|
import com.lagradost.cloudstream3.utils.getQualityFromName
|
||||||
import com.lagradost.nicehttp.NiceResponse
|
import com.lagradost.nicehttp.NiceResponse
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
import org.jsoup.Jsoup
|
import org.jsoup.Jsoup
|
||||||
import java.net.URI
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
private fun String.toAscii() = this.map { it.code }.joinToString()
|
private fun String.toAscii() = this.map { it.code }.joinToString()
|
||||||
|
@ -18,14 +19,16 @@ class KrunchyGeoBypasser {
|
||||||
companion object {
|
companion object {
|
||||||
const val BYPASS_SERVER = "https://cr-unblocker.us.to/start_session"
|
const val BYPASS_SERVER = "https://cr-unblocker.us.to/start_session"
|
||||||
val headers = mapOf(
|
val headers = mapOf(
|
||||||
"Accept" to "*/*",
|
"accept" to "*/*",
|
||||||
"Accept-Encoding" to "gzip, deflate",
|
// "Accept-Encoding" to "gzip, deflate",
|
||||||
"Connection" to "keep-alive",
|
"connection" to "keep-alive",
|
||||||
"Referer" to "https://google.com/",
|
// "Referer" to "https://google.com/",
|
||||||
"User-Agent" to "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36".toAscii()
|
"user-agent" to "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36".toAscii()
|
||||||
)
|
)
|
||||||
var sessionId: String? = null
|
var sessionId: String? = null
|
||||||
val session = HttpSession()
|
|
||||||
|
// val interceptor = CookieInterceptor()
|
||||||
|
val session = CustomSession(app.baseClient)
|
||||||
}
|
}
|
||||||
|
|
||||||
data class KrunchySession(
|
data class KrunchySession(
|
||||||
|
@ -54,6 +57,8 @@ class KrunchyGeoBypasser {
|
||||||
private suspend fun autoLoadSession(): Boolean {
|
private suspend fun autoLoadSession(): Boolean {
|
||||||
if (sessionId != null) return true
|
if (sessionId != null) return true
|
||||||
getSessionId()
|
getSessionId()
|
||||||
|
// Do not spam the api!
|
||||||
|
delay(3000)
|
||||||
return autoLoadSession()
|
return autoLoadSession()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,6 +74,7 @@ class KrunchyProvider : MainAPI() {
|
||||||
val episodeNumRegex = Regex("""Episode (\d+)""")
|
val episodeNumRegex = Regex("""Episode (\d+)""")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Do not make https! It will fail!
|
||||||
override var mainUrl = "http://www.crunchyroll.com"
|
override var mainUrl = "http://www.crunchyroll.com"
|
||||||
override var name: String = "Crunchyroll"
|
override var name: String = "Crunchyroll"
|
||||||
override var lang = "en"
|
override var lang = "en"
|
||||||
|
@ -87,13 +93,15 @@ class KrunchyProvider : MainAPI() {
|
||||||
)
|
)
|
||||||
|
|
||||||
override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse {
|
override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse {
|
||||||
|
println("GETMAINPAGE ")
|
||||||
val categoryData = request.data
|
val categoryData = request.data
|
||||||
|
|
||||||
val paginated = categoryData.endsWith("=")
|
val paginated = categoryData.endsWith("=")
|
||||||
val pagedLink = if (paginated) categoryData + page else categoryData
|
val pagedLink = if (paginated) categoryData + page else categoryData
|
||||||
val items = mutableListOf<HomePageList>()
|
val items = mutableListOf<HomePageList>()
|
||||||
|
|
||||||
// Only fetch page at first-time load of homepage
|
// Only fetch page at first-time load of homepage
|
||||||
if (page <= 1) {
|
if (page <= 1 && request.name == "Popular") {
|
||||||
val doc = Jsoup.parse(crUnblock.geoBypassRequest(mainUrl).text)
|
val doc = Jsoup.parse(crUnblock.geoBypassRequest(mainUrl).text)
|
||||||
val featured = doc.select(".js-featured-show-list > li").mapNotNull { anime ->
|
val featured = doc.select(".js-featured-show-list > li").mapNotNull { anime ->
|
||||||
val url =
|
val url =
|
||||||
|
@ -145,7 +153,9 @@ class KrunchyProvider : MainAPI() {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
items.add(HomePageList("Featured", featured))
|
if (featured.isNotEmpty()) {
|
||||||
|
items.add(HomePageList("Featured", featured))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (paginated || !paginated && page <= 1) {
|
if (paginated || !paginated && page <= 1) {
|
||||||
|
@ -176,25 +186,23 @@ class KrunchyProvider : MainAPI() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (items.isNotEmpty()) {
|
if (items.isNotEmpty()) {
|
||||||
return newHomePageResponse(items.toList()
|
return newHomePageResponse(items)
|
||||||
)
|
|
||||||
}
|
}
|
||||||
throw ErrorLoadingException()
|
throw ErrorLoadingException()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getCloseMatches(sequence: String, items: Collection<String>): ArrayList<String> {
|
// Maybe fuzzy match in the future
|
||||||
val closeMatches = ArrayList<String>()
|
private fun getCloseMatches(sequence: String, items: Collection<String>): List<String> {
|
||||||
val a = sequence.trim().lowercase()
|
val a = sequence.trim().lowercase()
|
||||||
|
|
||||||
for (item in items) {
|
return items.mapNotNull { item ->
|
||||||
val b = item.trim().lowercase()
|
val b = item.trim().lowercase()
|
||||||
if (b.contains(a)) {
|
if (b.contains(a))
|
||||||
closeMatches.add(item)
|
item
|
||||||
} else if (a.contains(b)) {
|
else if (a.contains(b))
|
||||||
closeMatches.add(item)
|
item
|
||||||
}
|
else null
|
||||||
}
|
}
|
||||||
return closeMatches
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private data class CrunchyAnimeData(
|
private data class CrunchyAnimeData(
|
||||||
|
@ -261,7 +269,7 @@ class KrunchyProvider : MainAPI() {
|
||||||
}
|
}
|
||||||
|
|
||||||
val genres = soup.select(".large-margin-bottom > ul:nth-child(2) li:nth-child(2) a")
|
val genres = soup.select(".large-margin-bottom > ul:nth-child(2) li:nth-child(2) a")
|
||||||
.map { it.text() }
|
.map { it.text().capitalize() }
|
||||||
val year = genres.filter { it.toIntOrNull() != null }.map { it.toInt() }.sortedBy { it }
|
val year = genres.filter { it.toIntOrNull() != null }.map { it.toInt() }.sortedBy { it }
|
||||||
.getOrNull(0)
|
.getOrNull(0)
|
||||||
|
|
||||||
|
@ -364,7 +372,19 @@ class KrunchyProvider : MainAPI() {
|
||||||
@JsonProperty("url") val url: String,
|
@JsonProperty("url") val url: String,
|
||||||
@JsonProperty("resolution") val resolution: String?,
|
@JsonProperty("resolution") val resolution: String?,
|
||||||
@JsonProperty("title") var title: String?
|
@JsonProperty("title") var title: String?
|
||||||
)
|
) {
|
||||||
|
fun title(): String {
|
||||||
|
return when {
|
||||||
|
this.hardsubLang == "enUS" && this.audioLang == "jaJP" -> "Hardsub (English)"
|
||||||
|
this.hardsubLang == "esLA" && this.audioLang == "jaJP" -> "Hardsub (Latino)"
|
||||||
|
this.hardsubLang == "esES" && this.audioLang == "jaJP" -> "Hardsub (Español España)"
|
||||||
|
this.audioLang == "esLA" -> "Latino"
|
||||||
|
this.audioLang == "esES" -> "Español España"
|
||||||
|
this.audioLang == "enUS" -> "English (US)"
|
||||||
|
else -> "RAW"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
data class KrunchyVideo(
|
data class KrunchyVideo(
|
||||||
@JsonProperty("streams") val streams: List<Streams>,
|
@JsonProperty("streams") val streams: List<Streams>,
|
||||||
|
@ -387,6 +407,7 @@ class KrunchyProvider : MainAPI() {
|
||||||
if (!dat.isNullOrEmpty()) {
|
if (!dat.isNullOrEmpty()) {
|
||||||
val json = parseJson<KrunchyVideo>(dat)
|
val json = parseJson<KrunchyVideo>(dat)
|
||||||
val streams = ArrayList<Streams>()
|
val streams = ArrayList<Streams>()
|
||||||
|
|
||||||
for (stream in json.streams) {
|
for (stream in json.streams) {
|
||||||
if (
|
if (
|
||||||
listOf(
|
listOf(
|
||||||
|
@ -408,21 +429,13 @@ class KrunchyProvider : MainAPI() {
|
||||||
"enUS",
|
"enUS",
|
||||||
null
|
null
|
||||||
).contains(stream.hardsubLang))
|
).contains(stream.hardsubLang))
|
||||||
&& URI(stream.url).path.endsWith(".m3u")
|
// && URI(stream.url).path.endsWith(".m3u")
|
||||||
|
|
||||||
) {
|
) {
|
||||||
stream.title =
|
stream.title = stream.title()
|
||||||
if (stream.hardsubLang == "enUS" && stream.audioLang == "jaJP") "Hardsub (English)"
|
|
||||||
else if (stream.hardsubLang == "esLA" && stream.audioLang == "jaJP") "Hardsub (Latino)"
|
|
||||||
else if (stream.hardsubLang == "esES" && stream.audioLang == "jaJP") "Hardsub (Español España)"
|
|
||||||
else if (stream.audioLang == "esLA") "Latino"
|
|
||||||
else if (stream.audioLang == "esES") "Español España"
|
|
||||||
else if (stream.audioLang == "enUS") "English (US)"
|
|
||||||
else "RAW"
|
|
||||||
streams.add(stream)
|
streams.add(stream)
|
||||||
}
|
}
|
||||||
//Premium eps
|
// Premium eps
|
||||||
if (stream.format == "trailer_hls" && listOf(
|
else if (stream.format == "trailer_hls" && listOf(
|
||||||
"jaJP",
|
"jaJP",
|
||||||
"esLA",
|
"esLA",
|
||||||
"esES",
|
"esES",
|
||||||
|
@ -430,34 +443,29 @@ class KrunchyProvider : MainAPI() {
|
||||||
).contains(stream.audioLang) &&
|
).contains(stream.audioLang) &&
|
||||||
(listOf("esLA", "esES", "enUS", null).contains(stream.hardsubLang))
|
(listOf("esLA", "esES", "enUS", null).contains(stream.hardsubLang))
|
||||||
) {
|
) {
|
||||||
stream.title =
|
stream.title = stream.title()
|
||||||
if (stream.hardsubLang == "enUS" && stream.audioLang == "jaJP") "Hardsub (English)"
|
|
||||||
else if (stream.hardsubLang == "esLA" && stream.audioLang == "jaJP") "Hardsub (Latino)"
|
|
||||||
else if (stream.hardsubLang == "esES" && stream.audioLang == "jaJP") "Hardsub (Español España)"
|
|
||||||
else if (stream.audioLang == "esLA") "Latino"
|
|
||||||
else if (stream.audioLang == "esES") "Español España"
|
|
||||||
else if (stream.audioLang == "enUS") "English (US)"
|
|
||||||
else "RAW"
|
|
||||||
streams.add(stream)
|
streams.add(stream)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
streams.apmap { stream ->
|
streams.apmap { stream ->
|
||||||
if (stream.url.contains("m3u8") && stream.format!!.contains("adaptive")) {
|
if (stream.url.contains("m3u8") && stream.format!!.contains("adaptive")) {
|
||||||
hlsHelper.m3u8Generation(M3u8Helper.M3u8Stream(stream.url, null), false).apmap {
|
hlsHelper.m3u8Generation(M3u8Helper.M3u8Stream(stream.url, null), false)
|
||||||
callback(
|
.forEach {
|
||||||
ExtractorLink(
|
callback(
|
||||||
"Crunchyroll",
|
ExtractorLink(
|
||||||
"Crunchy - ${stream.title} - ${it.quality}p",
|
"Crunchyroll",
|
||||||
it.streamUrl,
|
"Crunchy - ${stream.title}",
|
||||||
"",
|
it.streamUrl,
|
||||||
getQualityFromName(it.quality.toString()),
|
"",
|
||||||
true
|
getQualityFromName(it.quality.toString()),
|
||||||
|
true
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
}
|
||||||
}
|
|
||||||
} else if (stream.format == "trailer_hls") {
|
} else if (stream.format == "trailer_hls") {
|
||||||
val premiumstream = stream.url
|
val premiumStream = stream.url
|
||||||
.replace("\\/", "/")
|
.replace("\\/", "/")
|
||||||
.replace(Regex("\\/clipFrom.*?index.m3u8"), "").replace("'_,'", "'_'")
|
.replace(Regex("\\/clipFrom.*?index.m3u8"), "").replace("'_,'", "'_'")
|
||||||
.replace(stream.url.split("/")[2], "fy.v.vrv.co")
|
.replace(stream.url.split("/")[2], "fy.v.vrv.co")
|
||||||
|
@ -465,7 +473,7 @@ class KrunchyProvider : MainAPI() {
|
||||||
ExtractorLink(
|
ExtractorLink(
|
||||||
this.name,
|
this.name,
|
||||||
"Crunchy - ${stream.title} ★",
|
"Crunchy - ${stream.title} ★",
|
||||||
premiumstream,
|
premiumStream,
|
||||||
"",
|
"",
|
||||||
Qualities.Unknown.value,
|
Qualities.Unknown.value,
|
||||||
false
|
false
|
||||||
|
@ -473,7 +481,7 @@ class KrunchyProvider : MainAPI() {
|
||||||
)
|
)
|
||||||
} else null
|
} else null
|
||||||
}
|
}
|
||||||
json.subtitles.apmap {
|
json.subtitles.forEach {
|
||||||
val langclean = it.language.replace("esLA", "Spanish")
|
val langclean = it.language.replace("esLA", "Spanish")
|
||||||
.replace("enUS", "English")
|
.replace("enUS", "English")
|
||||||
.replace("esES", "Spanish (Spain)")
|
.replace("esES", "Spanish (Spain)")
|
|
@ -6,7 +6,7 @@ import com.lagradost.cloudstream3.plugins.Plugin
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
|
||||||
@CloudstreamPlugin
|
@CloudstreamPlugin
|
||||||
class KrunchyProviderPlugin: Plugin() {
|
class CrunchyrollProviderPlugin: Plugin() {
|
||||||
override fun load(context: Context) {
|
override fun load(context: Context) {
|
||||||
// All providers should be added in this manner. Please don't edit the providers list directly.
|
// All providers should be added in this manner. Please don't edit the providers list directly.
|
||||||
registerMainAPI(KrunchyProvider())
|
registerMainAPI(KrunchyProvider())
|
|
@ -1,50 +0,0 @@
|
||||||
package com.lagradost
|
|
||||||
//Credits https://github.com/ArjixWasTaken/CloudStream-3/blob/master/app/src/main/java/com/ArjixWasTaken/cloudstream3/utils/HttpSession.kt
|
|
||||||
import com.lagradost.cloudstream3.app
|
|
||||||
import com.lagradost.nicehttp.NiceResponse
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An HTTP session manager.
|
|
||||||
*
|
|
||||||
* This class simply keeps cookies across requests.
|
|
||||||
*
|
|
||||||
* @property sessionCookies A cookie jar.
|
|
||||||
*
|
|
||||||
* TODO: be replaced with built in Session once that works as it should.
|
|
||||||
*/
|
|
||||||
class HttpSession {
|
|
||||||
private val sessionCookies: MutableMap<String, String> = mutableMapOf()
|
|
||||||
|
|
||||||
suspend fun get(
|
|
||||||
url: String,
|
|
||||||
headers: Map<String, String> = mapOf(),
|
|
||||||
cookies: Map<String, String> = mapOf(),
|
|
||||||
): NiceResponse {
|
|
||||||
sessionCookies.putAll(cookies)
|
|
||||||
val res =
|
|
||||||
app.get(
|
|
||||||
url,
|
|
||||||
headers,
|
|
||||||
cookies = sessionCookies,
|
|
||||||
)
|
|
||||||
sessionCookies.putAll(res.headers.filter { it.first.lowercase() == "set-cookie" })
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun post(
|
|
||||||
url: String,
|
|
||||||
headers: Map<String, String> = mapOf(),
|
|
||||||
cookies: Map<String, String> = mapOf(),
|
|
||||||
): NiceResponse {
|
|
||||||
sessionCookies.putAll(cookies)
|
|
||||||
val res =
|
|
||||||
app.post(
|
|
||||||
url,
|
|
||||||
headers,
|
|
||||||
cookies = sessionCookies,
|
|
||||||
)
|
|
||||||
|
|
||||||
sessionCookies.putAll(res.headers.filter { it.first.lowercase() == "set-cookie" })
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in a new issue