-
Notifications
You must be signed in to change notification settings - Fork 261
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(pt/pobreflix): Fix video extractor + add more extractors + parall…
…elize (#2610)
- Loading branch information
1 parent
dadd535
commit 34b01b1
Showing
5 changed files
with
161 additions
and
87 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,6 @@ | ||
dependencies { | ||
implementation(project(":lib-filemoon-extractor")) | ||
implementation(project(":lib-streamwish-extractor")) | ||
implementation(project(":lib-streamtape-extractor")) | ||
implementation(project(":lib-playlist-utils")) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
80 changes: 0 additions & 80 deletions
80
multisrc/overrides/dooplay/pobreflix/src/extractors/PainelfxExtractor.kt
This file was deleted.
Oops, something went wrong.
139 changes: 139 additions & 0 deletions
139
multisrc/overrides/dooplay/pobreflix/src/extractors/SuperFlixExtractor.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
package eu.kanade.tachiyomi.animeextension.pt.pobreflix.extractors | ||
|
||
import eu.kanade.tachiyomi.animesource.model.Video | ||
import eu.kanade.tachiyomi.network.GET | ||
import eu.kanade.tachiyomi.network.POST | ||
import eu.kanade.tachiyomi.util.asJsoup | ||
import kotlinx.coroutines.Dispatchers | ||
import kotlinx.coroutines.async | ||
import kotlinx.coroutines.awaitAll | ||
import kotlinx.coroutines.runBlocking | ||
import kotlinx.serialization.Serializable | ||
import kotlinx.serialization.json.Json | ||
import okhttp3.FormBody | ||
import okhttp3.Headers | ||
import okhttp3.HttpUrl.Companion.toHttpUrl | ||
import okhttp3.OkHttpClient | ||
import uy.kohesive.injekt.injectLazy | ||
|
||
class SuperFlixExtractor( | ||
private val client: OkHttpClient, | ||
private val defaultHeaders: Headers, | ||
private val genericExtractor: (String, String) -> List<Video>, | ||
) { | ||
private val json: Json by injectLazy() | ||
|
||
fun videosFromUrl(url: String): List<Video> { | ||
val links = linksFromUrl(url) | ||
|
||
val fixedLinks = links.parallelCatchingFlatMap { | ||
val (language, linkUrl) = it | ||
when { | ||
linkUrl.contains("?vid=") -> linksFromPlayer(linkUrl, language) | ||
else -> listOf(it) | ||
} | ||
} | ||
|
||
return fixedLinks.parallelCatchingFlatMap { genericExtractor(it.second, it.first) } | ||
} | ||
|
||
private fun linksFromPlayer(url: String, language: String): List<Pair<String, String>> { | ||
val httpUrl = url.toHttpUrl() | ||
val id = httpUrl.queryParameter("vid")!! | ||
val headers = defaultHeaders.newBuilder() | ||
.set("referer", "$API_DOMAIN/") | ||
.set("origin", API_DOMAIN) | ||
.build() | ||
|
||
val doc = client.newCall(GET(url, headers)).execute().use { it.asJsoup() } | ||
|
||
val baseUrl = "https://" + httpUrl.host | ||
val apiUrl = "$baseUrl/ajax_sources.php" | ||
|
||
val apiHeaders = headers.newBuilder() | ||
.set("referer", url) | ||
.set("origin", baseUrl) | ||
.set("X-Requested-With", "XMLHttpRequest") | ||
.build() | ||
|
||
return doc.select("ul > li[data-order-value]").mapNotNull { | ||
val name = it.attr("data-dropdown-value") | ||
val order = it.attr("data-order-value") | ||
|
||
val formBody = FormBody.Builder() | ||
.add("vid", id) | ||
.add("alternative", name) | ||
.add("ord", order) | ||
.build() | ||
|
||
val req = client.newCall(POST(apiUrl, apiHeaders, formBody)).execute() | ||
.use { it.body.string() } | ||
|
||
runCatching { | ||
val iframeUrl = json.decodeFromString<PlayerLinkDto>(req).iframe!! | ||
val iframeServer = iframeUrl.toHttpUrl().queryParameter("sv")!! | ||
language to when (name) { | ||
"1xbet" -> "https://watch.brplayer.site/watch?v=${iframeServer.trim('/')}" | ||
else -> iframeServer | ||
} | ||
}.getOrNull() | ||
} | ||
} | ||
|
||
@Serializable | ||
data class PlayerLinkDto(val iframe: String? = null) | ||
|
||
private fun linksFromUrl(url: String): List<Pair<String, String>> { | ||
val doc = client.newCall(GET(url, defaultHeaders)).execute().use { it.asJsoup() } | ||
|
||
val items = doc.select("div.select_language").mapNotNull { | ||
val target = it.attr("data-target") | ||
val id = doc.selectFirst("div.players_select div[data-target=$target] div[data-id]") | ||
?.attr("data-id") | ||
?: return@mapNotNull null | ||
|
||
it.text() to id // (Language, videoId) | ||
} | ||
|
||
val headers = defaultHeaders.newBuilder() | ||
.set("Origin", API_DOMAIN) | ||
.set("Referer", url) | ||
.set("X-Requested-With", "XMLHttpRequest") | ||
.build() | ||
|
||
return items.mapNotNull { | ||
runCatching { | ||
it.first to getLink(it.second, headers)!! | ||
}.getOrNull() | ||
} | ||
} | ||
|
||
private fun getLink(id: String, headers: Headers): String? { | ||
val body = FormBody.Builder() | ||
.add("action", "getPlayer") | ||
.add("video_id", id) | ||
.build() | ||
|
||
val res = client.newCall(POST("$API_DOMAIN/api", headers, body)).execute() | ||
.use { it.body.string() } | ||
|
||
return json.decodeFromString<ApiResponseDto>(res).data?.video_url | ||
} | ||
|
||
@Serializable | ||
data class ApiResponseDto(val data: DataDto? = null) | ||
|
||
@Serializable | ||
data class DataDto(val video_url: String? = null) | ||
|
||
private inline fun <A, B> Iterable<A>.parallelCatchingFlatMap(crossinline f: suspend (A) -> Iterable<B>): List<B> = | ||
runBlocking { | ||
map { | ||
async(Dispatchers.Default) { | ||
runCatching { f(it) }.getOrElse { emptyList() } | ||
} | ||
}.awaitAll().flatten() | ||
} | ||
} | ||
|
||
private const val API_DOMAIN = "https://superflixapi.top" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters