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
2 changed files with 19 additions and 15 deletions
Showing only changes of commit 9a8ea456b4 - Show all commits

View file

@ -5,13 +5,11 @@ 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 eu.kanade.tachiyomi.util.parseAs import eu.kanade.tachiyomi.util.parseAs
import kotlinx.serialization.json.Json
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) {
@ -20,8 +18,6 @@ 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()
// 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
fun getVideosFromUrl(url: String, headers: Headers, prefix: String): List<Video> { fun getVideosFromUrl(url: String, headers: Headers, prefix: String): List<Video> {
@ -34,17 +30,25 @@ class LycorisCafeExtractor(private val client: OkHttpClient) {
GET(url, headers = embedHeaders), GET(url, headers = embedHeaders),
).execute().asJsoup() ).execute().asJsoup()
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
val script = document.select("script[type='application/json']").first()?.data()?.toString() ?: return emptyList() val script = document.selectFirst("script[type='application/json']")?.data() ?: return emptyList()
cuong-tran commented 2025-03-13 08:15:11 -05:00 (Migrated from github.com)
    private fun decodeVideoLinks(encodedUrl: String): String? {
        if (encodedUrl.isBlank()) {
```suggestion private fun decodeVideoLinks(encodedUrl: String): String? { if (encodedUrl.isBlank()) { ```
cuong-tran commented 2025-03-24 00:41:52 -05:00 (Migrated from github.com)
        val script = document.selectFirst("script[type='application/json']")?.data() ?: return emptyList()
```suggestion val script = document.selectFirst("script[type='application/json']")?.data() ?: return emptyList() ```
cuong-tran commented 2025-03-24 01:03:44 -05:00 (Migrated from github.com)
        val linkList = data.episodeInfo.id?.let { fetchAndDecodeVideo(client, data.episodeInfo.id.toString(), isSecondary = false) }

        val fhdLink = data.episodeInfo.FHD?.let { fetchAndDecodeVideo(client, data.episodeInfo.FHD, isSecondary = true) }
        val sdLink = data.episodeInfo.SD?.let { fetchAndDecodeVideo(client, data.episodeInfo.SD, isSecondary = true) }
        val hdLink = data.episodeInfo.HD?.let { fetchAndDecodeVideo(client, data.episodeInfo.HD, isSecondary = true) }
```suggestion val linkList = data.episodeInfo.id?.let { fetchAndDecodeVideo(client, data.episodeInfo.id.toString(), isSecondary = false) } val fhdLink = data.episodeInfo.FHD?.let { fetchAndDecodeVideo(client, data.episodeInfo.FHD, isSecondary = true) } val sdLink = data.episodeInfo.SD?.let { fetchAndDecodeVideo(client, data.episodeInfo.SD, isSecondary = true) } val hdLink = data.episodeInfo.HD?.let { fetchAndDecodeVideo(client, data.episodeInfo.HD, isSecondary = true) } ```
cuong-tran commented 2025-03-24 01:04:12 -05:00 (Migrated from github.com)
```suggestion ```
cuong-tran commented 2025-03-24 01:04:54 -05:00 (Migrated from github.com)
            } ?: hdLink?.takeIf { it.contains("https://") }?.let {
                videos.add(Video(it, "${prefix}lycoris.cafe - 720p", it))
            }
```suggestion } ?: hdLink?.takeIf { it.contains("https://") }?.let { videos.add(Video(it, "${prefix}lycoris.cafe - 720p", it)) } ```
cuong-tran commented 2025-03-24 01:05:40 -05:00 (Migrated from github.com)
            } ?: sdLink?.takeIf { it.contains("https://") }?.let {
```suggestion } ?: sdLink?.takeIf { it.contains("https://") }?.let { ```
cuong-tran commented 2025-03-24 01:05:47 -05:00 (Migrated from github.com)

could we use checkLinks() instead of it.contains()?

could we use `checkLinks()` instead of `it.contains()`?
cuong-tran commented 2025-03-24 01:05:55 -05:00 (Migrated from github.com)

could we use checkLinks() instead of it.contains()?

could we use `checkLinks()` instead of `it.contains()`?
val scriptData = script.parseAs<ScriptBody>() val scriptData = script.parseAs<ScriptBody>()
val data = scriptData.body.parseAs<ScriptEpisode>() val data = scriptData.body.parseAs<ScriptEpisode>()
val linkList: String? = fetchAndDecodeVideo(client, data.episodeInfo.id.toString(), isSecondary = false) val linkList = data.episodeInfo.id?.let {
fetchAndDecodeVideo(client, data.episodeInfo.id.toString(), isSecondary = false)
}
val fhdLink = fetchAndDecodeVideo(client, data.episodeInfo.FHD.toString(), isSecondary = true) val fhdLink = data.episodeInfo.FHD?.let {
val sdLink = fetchAndDecodeVideo(client, data.episodeInfo.SD.toString(), isSecondary = true) fetchAndDecodeVideo(client, data.episodeInfo.FHD, isSecondary = true)
val hdLink = fetchAndDecodeVideo(client, data.episodeInfo.HD.toString(), isSecondary = true) }
val sdLink = data.episodeInfo.SD?.let {
fetchAndDecodeVideo(client, data.episodeInfo.SD, isSecondary = true)
}
val hdLink = data.episodeInfo.HD?.let {
fetchAndDecodeVideo(client, data.episodeInfo.HD, isSecondary = true)
}
if (linkList.isNullOrBlank() || linkList == "{}") { if (linkList.isNullOrBlank() || linkList == "{}") {
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 { ```
if (!fhdLink.isNullOrBlank()) { if (!fhdLink.isNullOrBlank()) {
@ -56,25 +60,24 @@ class LycorisCafeExtractor(private val client: OkHttpClient) {
if (!sdLink.isNullOrBlank()) { 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 = linkList.parseAs<VideoLinksApi>() 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))
}?: fhdLink?.takeIf { it.contains("https://") }?.let { } ?: fhdLink?.takeIf { checkLinks(client, it) }?.let {
videos.add(Video(it, "${prefix}lycoris.cafe - 1080p", it)) 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))
}?: hdLink?.takeIf { it.contains("https://") }?.let { } ?: hdLink?.takeIf { checkLinks(client, it) }?.let {
videos.add(Video(it, "${prefix}lycoris.cafe - 720p", it)) 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))
}?: sdLink?.takeIf { it.contains("https://") }?.let { } ?: sdLink?.takeIf { checkLinks(client, it) }?.let {
videos.add(Video(it, "${prefix}lycoris.cafe - 480p", it)) videos.add(Video(it, "${prefix}lycoris.cafe - 480p", it))
} }
} }
@ -125,7 +128,7 @@ class LycorisCafeExtractor(private val client: OkHttpClient) {
client.newCall(GET(url)) client.newCall(GET(url))
.execute() .execute()
.use { response -> .use { response ->
val data = response.body.string() ?: "" val data = response.body.string()
return decodeVideoLinks(data) return decodeVideoLinks(data)
} }
} }
@ -142,6 +145,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", "")
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
// 2. Regex do wykrywania wszystkich sekwencji escape // 2. Regex do wykrywania wszystkich sekwencji escape
val regex = Regex( val regex = Regex(
"""\\U([0-9a-fA-F]{8})|""" + // \UXXXXXXXX """\\U([0-9a-fA-F]{8})|""" + // \UXXXXXXXX

View file

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