Rename and fix Crunchyroll

This commit is contained in:
Blatzar 2022-08-25 13:44:13 +02:00
parent 0b88ee63e4
commit 4c3d741a7c
6 changed files with 112 additions and 108 deletions

View file

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

View file

@ -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 }
}
}
}

View file

@ -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)")

View file

@ -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())

View file

@ -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
}
}