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:
parent
9f385108fc
commit
1384df62f3
350 changed files with 12176 additions and 1064 deletions
|
@ -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])))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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/")
|
||||
|
|
3
lib/goodstream-extractor/build.gradle.kts
Normal file
3
lib/goodstream-extractor/build.gradle.kts
Normal file
|
@ -0,0 +1,3 @@
|
|||
plugins {
|
||||
id("lib-android")
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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:"
|
||||
|
||||
|
|
|
@ -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/")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
7
lib/universal-extractor/build.gradle.kts
Normal file
7
lib/universal-extractor/build.gradle.kts
Normal file
|
@ -0,0 +1,7 @@
|
|||
plugins {
|
||||
id("lib-android")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":lib:playlist-utils"))
|
||||
}
|
|
@ -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)(\\?.*)?$") }
|
||||
}
|
||||
}
|
|
@ -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))
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue