fix(lib/lycoris&lulu) Repair decode json and work LuluStream #810

Merged
Hayanek merged 14 commits from fix-lycoris into main 2025-04-06 13:41:23 -05:00
4 changed files with 124 additions and 109 deletions

View file

@ -3,16 +3,18 @@ package eu.kanade.tachiyomi.lib.luluextractor
import eu.kanade.tachiyomi.animesource.model.Video import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import okhttp3.Headers import okhttp3.Headers
cuong-tran commented 2025-04-02 11:49:07 -05:00 (Migrated from github.com)
        .add("Referer", "https://luluvdo.com/")
```suggestion .add("Referer", "https://luluvdo.com/") ```
cuong-tran commented 2025-04-02 11:50:18 -05:00 (Migrated from github.com)
            val quality = getResolution(fixedUrl)
```suggestion val quality = getResolution(fixedUrl) ```
import okhttp3.HttpUrl.Companion.toHttpUrl
cuong-tran commented 2025-03-25 21:55:14 -05:00 (Migrated from github.com)

It's exact the same as the old headers, why need a new one?
Also, where it was, outside, is better. Same as the Regex one.

It's exact the same as the old headers, why need a new one? Also, where it was, outside, is better. Same as the Regex one.
Hayanek commented 2025-03-25 22:36:05 -05:00 (Migrated from github.com)

Well, not entirely the same if the current one has a user-agent from the application and the previous form relied solely on these 2 headers.
I don't understand which Regex you mean :(

Well, not entirely the same if the current one has a user-agent from the application and the previous form relied solely on these 2 headers. I don't understand which Regex you mean :(
cuong-tran commented 2025-03-26 03:35:20 -05:00 (Migrated from github.com)

Just change the Headers.Builder() to headers.Builder() then. Keep it outside like you did with the wordRegex previously.
if you meant creating a header based on parameter headers passed in to the function, find where the actual function called and restructure it so the original caller should pass the existed lulu one.

