Merged with dark25 (#636)

* merge

merged lib, lib-multisrc, all, ar, de, en, es, fr, hi, id, it, pt, tr src from dark25

* patch
This commit is contained in:
Hak 2025-02-10 15:41:59 +07:00 committed by GitHub
parent 9f385108fc
commit 1384df62f3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
350 changed files with 12176 additions and 1064 deletions

View file

@ -1,5 +1,6 @@
package eu.kanade.tachiyomi.lib.chillxextractor
import android.util.Log
import eu.kanade.tachiyomi.animesource.model.Track
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.lib.cryptoaes.CryptoAES
@ -51,6 +52,7 @@ class ChillxExtractor(private val client: OkHttpClient, private val headers: Hea
val subtitleList = buildList {
val subtitles = REGEX_SUBS.findAll(decryptedScript)
subtitles.forEach {
Log.d("ChillxExtractor", "Found subtitle: ${it.groupValues}")
add(Track(it.groupValues[1], decodeUnicodeEscape(it.groupValues[2])))
}
}

View file

@ -5,36 +5,50 @@ import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.network.GET
import okhttp3.Headers
import okhttp3.OkHttpClient
import java.net.URI
class DoodExtractor(private val client: OkHttpClient) {
fun videoFromUrl(
url: String,
quality: String? = null,
prefix: String? = null,
redirect: Boolean = true,
externalSubs: List<Track> = emptyList(),
): Video? {
val newQuality = quality ?: ("Doodstream" + if (redirect) " mirror" else "")
return runCatching {
val response = client.newCall(GET(url)).execute()
val newUrl = if (redirect) response.request.url.toString() else url
val doodHost = Regex("https://(.*?)/").find(newUrl)!!.groupValues[1]
val doodHost = getBaseUrl(newUrl)
val content = response.body.string()
if (!content.contains("'/pass_md5/")) return null
val md5 = content.substringAfter("'/pass_md5/").substringBefore("',")
// Obtener la calidad del título de la página
val extractedQuality = Regex("\\d{3,4}p")
.find(content.substringAfter("<title>").substringBefore("</title>"))
?.groupValues
?.getOrNull(0)
// Determinar la calidad a usar
val newQuality = extractedQuality ?: ( if (redirect) " mirror" else "")
// Obtener el hash MD5
val md5 = doodHost + (Regex("/pass_md5/[^']*").find(content)?.value ?: return null)
val token = md5.substringAfterLast("/")
val randomString = getRandomString()
val randomString = createHashTable()
val expiry = System.currentTimeMillis()
// Obtener la URL del video
val videoUrlStart = client.newCall(
GET(
"https://$doodHost/pass_md5/$md5",
md5,
Headers.headersOf("referer", newUrl),
),
).execute().body.string()
val videoUrl = "$videoUrlStart$randomString?token=$token&expiry=$expiry"
Video(videoUrl, newQuality, videoUrl, headers = doodHeaders(doodHost), subtitleTracks = externalSubs)
val trueUrl = "$videoUrlStart$randomString?token=$token&expiry=$expiry"
Video(trueUrl, prefix + "Doodstream " + newQuality , trueUrl, headers = doodHeaders(doodHost), subtitleTracks = externalSubs)
}.getOrNull()
}
@ -44,16 +58,27 @@ class DoodExtractor(private val client: OkHttpClient) {
redirect: Boolean = true,
): List<Video> {
val video = videoFromUrl(url, quality, redirect)
return video?.let(::listOf) ?: emptyList<Video>()
return video?.let(::listOf) ?: emptyList()
}
private fun getRandomString(length: Int = 10): String {
val allowedChars = ('A'..'Z') + ('a'..'z') + ('0'..'9')
return (1..length)
.map { allowedChars.random() }
.joinToString("")
// Método para generar una cadena aleatoria
private fun createHashTable(): String {
val alphabet = ('A'..'Z') + ('a'..'z') + ('0'..'9')
return buildString {
repeat(10) {
append(alphabet.random())
}
}
}
// Método para obtener la base de la URL
private fun getBaseUrl(url: String): String {
return URI(url).let {
"${it.scheme}://${it.host}"
}
}
// Método para obtener headers personalizados
private fun doodHeaders(host: String) = Headers.Builder().apply {
add("User-Agent", "Aniyomi")
add("Referer", "https://$host/")

View file

@ -0,0 +1,3 @@
plugins {
id("lib-android")
}

View file

@ -0,0 +1,33 @@
package eu.kanade.tachiyomi.lib.goodstramextractor
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.Headers
import okhttp3.OkHttpClient
class GoodStreamExtractor(private val client: OkHttpClient, private val headers: Headers) {
fun videosFromUrl(url: String, name: String): List<Video> {
val doc = client.newCall(GET(url, headers)).execute().asJsoup()
val videos = mutableListOf<Video>()
doc.select("script").forEach { script ->
if (script.data().contains(Regex("file|player"))) {
val urlRegex = Regex("file: \"(https:\\/\\/[a-z0-9.\\/-_?=&]+)\",")
urlRegex.find(script.data())?.groupValues?.get(1)?.let { link ->
videos.add(
Video(
url = link,
quality = name,
videoUrl = link,
headers = headers
)
)
}
}
}
return videos
}
}

View file

@ -8,6 +8,7 @@ import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient
import okhttp3.internal.commonEmptyHeaders
import kotlin.math.abs
class PlaylistUtils(private val client: OkHttpClient, private val headers: Headers = commonEmptyHeaders) {
@ -126,10 +127,16 @@ class PlaylistUtils(private val client: OkHttpClient, private val headers: Heade
}.toList()
return masterPlaylist.substringAfter(PLAYLIST_SEPARATOR).split(PLAYLIST_SEPARATOR).mapNotNull {
val codec = it.substringAfter("CODECS=\"", "").substringBefore("\"", "")
if (codec.isNotEmpty()) {
if (codec.startsWith("mp4a")) return@mapNotNull null
}
val resolution = it.substringAfter("RESOLUTION=")
.substringBefore("\n")
.substringAfter("x")
.substringBefore(",") + "p"
.substringBefore(",").let(::stnQuality)
val videoUrl = it.substringAfter("\n").substringBefore("\n").let { url ->
getAbsoluteUrl(url, playlistUrl, masterUrlBasePath)?.trimEnd()
@ -328,6 +335,13 @@ class PlaylistUtils(private val client: OkHttpClient, private val headers: Heade
// ============================= Utilities ==============================
private fun stnQuality(quality: String): String {
val intQuality = quality.toInt()
val standardQualities = listOf(144, 240, 360, 480, 720, 1080)
val result = standardQualities.minByOrNull { abs(it - intQuality) } ?: quality
return "${result}p"
}
companion object {
private const val PLAYLIST_SEPARATOR = "#EXT-X-STREAM-INF:"

View file

@ -1,25 +1,48 @@
package eu.kanade.tachiyomi.lib.streamhidevidextractor
import android.util.Log
import dev.datlag.jsunpacker.JsUnpacker
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.lib.playlistutils.PlaylistUtils
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.Headers
import okhttp3.OkHttpClient
class StreamHideVidExtractor(private val client: OkHttpClient) {
class StreamHideVidExtractor(private val client: OkHttpClient, private val headers: Headers) {
private val playlistUtils by lazy { PlaylistUtils(client) }
fun videosFromUrl(url: String, prefix: String = ""): List<Video> {
val page = client.newCall(GET(url)).execute().body.string()
val playlistUrl = (JsUnpacker.unpackAndCombine(page) ?: page)
.substringAfter("sources:")
.substringAfter("file:\"") // StreamHide
.substringAfter("src:\"") // StreamVid
.substringBefore('"')
if (!playlistUrl.startsWith("http")) return emptyList()
return playlistUtils.extractFromHls(playlistUrl,
videoNameGen = { "${prefix}StreamHideVid - $it" }
)
private val playlistUtils by lazy { PlaylistUtils(client, headers) }
fun videosFromUrl(url: String, videoNameGen: (String) -> String = { quality -> "StreamHideVid - $quality" }): List<Video> {
val doc = client.newCall(GET(getEmbedUrl(url), headers)).execute().asJsoup()
val scriptBody = doc.selectFirst("script:containsData(m3u8)")?.data()
?.let { script ->
if (script.contains("eval(function(p,a,c")) {
JsUnpacker.unpackAndCombine(script)
} else {
script
}
}
val masterUrl = scriptBody
?.substringAfter("source", "")
?.substringAfter("file:\"", "")
?.substringBefore("\"", "")
?.takeIf(String::isNotBlank)
?: return emptyList()
Log.d("StreamHideVidExtractor", "Playlist URL: $masterUrl")
return playlistUtils.extractFromHls(masterUrl, url, videoNameGen = videoNameGen)
}
private fun getEmbedUrl(url: String): String {
return when {
url.contains("/d/") -> url.replace("/d/", "/v/")
url.contains("/download/") -> url.replace("/download/", "/v/")
url.contains("/file/") -> url.replace("/file/", "/v/")
else -> url.replace("/f/", "/v/")
}
}
}

View file

@ -0,0 +1,7 @@
plugins {
id("lib-android")
}
dependencies {
implementation(project(":lib:playlist-utils"))
}

View file

@ -0,0 +1,115 @@
package eu.kanade.tachiyomi.lib.universalextractor
import android.annotation.SuppressLint
import android.app.Application
import android.os.Handler
import android.os.Looper
import android.util.Log
import android.webkit.WebResourceRequest
import android.webkit.WebResourceResponse
import android.webkit.WebView
import android.webkit.WebViewClient
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.lib.playlistutils.PlaylistUtils
import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient
import uy.kohesive.injekt.injectLazy
import java.util.Locale
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
class UniversalExtractor(private val client: OkHttpClient) {
private val context: Application by injectLazy()
private val handler by lazy { Handler(Looper.getMainLooper()) }
@SuppressLint("SetJavaScriptEnabled")
fun videosFromUrl(origRequestUrl: String, origRequestHeader: Headers, customQuality: String? = null, prefix: String = ""): List<Video> {
val host = origRequestUrl.toHttpUrl().host.substringBefore(".").proper()
val latch = CountDownLatch(1)
var webView: WebView? = null
var resultUrl = ""
val playlistUtils by lazy { PlaylistUtils(client, origRequestHeader) }
val headers = origRequestHeader.toMultimap().mapValues { it.value.getOrNull(0) ?: "" }.toMutableMap()
handler.post {
val newView = WebView(context)
webView = newView
with(newView.settings) {
javaScriptEnabled = true
domStorageEnabled = true
databaseEnabled = true
useWideViewPort = false
loadWithOverviewMode = false
userAgentString = origRequestHeader["User-Agent"]
}
newView.webViewClient = object : WebViewClient() {
override fun shouldInterceptRequest(
view: WebView,
request: WebResourceRequest,
): WebResourceResponse? {
val url = request.url.toString()
if (VIDEO_REGEX.containsMatchIn(url)) {
resultUrl = url
latch.countDown()
}
return super.shouldInterceptRequest(view, request)
}
}
webView?.loadUrl(origRequestUrl, headers)
}
latch.await(TIMEOUT_SEC, TimeUnit.SECONDS)
handler.post {
webView?.stopLoading()
webView?.destroy()
webView = null
}
// terabox special case start
if ("M3U8_AUTO_360" in resultUrl) {
val qualities = listOf("1080", "720", "480", "360")
val allVideos = mutableListOf<Video>()
for (quality in qualities) {
val modifiedUrl = resultUrl.replace("M3U8_AUTO_360", "M3U8_AUTO_$quality")
val videos = playlistUtils.extractFromHls(modifiedUrl, origRequestUrl, videoNameGen = { "$prefix - $host: $it $quality" + "p" })
if (videos.isNotEmpty()) {
allVideos.addAll(videos)
}
}
if (allVideos.isNotEmpty()) {
return allVideos
}
}
// terabox special case end
return when {
"m3u8" in resultUrl -> {
Log.d("UniversalExtractor", "m3u8 URL: $resultUrl")
playlistUtils.extractFromHls(resultUrl, origRequestUrl, videoNameGen = { "$prefix - $host: $it" })
}
"mpd" in resultUrl -> {
Log.d("UniversalExtractor", "mpd URL: $resultUrl")
playlistUtils.extractFromDash(resultUrl, { it -> "$prefix - $host: $it" }, referer = origRequestUrl)
}
"mp4" in resultUrl -> {
Log.d("UniversalExtractor", "mp4 URL: $resultUrl")
Video(resultUrl, "$prefix - $host: ${customQuality ?: "Mirror"}", resultUrl, origRequestHeader.newBuilder().add("referer", origRequestUrl).build()).let(::listOf)
}
else -> emptyList()
}
}
private fun String.proper(): String {
return this.replaceFirstChar { if (it.isLowerCase()) it.titlecase(
Locale.getDefault()) else it.toString() }
}
companion object {
const val TIMEOUT_SEC: Long = 10
private val VIDEO_REGEX by lazy { Regex(".*\\.(mp4|m3u8|mpd)(\\?.*)?$") }
}
}

View file

@ -17,7 +17,7 @@ class UqloadExtractor(private val client: OkHttpClient) {
?.takeIf { it.startsWith("http") }
?: return emptyList()
val videoHeaders = Headers.headersOf("Referer", "https://uqload.co/")
val videoHeaders = Headers.headersOf("Referer", "https://uqload.ws/")
val quality = if (prefix.isNotBlank()) "$prefix Uqload" else "Uqload"
return listOf(Video(videoUrl, quality, videoUrl, videoHeaders))