feat(de/cinemathek): Add more video extractors (#2469)

This commit is contained in:
Claudemirovsky
2023-11-03 14:29:05 -03:00
committed by GitHub
parent 9a20e7ab2c
commit 9524d83790
4 changed files with 40 additions and 67 deletions

View File

@ -2,5 +2,6 @@ dependencies {
implementation(project(':lib-filemoon-extractor')) implementation(project(':lib-filemoon-extractor'))
implementation(project(':lib-dood-extractor')) implementation(project(':lib-dood-extractor'))
implementation(project(':lib-streamlare-extractor')) implementation(project(':lib-streamlare-extractor'))
implementation("dev.datlag.jsunpacker:jsunpacker:1.0.1") implementation(project(':lib-streamtape-extractor'))
implementation(project(':lib-streamwish-extractor'))
} }

View File

@ -3,19 +3,22 @@ package eu.kanade.tachiyomi.animeextension.de.cinemathek
import androidx.preference.ListPreference import androidx.preference.ListPreference
import androidx.preference.MultiSelectListPreference import androidx.preference.MultiSelectListPreference
import androidx.preference.PreferenceScreen import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.animeextension.de.cinemathek.extractors.StreamHideExtractor
import eu.kanade.tachiyomi.animesource.model.Video import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.lib.doodextractor.DoodExtractor import eu.kanade.tachiyomi.lib.doodextractor.DoodExtractor
import eu.kanade.tachiyomi.lib.filemoonextractor.FilemoonExtractor import eu.kanade.tachiyomi.lib.filemoonextractor.FilemoonExtractor
import eu.kanade.tachiyomi.lib.streamlareextractor.StreamlareExtractor import eu.kanade.tachiyomi.lib.streamlareextractor.StreamlareExtractor
import eu.kanade.tachiyomi.lib.streamtapeextractor.StreamTapeExtractor
import eu.kanade.tachiyomi.lib.streamwishextractor.StreamWishExtractor
import eu.kanade.tachiyomi.multisrc.dooplay.DooPlay import eu.kanade.tachiyomi.multisrc.dooplay.DooPlay
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.Request import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.runBlocking
import okhttp3.Response import okhttp3.Response
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import uy.kohesive.injekt.api.get
class Cinemathek : DooPlay( class Cinemathek : DooPlay(
"de", "de",
@ -23,15 +26,15 @@ class Cinemathek : DooPlay(
"https://cinemathek.net", "https://cinemathek.net",
) { ) {
// ============================== Popular =============================== // ============================== Popular ===============================
override fun popularAnimeSelector(): String = "article.movies div.poster" override fun popularAnimeSelector() = "article.movies div.poster"
override fun popularAnimeRequest(page: Int): Request = GET("$baseUrl/filme/page/$page/") override fun popularAnimeRequest(page: Int) = GET("$baseUrl/filme/page/$page/")
override fun popularAnimeNextPageSelector() = latestUpdatesNextPageSelector() override fun popularAnimeNextPageSelector() = latestUpdatesNextPageSelector()
// =============================== Latest =============================== // =============================== Latest ===============================
override fun latestUpdatesNextPageSelector(): String = "#nextpagination" override fun latestUpdatesNextPageSelector() = "#nextpagination"
override fun latestUpdatesRequest(page: Int): Request = GET("$baseUrl/episoden/page/$page") override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/episoden/page/$page")
// =========================== Anime Details ============================ // =========================== Anime Details ============================
override val additionalInfoItems = listOf("Original", "Start", "Staffeln", "letzte", "Episoden") override val additionalInfoItems = listOf("Original", "Start", "Staffeln", "letzte", "Episoden")
@ -46,9 +49,9 @@ class Cinemathek : DooPlay(
override fun videoListParse(response: Response): List<Video> { override fun videoListParse(response: Response): List<Video> {
val players = response.use { it.asJsoup().select("ul#playeroptionsul li") } val players = response.use { it.asJsoup().select("ul#playeroptionsul li") }
val hosterSelection = preferences.getStringSet(PREF_HOSTER_SELECTION_KEY, PREF_HOSTER_SELECTION_DEFAULT)!! val hosterSelection = preferences.getStringSet(PREF_HOSTER_SELECTION_KEY, PREF_HOSTER_SELECTION_DEFAULT)!!
return players.mapNotNull { player -> return players.parallelMapNotNull { player ->
runCatching { runCatching {
val url = getPlayerUrl(player).ifEmpty { return@mapNotNull null } val url = getPlayerUrl(player).ifEmpty { return@parallelMapNotNull null }
getPlayerVideos(url, hosterSelection) getPlayerVideos(url, hosterSelection)
}.getOrNull() }.getOrNull()
}.flatten() }.flatten()
@ -61,26 +64,35 @@ class Cinemathek : DooPlay(
if (num == "trailer") return "" if (num == "trailer") return ""
return client.newCall(GET("$baseUrl/wp-json/dooplayer/v2/$id/$type/$num")) return client.newCall(GET("$baseUrl/wp-json/dooplayer/v2/$id/$type/$num"))
.execute() .execute()
.body.string() .use { it.body.string() }
.substringAfter("\"embed_url\":\"") .substringAfter("\"embed_url\":\"")
.substringBefore("\",") .substringBefore("\",")
.replace("\\", "") .replace("\\", "")
} }
private val streamlareExtractor by lazy { StreamlareExtractor(client) }
private val filemoonExtractor by lazy { FilemoonExtractor(client) }
private val doodExtractor by lazy { DoodExtractor(client) }
private val streamtapeExtractor by lazy { StreamTapeExtractor(client) }
private val streamwishExtractor by lazy { StreamWishExtractor(client, headers) }
private fun getPlayerVideos(url: String, hosterSelection: Set<String>): List<Video>? { private fun getPlayerVideos(url: String, hosterSelection: Set<String>): List<Video>? {
return when { return when {
url.contains("https://streamlare.com") && hosterSelection.contains("slare") -> { url.contains("https://streamlare.com") && hosterSelection.contains("slare") -> {
StreamlareExtractor(client).videosFromUrl(url) streamlareExtractor.videosFromUrl(url)
} }
url.contains("https://filemoon") && hosterSelection.contains("fmoon") -> { url.contains("https://filemoon") && hosterSelection.contains("fmoon") -> {
FilemoonExtractor(client).videosFromUrl(url) filemoonExtractor.videosFromUrl(url)
} }
url.contains("https://dooood") && hosterSelection.contains("dood") -> { (url.contains("ds2play") || url.contains("https://doo")) && hosterSelection.contains("dood") -> {
DoodExtractor(client).videosFromUrl(url) doodExtractor.videosFromUrl(url)
} }
url.contains("https://streamhide") && hosterSelection.contains("shide") -> { url.contains("streamtape") && hosterSelection.contains("stape") -> {
StreamHideExtractor(client).videosFromUrl(url) streamtapeExtractor.videosFromUrl(url)
}
(url.contains("filelions") || url.contains("streamwish")) && hosterSelection.contains("swish") -> {
streamwishExtractor.videosFromUrl(url)
} }
else -> null else -> null
} }
@ -151,17 +163,22 @@ class Cinemathek : DooPlay(
).reversed() ).reversed()
} }
private inline fun <A, B> Iterable<A>.parallelMapNotNull(crossinline f: suspend (A) -> B?): List<B> =
runBlocking {
map { async(Dispatchers.Default) { f(it) } }.awaitAll().filterNotNull()
}
companion object { companion object {
private const val PREF_HOSTER_KEY = "preferred_hoster" private const val PREF_HOSTER_KEY = "preferred_hoster"
private const val PREF_HOSTER_TITLE = "Standard-Hoster" private const val PREF_HOSTER_TITLE = "Standard-Hoster"
private const val PREF_HOSTER_DEFAULT = "https://viewsb.com" private const val PREF_HOSTER_DEFAULT = "https://filemoon"
private val PREF_HOSTER_ENTRIES = arrayOf("Streamlare", "Filemoon", "DoodStream", "StreamHide") private val PREF_HOSTER_ENTRIES = arrayOf("Streamlare", "Filemoon", "DoodStream", "StreamTape", "StreamWish/Filelions")
private val PREF_HOSTER_VALUES = arrayOf("https://streamlare", "https://viewsb.com", "https://filemoon", "https://dooood", "https://streamhide") private val PREF_HOSTER_VALUES = arrayOf("https://streamlare", "https://filemoon", "https://doo", "https://streamtape", "https://streamwish")
private const val PREF_HOSTER_SELECTION_KEY = "hoster_selection" private const val PREF_HOSTER_SELECTION_KEY = "hoster_selection"
private const val PREF_HOSTER_SELECTION_TITLE = "Hoster auswählen" private const val PREF_HOSTER_SELECTION_TITLE = "Hoster auswählen"
private val PREF_HOSTER_SELECTION_ENTRIES = PREF_HOSTER_ENTRIES private val PREF_HOSTER_SELECTION_ENTRIES = PREF_HOSTER_ENTRIES
private val PREF_HOSTER_SELECTION_VALUES = arrayOf("slare", "fmoon", "dood", "shide") private val PREF_HOSTER_SELECTION_VALUES = arrayOf("slare", "fmoon", "dood", "stape", "swish")
private val PREF_HOSTER_SELECTION_DEFAULT = PREF_HOSTER_SELECTION_VALUES.toSet() private val PREF_HOSTER_SELECTION_DEFAULT = PREF_HOSTER_SELECTION_VALUES.toSet()
private const val PREF_QUALITY_KEY = "preferred_quality" private const val PREF_QUALITY_KEY = "preferred_quality"

View File

@ -1,45 +0,0 @@
package eu.kanade.tachiyomi.animeextension.de.cinemathek.extractors
import dev.datlag.jsunpacker.JsUnpacker
import eu.kanade.tachiyomi.animesource.model.Track
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.network.GET
import okhttp3.OkHttpClient
class StreamHideExtractor(private val client: OkHttpClient) {
// from nineanime / ask4movie FilemoonExtractor
private val subtitleRegex = Regex("""#EXT-X-MEDIA:TYPE=SUBTITLES.*?NAME="(.*?)".*?URI="(.*?)"""")
fun videosFromUrl(url: String): List<Video> {
val page = client.newCall(GET(url)).execute().body.string()
val unpacked = JsUnpacker.unpackAndCombine(page) ?: return emptyList()
val playlistUrl = unpacked.substringAfter("sources:")
.substringAfter("file:\"")
.substringBefore('"')
val playlistData = client.newCall(GET(playlistUrl)).execute().body.string()
val subs = subtitleRegex.findAll(playlistData).map {
val subUrl = fixUrl(it.groupValues[2], playlistUrl)
Track(subUrl, it.groupValues[1])
}.toList()
val separator = "#EXT-X-STREAM-INF"
return playlistData.substringAfter(separator).split(separator).map {
val resolution = it.substringAfter("RESOLUTION=")
.substringBefore("\n")
.substringAfter("x")
.substringBefore(",") + "p"
val urlPart = it.substringAfter("\n").substringBefore("\n")
val videoUrl = fixUrl(urlPart, playlistUrl)
Video(videoUrl, "StreamHide:$resolution", videoUrl, subtitleTracks = subs)
}
}
private fun fixUrl(urlPart: String, playlistUrl: String) =
when {
!urlPart.startsWith("https:") -> playlistUrl.substringBeforeLast("/") + "/$urlPart"
else -> urlPart
}
}

View File

@ -18,7 +18,7 @@ class DooPlayGenerator : ThemeSourceGenerator {
SingleLang("AnimeSAGA", "https://www.animesaga.in", "hi", isNsfw = false, overrideVersionCode = 7), SingleLang("AnimeSAGA", "https://www.animesaga.in", "hi", isNsfw = false, overrideVersionCode = 7),
SingleLang("AnimesFox BR", "https://animesfox.net", "pt-BR", isNsfw = false, overrideVersionCode = 2), SingleLang("AnimesFox BR", "https://animesfox.net", "pt-BR", isNsfw = false, overrideVersionCode = 2),
SingleLang("Animes House", "https://animeshouse.net", "pt-BR", isNsfw = false, overrideVersionCode = 7), SingleLang("Animes House", "https://animeshouse.net", "pt-BR", isNsfw = false, overrideVersionCode = 7),
SingleLang("Cinemathek", "https://cinemathek.net", "de", isNsfw = true, overrideVersionCode = 16), SingleLang("Cinemathek", "https://cinemathek.net", "de", isNsfw = true, overrideVersionCode = 17),
SingleLang("DonghuaX", "https://donghuax.com", "pt-BR", isNsfw = false, overrideVersionCode = 1), SingleLang("DonghuaX", "https://donghuax.com", "pt-BR", isNsfw = false, overrideVersionCode = 1),
SingleLang("GoAnimes", "https://goanimes.net", "pt-BR", isNsfw = true, overrideVersionCode = 5), SingleLang("GoAnimes", "https://goanimes.net", "pt-BR", isNsfw = true, overrideVersionCode = 5),
SingleLang("JetAnime", "https://ssl.jetanimes.com", "fr", isNsfw = false, overrideVersionCode = 2), SingleLang("JetAnime", "https://ssl.jetanimes.com", "fr", isNsfw = false, overrideVersionCode = 2),