Just change the `Headers.Builder()` to `headers.Builder()` then. Keep it outside like you did with the wordRegex previously. if you meant creating a header based on parameter `headers` passed in to the function, find where the actual function called and restructure it so the original caller should pass the existed lulu one.
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import java.util.regex.Pattern import java.util.regex.Pattern
class LuluExtractor(private val client: OkHttpClient) { class LuluExtractor(private val client: OkHttpClient, headers: Headers) {
private val headers = Headers.Builder() private val headers = headers.newBuilder()
.add("Referer", "https://luluvdo.com") .add("Referer", "https://luluvdo.com/")
.add("Origin", "https://luluvdo.com") .add("Origin", "https://luluvdo.com")
.build() .build()
//Credit: https://github.com/skoruppa/docchi-stremio-addon/blob/main/app/players/lulustream.py
fun videosFromUrl(url: String, prefix: String): List<Video> { fun videosFromUrl(url: String, prefix: String): List<Video> {
val videos = mutableListOf<Video>() val videos = mutableListOf<Video>()
@ -22,7 +24,7 @@ class LuluExtractor(private val client: OkHttpClient) {
val fixedUrl = fixM3u8Link(m3u8Url) val fixedUrl = fixM3u8Link(m3u8Url)
val quality = getResolution(fixedUrl) val quality = getResolution(fixedUrl)
videos.add(Video(fixedUrl, "${prefix}Lulu - $quality", fixedUrl)) videos.add(Video(fixedUrl, "${prefix}Lulu - $quality", fixedUrl, headers))
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
} }
@ -50,34 +52,41 @@ class LuluExtractor(private val client: OkHttpClient) {
private fun fixM3u8Link(link: String): String { private fun fixM3u8Link(link: String): String {
val paramOrder = listOf("t", "s", "e", "f") val paramOrder = listOf("t", "s", "e", "f")
val baseUrl = link.split("?").first() val params = Pattern.compile("[?&]([^=]*)=([^&]*)").matcher(link).let { matcher ->
val params = link.split("?").getOrNull(1)?.split("&") ?: emptyList() generateSequence { if (matcher.find()) matcher.group(1) to matcher.group(2) else null }.toList()
}
val paramMap = mutableMapOf<String, String>() val paramDict = mutableMapOf<String, String>()
val extraParams = mutableMapOf( val extraParams = mutableMapOf<String, String>()
"i" to "0.3",
"sp" to "0"
)
params.forEachIndexed { index, param -> params.forEachIndexed { index, (key , value) ->
val parts = param.split("=") if (key.isNullOrEmpty()) {
when { if (index < paramOrder.size) {
parts.size == 2 -> { if (value != null) {
val (key, value) = parts paramDict[paramOrder[index]] = value
if (key in paramOrder) paramMap[key] = value }
else extraParams[key] = value }
} else {
if (value != null) {
extraParams[key] = value
} }
index < paramOrder.size -> paramMap[paramOrder[index]] = parts.firstOrNull() ?: ""
} }
} }
return buildString { extraParams["i"] = "0.3"
append(baseUrl) extraParams["sp"] = "0"
append("?")
append(paramOrder.joinToString("&") { "$it=${paramMap[it]}" }) val baseUrl = link.split("?")[0]
append("&")
append(extraParams.map { "${it.key}=${it.value}" }.joinToString("&")) val fixedLink = baseUrl.toHttpUrl().newBuilder()
paramOrder.filter { paramDict.containsKey(it) }.forEach { key ->
fixedLink.addQueryParameter(key, paramDict[key])
} }
extraParams.forEach { (key, value) ->
fixedLink.addQueryParameter(key, value)
}
return fixedLink.build().toString()
} }
private fun getResolution(m3u8Url: String): String { private fun getResolution(m3u8Url: String): String {
@ -98,11 +107,10 @@ class LuluExtractor(private val client: OkHttpClient) {
} }
object JavaScriptUnpacker { object JavaScriptUnpacker {
private val UNPACK_REGEX = Regex( private val UNPACK_REGEX by lazy {
"""}\('(.*)', *(\d+), *(\d+), *'(.*?)'\.split\('\|'\)""", Regex("""\}\('(.*)', *(\d+), *(\d+), *'(.*?)'\.split\('\|'\)""",
RegexOption.DOT_MATCHES_ALL RegexOption.DOT_MATCHES_ALL)
) }
fun unpack(encodedJs: String): String? { fun unpack(encodedJs: String): String? {
val match = UNPACK_REGEX.find(encodedJs) ?: return null val match = UNPACK_REGEX.find(encodedJs) ?: return null
val (payload, radixStr, countStr, symtabStr) = match.destructured val (payload, radixStr, countStr, symtabStr) = match.destructured
@ -121,8 +129,8 @@ object JavaScriptUnpacker {
return Regex("""\b\w+\b""").replace(payload) { mr -> return Regex("""\b\w+\b""").replace(payload) { mr ->
symtab.getOrNull(unbase(mr.value, radix, baseDict)) ?: mr.value symtab.getOrNull(unbase(mr.value, radix, baseDict)) ?: mr.value
}.replace("\\", "") }.replace("\\", "")
}
}
private fun unbase(value: String, radix: Int, dict: Map<Char, Int>): Int { private fun unbase(value: String, radix: Int, dict: Map<Char, Int>): Int {
var result = 0 var result = 0
var multiplier = 1 var multiplier = 1

View file

@ -4,13 +4,12 @@ import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import android.util.Base64 import android.util.Base64
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.serialization.json.Json import eu.kanade.tachiyomi.util.parseAs
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import okhttp3.Headers import okhttp3.Headers
import okhttp3.HttpUrl import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import uy.kohesive.injekt.injectLazy
import java.nio.charset.Charset import java.nio.charset.Charset
class LycorisCafeExtractor(private val client: OkHttpClient) { class LycorisCafeExtractor(private val client: OkHttpClient) {
@ -19,7 +18,15 @@ class LycorisCafeExtractor(private val client: OkHttpClient) {
private val GETLNKURL = "https://www.lycoris.cafe/api/watch/getLink" private val GETLNKURL = "https://www.lycoris.cafe/api/watch/getLink"
private val json: Json by injectLazy() private val wordsRegex by lazy {
Regex(
"""\\U([0-9a-fA-F]{8})|""" + // \UXXXXXXXX
"""\\u([0-9a-fA-F]{4})|""" + // \uXXXX
"""\\x([0-9a-fA-F]{2})|""" + // \xHH
"""\\([0-7]{1,3})|""" + // \OOO (octal)
"""\\([btnfr"'$\\])""" // \n, \t, itd.
)
}
// Credit: https://github.com/skoruppa/docchi-stremio-addon/blob/main/app/players/lycoris.py // Credit: https://github.com/skoruppa/docchi-stremio-addon/blob/main/app/players/lycoris.py
cuong-tran commented 2025-03-12 11:51:48 -05:00 (Migrated from github.com)

this will send a string "null" to json.decode and cause a crash

this will send a string `"null"` to json.decode and cause a crash
cuong-tran commented 2025-03-12 11:52:59 -05:00 (Migrated from github.com)

use script.parseAs<ScriptBody>()

use `script.parseAs<ScriptBody>()`
cuong-tran commented 2025-03-12 11:53:47 -05:00 (Migrated from github.com)

same here

same here
cuong-tran commented 2025-03-12 11:54:37 -05:00 (Migrated from github.com)
        val linkList: String? = fetchAndDecodeVideo(client, data.episodeInfo.id.toString(), isSecondary = false)

also fix fetchAndDecodeVideo() & decodeVideoLinks() so they both return String?

```suggestion val linkList: String? = fetchAndDecodeVideo(client, data.episodeInfo.id.toString(), isSecondary = false) ``` also fix `fetchAndDecodeVideo()` & `decodeVideoLinks()` so they both return `String?`
cuong-tran commented 2025-03-12 12:18:42 -05:00 (Migrated from github.com)
        val fhdLink = fetchAndDecodeVideo(client, data.episodeInfo.FHD.toString(), isSecondary = true)
        val sdLink = fetchAndDecodeVideo(client, data.episodeInfo.SD.toString(), isSecondary = true)
        val hdLink = fetchAndDecodeVideo(client, data.episodeInfo.HD.toString(), isSecondary = true)
```suggestion val fhdLink = fetchAndDecodeVideo(client, data.episodeInfo.FHD.toString(), isSecondary = true) val sdLink = fetchAndDecodeVideo(client, data.episodeInfo.SD.toString(), isSecondary = true) val hdLink = fetchAndDecodeVideo(client, data.episodeInfo.HD.toString(), isSecondary = true) ```
cuong-tran commented 2025-03-12 12:27:09 -05:00 (Migrated from github.com)
            if (!fhdLink.isNullOrBlank()) {
```suggestion if (!fhdLink.isNullOrBlank()) { ```
Hayanek commented 2025-03-12 12:49:49 -05:00 (Migrated from github.com)

with this I was aware of it but had no idea how to fix it so that such a problem would not occur

with this I was aware of it but had no idea how to fix it so that such a problem would not occur
cuong-tran commented 2025-03-12 23:47:41 -05:00 (Migrated from github.com)

Add ?. before toString and conditional action if it's null

Add `?.` before `toString` and conditional action if it's null
fun getVideosFromUrl(url: String, headers: Headers, prefix: String): List<Video> { fun getVideosFromUrl(url: String, headers: Headers, prefix: String): List<Video> {
@ -33,72 +40,63 @@ class LycorisCafeExtractor(private val client: OkHttpClient) {
GET(url, headers = embedHeaders), GET(url, headers = embedHeaders),
).execute().asJsoup() ).execute().asJsoup()
val scripts = document.select("script") val script = document.selectFirst("script[type='application/json']")?.data() ?: return emptyList()
val episodeDataPattern = Regex("episodeInfo\\s*:\\s*(\\{.*?\\}),", RegexOption.DOT_MATCHES_ALL) val scriptData = script.parseAs<ScriptBody>()
var episodeData: String? = null
for (script in scripts) { val data = scriptData.body.parseAs<ScriptEpisode>()
val content = script.data()
val match = episodeDataPattern.find(content)
if (match != null) { val linkList = data.episodeInfo.id?.let {
episodeData = match.groupValues[1] fetchAndDecodeVideo(client, data.episodeInfo.id.toString(), isSecondary = false)
break
}
} }
val result = mutableMapOf<String, String?>() val fhdLink = data.episodeInfo.FHD?.let {
cuong-tran commented 2025-03-12 12:15:19 -05:00 (Migrated from github.com)
        } else {
```suggestion } else { ```
cuong-tran commented 2025-03-12 12:21:44 -05:00 (Migrated from github.com)
            val videoLinks = linkList.parseAs<VideoLinksApi>()
```suggestion val videoLinks = linkList.parseAs<VideoLinksApi>() ```
cuong-tran commented 2025-03-12 12:27:35 -05:00 (Migrated from github.com)
            if (!sdLink.isNullOrBlank()) {
```suggestion if (!sdLink.isNullOrBlank()) { ```
cuong-tran commented 2025-03-12 12:28:12 -05:00 (Migrated from github.com)
            }?: fhdLink?.takeIf { it.contains("https://") }?.let {
```suggestion }?: fhdLink?.takeIf { it.contains("https://") }?.let { ```
cuong-tran commented 2025-03-12 12:28:33 -05:00 (Migrated from github.com)
            }?: hdLink?.takeIf { it.contains("https://") }?.let {
```suggestion }?: hdLink?.takeIf { it.contains("https://") }?.let { ```
cuong-tran commented 2025-03-12 12:28:49 -05:00 (Migrated from github.com)
            }?: sdLink?.takeIf { it.contains("https://") }?.let {
```suggestion }?: sdLink?.takeIf { it.contains("https://") }?.let { ```
fetchAndDecodeVideo(client, data.episodeInfo.FHD, isSecondary = true)
val patterns = listOf( }
"id" to Regex("id\\s*:\\s*(\\d+)"), val sdLink = data.episodeInfo.SD?.let {
"FHD" to Regex("FHD\\s*:\\s*\"([^\"]+)\""), fetchAndDecodeVideo(client, data.episodeInfo.SD, isSecondary = true)
"HD" to Regex("HD\\s*:\\s*\"([^\"]+)\""), }
"SD" to Regex("SD\\s*:\\s*\"([^\"]+)\"") val hdLink = data.episodeInfo.HD?.let {
) fetchAndDecodeVideo(client, data.episodeInfo.HD, isSecondary = true)
patterns.forEach { (key, pattern) ->
result[key] = episodeData?.let { pattern.find(it)?.groups?.get(1)?.value }
} }
var linkList: String? = fetchAndDecodeVideo(client, result["id"].toString(), isSecondary = false).toString()
val fhdLink = fetchAndDecodeVideo(client, result["FHD"].toString(), isSecondary = true).toString()
val sdLink = fetchAndDecodeVideo(client, result["SD"].toString(), isSecondary = true).toString()
val hdLink = fetchAndDecodeVideo(client, result["HD"].toString(), isSecondary = true).toString()
if (linkList.isNullOrBlank() || linkList == "{}") { if (linkList.isNullOrBlank() || linkList == "{}") {
if (fhdLink.isNotEmpty()) { if (!fhdLink.isNullOrBlank()) {
videos.add(Video(fhdLink, "${prefix}lycoris.cafe - 1080p", fhdLink)) videos.add(Video(fhdLink, "${prefix}lycoris.cafe - 1080p", fhdLink))
} }
if (hdLink.isNotEmpty()) { if (!hdLink.isNullOrBlank()) {
videos.add(Video(hdLink, "${prefix}lycoris.cafe - 720p", hdLink)) videos.add(Video(hdLink, "${prefix}lycoris.cafe - 720p", hdLink))
} }
if (sdLink.isNotEmpty()) { if (!sdLink.isNullOrBlank()) {
videos.add(Video(sdLink, "${prefix}lycoris.cafe - 480p", sdLink)) videos.add(Video(sdLink, "${prefix}lycoris.cafe - 480p", sdLink))
} }
}else { } else {
val videoLinks = Json.decodeFromString<VideoLinks>(linkList) val videoLinks = linkList.parseAs<VideoLinksApi>()
videoLinks.FHD?.takeIf { checkLinks(client, it) }?.let { videoLinks.FHD?.takeIf { checkLinks(client, it) }?.let {
videos.add(Video(it, "${prefix}lycoris.cafe - 1080p", it)) videos.add(Video(it, "${prefix}lycoris.cafe - 1080p", it))
}?: videos.add(Video(fhdLink, "${prefix}lycoris.cafe - 1080p", fhdLink)) } ?: fhdLink?.takeIf { checkLinks(client, it) }?.let {
videos.add(Video(it, "${prefix}lycoris.cafe - 1080p", it))
}
videoLinks.HD?.takeIf { checkLinks(client, it) }?.let { videoLinks.HD?.takeIf { checkLinks(client, it) }?.let {
videos.add(Video(it, "${prefix}lycoris.cafe - 720p", it)) videos.add(Video(it, "${prefix}lycoris.cafe - 720p", it))
}?: videos.add(Video(hdLink, "${prefix}lycoris.cafe - 720p", hdLink)) } ?: hdLink?.takeIf { checkLinks(client, it) }?.let {
videos.add(Video(it, "${prefix}lycoris.cafe - 720p", it))
}
videoLinks.SD?.takeIf { checkLinks(client, it) }?.let { videoLinks.SD?.takeIf { checkLinks(client, it) }?.let {
videos.add(Video(it, "${prefix}lycoris.cafe - 480p", it)) videos.add(Video(it, "${prefix}lycoris.cafe - 480p", it))
}?: videos.add(Video(sdLink, "${prefix}lycoris.cafe - 480p", sdLink)) } ?: sdLink?.takeIf { checkLinks(client, it) }?.let {
videos.add(Video(it, "${prefix}lycoris.cafe - 480p", it))
}
} }
return videos return videos
} }
private fun decodeVideoLinks(encodedUrl: String?): Any? { private fun decodeVideoLinks(encodedUrl: String): String? {
if (encodedUrl.isNullOrEmpty()) { if (encodedUrl.isBlank()) {
return null return null
} }
@ -121,31 +119,33 @@ class LycorisCafeExtractor(private val client: OkHttpClient) {
} }
} }
private fun fetchAndDecodeVideo(client: OkHttpClient, episodeId: String, isSecondary: Boolean = false): Any? { private fun fetchAndDecodeVideo(client: OkHttpClient, episodeId: String, isSecondary: Boolean = false): String? {
val url: HttpUrl val url: HttpUrl
if (isSecondary) { if (isSecondary) {
val convertedText = episodeId.toByteArray(Charset.forName("UTF-8")).toString(Charset.forName("ISO-8859-1")) val convertedText = episodeId.toByteArray(Charset.forName("UTF-8")).toString(Charset.forName("ISO-8859-1"))
val unicodeEscape = decodePythonEscape(convertedText) val unicodeEscape = decodePythonEscape(convertedText)
val finalText = unicodeEscape.toByteArray(Charsets.ISO_8859_1).toString(Charsets.UTF_8) val finalText = unicodeEscape.toByteArray(Charsets.ISO_8859_1).toString(Charsets.UTF_8)
url = GETLNKURL.toHttpUrl().newBuilder() url = GETLNKURL.toHttpUrl().newBuilder()
?.addQueryParameter("link", finalText) .addQueryParameter("link", finalText)
?.build() ?: throw IllegalStateException("Invalid URL") .build()
} else { } else {
url = GETSECONDARYURL.toHttpUrl().newBuilder() url = GETSECONDARYURL.toHttpUrl().newBuilder()
?.addQueryParameter("id", episodeId) .addQueryParameter("id", episodeId)
?.build() ?: throw IllegalStateException("Invalid URL") .build()
}
client.newCall(GET(url))
.execute()
.use { response ->
val data = response.body.string()
return decodeVideoLinks(data)
} }
client.newCall(GET(url))
.execute()
.use { response ->
val data = response.body.string() ?: ""
return decodeVideoLinks(data)
}
} }
private fun checkLinks(client: OkHttpClient, link: String): Boolean { private fun checkLinks(client: OkHttpClient, link: String): Boolean {
if (!link.contains("https://")) return false
cuong-tran commented 2025-03-24 01:06:48 -05:00 (Migrated from github.com)

Move regex outside and make it lazy load

Move regex outside and make it lazy load
Hayanek commented 2025-03-24 10:42:33 -05:00 (Migrated from github.com)

I'll be honest I have no idea why, and how I could do it.

I'll be honest I have no idea why, and how I could do it.
cuong-tran commented 2025-03-24 15:26:12 -05:00 (Migrated from github.com)

move out so it's only needed to be initialized once, not every time the function is called.

Example:

private val wordRegex by lazy { Regex("""\w+""") }
move out so it's only needed to be initialized once, not every time the function is called. Example: ```kotlin private val wordRegex by lazy { Regex("""\w+""") } ```
Hayanek commented 2025-03-24 16:10:12 -05:00 (Migrated from github.com)

Will it be good this way?

private val wordsRegex by lazy { 
        Regex(
        """\\U([0-9a-fA-F]{8})|""" +     // \UXXXXXXXX
            """\\u([0-9a-fA-F]{4})|""" +     // \uXXXX
            """\\x([0-9a-fA-F]{2})|""" +     // \xHH
            """\\([0-7]{1,3})|""" +          // \OOO (octal)
            """\\([btnfr"'$\\])"""         // \n, \t, itd.
        ) 
    }

or do I have to knock them all down one by one :(

Will it be good this way? ```kotlin private val wordsRegex by lazy { Regex( """\\U([0-9a-fA-F]{8})|""" + // \UXXXXXXXX """\\u([0-9a-fA-F]{4})|""" + // \uXXXX """\\x([0-9a-fA-F]{2})|""" + // \xHH """\\([0-7]{1,3})|""" + // \OOO (octal) """\\([btnfr"'$\\])""" // \n, \t, itd. ) } ``` or do I have to knock them all down one by one :(
cuong-tran commented 2025-03-24 22:24:12 -05:00 (Migrated from github.com)

Just move the whole thing out like that is OK

Just move the whole thing out like that is OK
client.newCall(GET(link)).execute().use { response -> client.newCall(GET(link)).execute().use { response ->
return response.code.toString() == "200" return response.code.toString() == "200"
} }
@ -155,16 +155,7 @@ class LycorisCafeExtractor(private val client: OkHttpClient) {
// 1. Obsługa kontynuacji linii (backslash + newline) // 1. Obsługa kontynuacji linii (backslash + newline)
val withoutLineContinuation = text.replace("\\\n", "") val withoutLineContinuation = text.replace("\\\n", "")
// 2. Regex do wykrywania wszystkich sekwencji escape return wordsRegex.replace(withoutLineContinuation) { match ->
val regex = Regex(
"""\\U([0-9a-fA-F]{8})|""" + // \UXXXXXXXX
"""\\u([0-9a-fA-F]{4})|""" + // \uXXXX
"""\\x([0-9a-fA-F]{2})|""" + // \xHH
"""\\([0-7]{1,3})|""" + // \OOO (octal)
"""\\([btnfr"'$\\\\])""" // \n, \t, itd.
)
return regex.replace(withoutLineContinuation) { match ->
val (u8, u4, x2, octal, simple) = match.destructured val (u8, u4, x2, octal, simple) = match.destructured
when { when {
u8.isNotEmpty() -> handleUnicode8(u8) u8.isNotEmpty() -> handleUnicode8(u8)
@ -208,16 +199,32 @@ class LycorisCafeExtractor(private val client: OkHttpClient) {
} }
@Serializable @Serializable
data class VideoLinks( data class ScriptBody(
val body: String
)
@Serializable
data class ScriptEpisode(
val episodeInfo: EpisodeInfo
)
@Serializable
data class EpisodeInfo(
val id: Int? = null,
val FHD: String? = null,
val HD: String? = null,
val SD: String? = null,
)
@Serializable
data class VideoLinksApi(
val HD: String? = null, val HD: String? = null,
val SD: String? = null, val SD: String? = null,
val FHD: String? = null, val FHD: String? = null,
val Source: String? = null, val Source: String? = null,
val preview: String? = null,
val SourceMKV: String? = null val SourceMKV: String? = null
) )
} }

View file

@ -1,7 +1,7 @@
ext { ext {
extName = 'Docchi' extName = 'Docchi'
extClass = '.Docchi' extClass = '.Docchi'
extVersionCode = 2 extVersionCode = 3
isNsfw = true isNsfw = true
} }

View file

@ -137,7 +137,7 @@ class Docchi : ConfigurableAnimeSource, AnimeHttpSource() {
private val sibnetExtractor by lazy { SibnetExtractor(client) } private val sibnetExtractor by lazy { SibnetExtractor(client) }
private val doodExtractor by lazy { DoodExtractor(client) } private val doodExtractor by lazy { DoodExtractor(client) }
private val lycorisExtractor by lazy { LycorisCafeExtractor(client) } private val lycorisExtractor by lazy { LycorisCafeExtractor(client) }
private val luluExtractor by lazy { LuluExtractor(client) } private val luluExtractor by lazy { LuluExtractor(client, headers) }
private val googledriveExtractor by lazy { GoogleDriveExtractor(client, headers) } private val googledriveExtractor by lazy { GoogleDriveExtractor(client, headers) }
override fun videoListParse(response: Response): List<Video> { override fun videoListParse(response: Response): List<Video> {
@ -278,7 +278,7 @@ class Docchi : ConfigurableAnimeSource, AnimeHttpSource() {
val genres: List<String>, val genres: List<String>,
val broadcast_day: String?, val broadcast_day: String?,
val aired_from: String?, val aired_from: String?,
val episodes: Int, val episodes: Int?,
val season: String, val season: String,
val season_year: Int, val season_year: Int,
val series_type: String, val series_type: String,
@ -294,7 +294,7 @@ class Docchi : ConfigurableAnimeSource, AnimeHttpSource() {
val cover: String, val cover: String,
val adult_content: String, val adult_content: String,
val series_type: String, val series_type: String,
val episodes: Int, val episodes: Int?,
val season: String, val season: String,
val season_year: Int, val season_year: Int,
) )
@ -315,7 +315,7 @@ class Docchi : ConfigurableAnimeSource, AnimeHttpSource() {
val genres: List<String>, val genres: List<String>,
val broadcast_day: String?, val broadcast_day: String?,
val aired_from: String?, val aired_from: String?,
val episodes: Int, val episodes: Int?,
val season: String, val season: String,
val season_year: Int, val season_year: Int,
val series_type: String, val series_type: String,