Tighter extraction
This commit is contained in:
parent
a4d3a117cf
commit
afb5e0c56f
1 changed files with 42 additions and 14 deletions
|
@ -1,22 +1,27 @@
|
||||||
package eu.kanade.tachiyomi.lib.buzzheavierextractor
|
package eu.kanade.tachiyomi.lib.buzzheavierextractor
|
||||||
|
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.animesource.model.Video
|
import eu.kanade.tachiyomi.animesource.model.Video
|
||||||
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 eu.kanade.tachiyomi.util.parseAs
|
import eu.kanade.tachiyomi.util.parseAs
|
||||||
|
import java.io.IOException
|
||||||
import kotlinx.serialization.ExperimentalSerializationApi
|
import kotlinx.serialization.ExperimentalSerializationApi
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import okhttp3.Headers
|
import okhttp3.Headers
|
||||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import okhttp3.internal.EMPTY_HEADERS
|
import okhttp3.Request
|
||||||
|
import okhttp3.Response
|
||||||
|
|
||||||
class BuzzheavierExtractor(
|
class BuzzheavierExtractor(
|
||||||
private val client: OkHttpClient,
|
private val client: OkHttpClient,
|
||||||
private val headers: Headers,
|
private val headers: Headers,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val SIZE_REGEX = Regex("""Size\s*-\s*([0-9.]+\s*[GMK]B)""")
|
||||||
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalSerializationApi::class)
|
@OptIn(ExperimentalSerializationApi::class)
|
||||||
fun videosFromUrl(url: String, prefix: String = "Buzzheavier - ", proxyUrl: String? = null): List<Video> {
|
fun videosFromUrl(url: String, prefix: String = "Buzzheavier - ", proxyUrl: String? = null): List<Video> {
|
||||||
val httpUrl = url.toHttpUrl()
|
val httpUrl = url.toHttpUrl()
|
||||||
|
@ -24,29 +29,52 @@ class BuzzheavierExtractor(
|
||||||
|
|
||||||
val dlHeaders = headers.newBuilder().apply {
|
val dlHeaders = headers.newBuilder().apply {
|
||||||
add("Accept", "*/*")
|
add("Accept", "*/*")
|
||||||
add("Host", httpUrl.host)
|
|
||||||
add("HX-Current-URL", url)
|
add("HX-Current-URL", url)
|
||||||
add("HX-Request", "true")
|
add("HX-Request", "true")
|
||||||
|
add("Priority", "u=1, i")
|
||||||
add("Referer", url)
|
add("Referer", url)
|
||||||
}.build()
|
}.build()
|
||||||
|
|
||||||
val videoHeaders = headers.newBuilder().apply {
|
val videoHeaders = headers.newBuilder().apply {
|
||||||
|
add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7")
|
||||||
|
add("Priority", "u=0, i")
|
||||||
add("Referer", url)
|
add("Referer", url)
|
||||||
}.build()
|
}.build()
|
||||||
|
|
||||||
val path = client.newCall(
|
val siteRequest = client.newCall(GET(url)).execute()
|
||||||
GET("https://${httpUrl.host}/$id/download", dlHeaders)
|
val parsedHtml = siteRequest.asJsoup()
|
||||||
).execute().headers["hx-redirect"].orEmpty()
|
val detailsText = parsedHtml.selectFirst("li:contains(Details:)")?.text() ?: ""
|
||||||
|
val size = SIZE_REGEX.find(detailsText)?.groupValues?.getOrNull(1)?.trim() ?: "Unknown"
|
||||||
|
|
||||||
return if (path.isNotEmpty()) {
|
val downloadRequest = GET("https://${httpUrl.host}/$id/download", dlHeaders)
|
||||||
val videoUrl = if (path.startsWith("http")) path else "https://${httpUrl.host}$path"
|
val path = client.executeWithRetry(downloadRequest, 5, 204).use { response ->
|
||||||
listOf(Video(videoUrl, "${prefix}Video", videoUrl, videoHeaders))
|
response.header("hx-redirect").orEmpty()
|
||||||
} else if (proxyUrl?.isNotEmpty() == true) {
|
|
||||||
val videoUrl = client.newCall(GET(proxyUrl + id)).execute().parseAs<UrlDto>().url
|
|
||||||
listOf(Video(videoUrl, "${prefix}Video", videoUrl, videoHeaders))
|
|
||||||
} else {
|
|
||||||
emptyList()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val videoUrl = if (path.isNotEmpty()) {
|
||||||
|
if (path.startsWith("http")) path else "https://${httpUrl.host}$path"
|
||||||
|
} else if (proxyUrl?.isNotEmpty() == true) {
|
||||||
|
client.executeWithRetry(GET(proxyUrl + id), 5, 200).parseAs<UrlDto>().url
|
||||||
|
} else {
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
return listOf(Video(videoUrl, "${prefix}${size}", videoUrl, videoHeaders))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun OkHttpClient.executeWithRetry(request: Request, maxRetries: Int, validCode: Int): Response {
|
||||||
|
var response: Response? = null
|
||||||
|
for (attempt in 0 until maxRetries) {
|
||||||
|
response?.close()
|
||||||
|
response = this.newCall(request).execute()
|
||||||
|
if (response.code == validCode) {
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
if (attempt < maxRetries - 1) {
|
||||||
|
Thread.sleep(1000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return response ?: throw IOException("Failed to execute request after $maxRetries attempts")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue