feat(animestream/en): New source Luciferdonghua (#2119)
This commit is contained in:
@ -0,0 +1,4 @@
|
||||
dependencies {
|
||||
implementation(project(':lib-okru-extractor'))
|
||||
implementation(project(':lib-playlist-utils'))
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 10 KiB |
Binary file not shown.
After Width: | Height: | Size: 4.8 KiB |
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
Binary file not shown.
After Width: | Height: | Size: 34 KiB |
Binary file not shown.
After Width: | Height: | Size: 58 KiB |
@ -0,0 +1,55 @@
|
||||
package eu.kanade.tachiyomi.animeextension.en.luciferdonghua
|
||||
|
||||
import android.util.Base64
|
||||
import eu.kanade.tachiyomi.animeextension.en.luciferdonghua.extractors.DailymotionExtractor
|
||||
import eu.kanade.tachiyomi.animesource.model.Video
|
||||
import eu.kanade.tachiyomi.lib.okruextractor.OkruExtractor
|
||||
import eu.kanade.tachiyomi.multisrc.animestream.AnimeStream
|
||||
import org.jsoup.Jsoup
|
||||
|
||||
class LuciferDonghua : AnimeStream(
|
||||
"en",
|
||||
"LuciferDonghua",
|
||||
"https://luciferdonghua.in",
|
||||
) {
|
||||
// ============================ Video Links =============================
|
||||
|
||||
override fun getHosterUrl(encodedData: String): String {
|
||||
val doc = Base64.decode(encodedData, Base64.DEFAULT)
|
||||
.let(::String) // bytearray -> string
|
||||
.let(Jsoup::parse) // string -> document
|
||||
|
||||
val url = doc.selectFirst("iframe[src~=.]")?.attr("src")
|
||||
?: doc.selectFirst("meta[content~=.][itemprop=embedUrl]")!!.attr("content")
|
||||
|
||||
return when {
|
||||
url.startsWith("http") -> url
|
||||
else -> "https:$url"
|
||||
}
|
||||
}
|
||||
|
||||
override fun getVideoList(url: String, name: String): List<Video> {
|
||||
val prefix = "$name - "
|
||||
return when {
|
||||
url.contains("ok.ru") -> {
|
||||
OkruExtractor(client).videosFromUrl(url, prefix = prefix)
|
||||
}
|
||||
url.contains("dailymotion") -> {
|
||||
DailymotionExtractor(client, headers).videosFromUrl(url, prefix = prefix)
|
||||
}
|
||||
else -> emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
// ============================= Utilities ==============================
|
||||
|
||||
override fun List<Video>.sort(): List<Video> {
|
||||
val quality = preferences.getString(videoSortPrefKey, videoSortPrefDefault)!!
|
||||
return sortedWith(
|
||||
compareBy(
|
||||
{ it.quality.contains(quality) },
|
||||
{ Regex("""(\d+)p""").find(it.quality)?.groupValues?.get(1)?.toIntOrNull() ?: 0 },
|
||||
),
|
||||
).reversed()
|
||||
}
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
package eu.kanade.tachiyomi.animeextension.en.luciferdonghua.extractors
|
||||
|
||||
import eu.kanade.tachiyomi.animesource.model.Track
|
||||
import eu.kanade.tachiyomi.animesource.model.Video
|
||||
import eu.kanade.tachiyomi.lib.playlistutils.PlaylistUtils
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import okhttp3.Headers
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Response
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
class DailymotionExtractor(private val client: OkHttpClient, private val headers: Headers) {
|
||||
|
||||
private val json: Json by injectLazy()
|
||||
|
||||
private val playlistUtils by lazy { PlaylistUtils(client, headers) }
|
||||
|
||||
fun videosFromUrl(url: String, prefix: String): List<Video> {
|
||||
val htmlString = client.newCall(GET(url)).execute().use { it.body.string() }
|
||||
|
||||
val internalData = htmlString.substringAfter("\"dmInternalData\":").substringBefore("</script>")
|
||||
val ts = internalData.substringAfter("\"ts\":").substringBefore(",")
|
||||
val v1st = internalData.substringAfter("\"v1st\":\"").substringBefore("\",")
|
||||
|
||||
val videoQuery = url.toHttpUrl().queryParameter("video") ?: url.toHttpUrl().pathSegments.last()
|
||||
|
||||
val jsonUrl = "https://www.dailymotion.com/player/metadata/video/$videoQuery?locale=en-US&dmV1st=$v1st&dmTs=$ts&is_native_app=0"
|
||||
val parsed = client.newCall(GET(jsonUrl)).execute().parseAs<DailyQuality>()
|
||||
|
||||
// Blame dailymotion for this monstrosity
|
||||
val subtitlesString = parsed.subtitles.data.toString()
|
||||
val subtitleList = if (subtitlesString == "[]") {
|
||||
emptyList()
|
||||
} else {
|
||||
json.decodeFromString<Map<String, DailyQuality.SubtitleObject>>(subtitlesString).map { k ->
|
||||
Track(
|
||||
k.value.urls.first(),
|
||||
k.value.label,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val masterUrl = parsed.qualities.auto.first().url
|
||||
|
||||
return playlistUtils.extractFromHls(masterUrl, subtitleList = subtitleList, videoNameGen = { q -> "$prefix$q" })
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class DailyQuality(
|
||||
val qualities: Auto,
|
||||
val subtitles: Subtitle,
|
||||
) {
|
||||
@Serializable
|
||||
data class Auto(
|
||||
val auto: List<Item>,
|
||||
) {
|
||||
@Serializable
|
||||
data class Item(
|
||||
val type: String,
|
||||
val url: String,
|
||||
)
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class Subtitle(
|
||||
val data: JsonElement,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class SubtitleObject(
|
||||
val label: String,
|
||||
val urls: List<String>,
|
||||
)
|
||||
}
|
||||
|
||||
private inline fun <reified T> Response.parseAs(transform: (String) -> String = { it }): T {
|
||||
val responseBody = use { transform(it.body.string()) }
|
||||
return json.decodeFromString(responseBody)
|
||||
}
|
||||
}
|
@ -20,6 +20,7 @@ class AnimeStreamGenerator : ThemeSourceGenerator {
|
||||
SingleLang("DonghuaStream", "https://donghuastream.co.in", "en", isNsfw = false),
|
||||
SingleLang("Hstream", "https://hstream.moe", "en", isNsfw = true, overrideVersionCode = 3),
|
||||
SingleLang("LMAnime", "https://lmanime.com", "all", isNsfw = false, overrideVersionCode = 2),
|
||||
SingleLang("LuciferDonghua", "https://luciferdonghua.in", "en", isNsfw = false),
|
||||
SingleLang("MiniOppai", "https://minioppai.org", "id", isNsfw = true, overrideVersionCode = 2),
|
||||
SingleLang("RineCloud", "https://rine.cloud", "pt-BR", isNsfw = false),
|
||||
)
|
||||
|
Reference in New Issue
Block a user