forked from AlmightyHak/extensions-source
chore: Sync with latest commits
This commit is contained in:
parent
9b96273df0
commit
56c945d716
115 changed files with 2230 additions and 445 deletions
18
src/pt/pobreflix/build.gradle
Normal file
18
src/pt/pobreflix/build.gradle
Normal file
|
@ -0,0 +1,18 @@
|
|||
ext {
|
||||
extName = 'Pobreflix'
|
||||
extClass = '.Pobreflix'
|
||||
themePkg = 'dooplay'
|
||||
baseUrl = 'https://pobreflix1.art'
|
||||
overrideVersionCode = 9
|
||||
isNsfw = true
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
||||
dependencies {
|
||||
implementation(project(":lib:filemoon-extractor"))
|
||||
implementation(project(":lib:streamwish-extractor"))
|
||||
implementation(project(":lib:streamtape-extractor"))
|
||||
implementation(project(":lib:playlist-utils"))
|
||||
implementation "dev.datlag.jsunpacker:jsunpacker:1.0.1"
|
||||
}
|
BIN
src/pt/pobreflix/res/mipmap-hdpi/ic_launcher.png
Normal file
BIN
src/pt/pobreflix/res/mipmap-hdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3 KiB |
BIN
src/pt/pobreflix/res/mipmap-mdpi/ic_launcher.png
Normal file
BIN
src/pt/pobreflix/res/mipmap-mdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.6 KiB |
BIN
src/pt/pobreflix/res/mipmap-xhdpi/ic_launcher.png
Normal file
BIN
src/pt/pobreflix/res/mipmap-xhdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4 KiB |
BIN
src/pt/pobreflix/res/mipmap-xxhdpi/ic_launcher.png
Normal file
BIN
src/pt/pobreflix/res/mipmap-xxhdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.9 KiB |
BIN
src/pt/pobreflix/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
BIN
src/pt/pobreflix/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.1 KiB |
|
@ -0,0 +1,79 @@
|
|||
package eu.kanade.tachiyomi.animeextension.pt.pobreflix
|
||||
|
||||
import android.util.Base64
|
||||
import eu.kanade.tachiyomi.animeextension.pt.pobreflix.extractors.FireplayerExtractor
|
||||
import eu.kanade.tachiyomi.animeextension.pt.pobreflix.extractors.MyStreamExtractor
|
||||
import eu.kanade.tachiyomi.animeextension.pt.pobreflix.extractors.SuperFlixExtractor
|
||||
import eu.kanade.tachiyomi.animesource.model.Video
|
||||
import eu.kanade.tachiyomi.lib.filemoonextractor.FilemoonExtractor
|
||||
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.network.GET
|
||||
import eu.kanade.tachiyomi.util.asJsoup
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import okhttp3.Response
|
||||
|
||||
class Pobreflix : DooPlay(
|
||||
"pt-BR",
|
||||
"Pobreflix",
|
||||
"https://pobreflix1.art",
|
||||
) {
|
||||
// ============================== Popular ===============================
|
||||
override fun popularAnimeSelector() = "div.featured div.poster"
|
||||
|
||||
// =============================== Latest ===============================
|
||||
override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/series/page/$page/", headers)
|
||||
|
||||
// ============================ Video Links =============================
|
||||
private val embedplayerExtractor by lazy { FireplayerExtractor(client) }
|
||||
private val brbeastExtractor by lazy { FireplayerExtractor(client, "https://brbeast.com") }
|
||||
private val superembedsExtractor by lazy { FireplayerExtractor(client, "https://superembeds.com/") }
|
||||
private val filemoonExtractor by lazy { FilemoonExtractor(client) }
|
||||
private val mystreamExtractor by lazy { MyStreamExtractor(client, headers) }
|
||||
private val streamtapeExtractor by lazy { StreamTapeExtractor(client) }
|
||||
private val streamwishExtractor by lazy { StreamWishExtractor(client, headers) }
|
||||
private val superflixExtractor by lazy { SuperFlixExtractor(client, headers, ::genericExtractor) }
|
||||
private val supercdnExtractor by lazy { SuperFlixExtractor(client, headers, ::genericExtractor, "https://supercdn.org") }
|
||||
|
||||
override fun videoListParse(response: Response): List<Video> {
|
||||
val doc = response.asJsoup()
|
||||
return doc.select("div.source-box > a").flatMap {
|
||||
runCatching {
|
||||
val data = it.attr("href").toHttpUrl().queryParameter("auth")
|
||||
?.let { Base64.decode(it, Base64.DEFAULT) }
|
||||
?.let(::String)
|
||||
?: return@flatMap emptyList()
|
||||
val url = data.replace("\\", "").substringAfter("url\":\"").substringBefore('"')
|
||||
genericExtractor(url)
|
||||
}.getOrElse { emptyList() }
|
||||
}
|
||||
}
|
||||
|
||||
private fun genericExtractor(url: String, language: String = ""): List<Video> {
|
||||
val langSubstr = "[$language]"
|
||||
return when {
|
||||
url.contains("superflix") ->
|
||||
superflixExtractor.videosFromUrl(url)
|
||||
url.contains("supercdn") ->
|
||||
supercdnExtractor.videosFromUrl(url)
|
||||
url.contains("filemoon") ->
|
||||
filemoonExtractor.videosFromUrl(url, "$langSubstr Filemoon - ", headers = headers)
|
||||
url.contains("watch.brplayer") || url.contains("/watch?v=") ->
|
||||
mystreamExtractor.videosFromUrl(url, language)
|
||||
url.contains("brbeast") ->
|
||||
brbeastExtractor.videosFromUrl(url, language)
|
||||
url.contains("embedplayer") ->
|
||||
embedplayerExtractor.videosFromUrl(url, language)
|
||||
url.contains("superembeds") ->
|
||||
superembedsExtractor.videosFromUrl(url, language)
|
||||
url.contains("streamtape") ->
|
||||
streamtapeExtractor.videosFromUrl(url, "$langSubstr Streamtape")
|
||||
url.contains("filelions") ->
|
||||
streamwishExtractor.videosFromUrl(url, videoNameGen = { "$langSubstr FileLions - $it" })
|
||||
url.contains("streamwish") ->
|
||||
streamwishExtractor.videosFromUrl(url, videoNameGen = { "$langSubstr Streamwish - $it" })
|
||||
else -> emptyList()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
package eu.kanade.tachiyomi.animeextension.pt.pobreflix.extractors
|
||||
|
||||
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.network.POST
|
||||
import eu.kanade.tachiyomi.util.asJsoup
|
||||
import okhttp3.FormBody
|
||||
import okhttp3.Headers
|
||||
import okhttp3.OkHttpClient
|
||||
|
||||
class FireplayerExtractor(private val client: OkHttpClient, private val host: String = "https://embedplayer.online") {
|
||||
private val headers by lazy {
|
||||
Headers.headersOf(
|
||||
"X-Requested-With",
|
||||
"XMLHttpRequest",
|
||||
"Referer",
|
||||
host,
|
||||
"Origin",
|
||||
host,
|
||||
)
|
||||
}
|
||||
|
||||
private val playlistUtils by lazy { PlaylistUtils(client, headers) }
|
||||
|
||||
fun videosFromUrl(url: String, lang: String): List<Video> {
|
||||
var id = url.substringAfterLast("/")
|
||||
|
||||
if (id.length < 32) {
|
||||
val doc = client.newCall(GET(url, headers)).execute().asJsoup()
|
||||
|
||||
val script = doc.selectFirst("script:containsData(eval):containsData(p,a,c,k,e,d)")?.data()
|
||||
?.let(JsUnpacker::unpackAndCombine)
|
||||
?: doc.selectFirst("script:containsData(FirePlayer)")?.data()
|
||||
|
||||
if (script?.contains("FirePlayer(") == true) {
|
||||
id = script.substringAfter("FirePlayer(\"").substringBefore('"')
|
||||
}
|
||||
}
|
||||
|
||||
val postUrl = "$host/player/index.php?data=$id&do=getVideo"
|
||||
val body = FormBody.Builder()
|
||||
.add("hash", id)
|
||||
.add("r", "")
|
||||
.build()
|
||||
|
||||
val masterUrl = client.newCall(POST(postUrl, headers, body = body)).execute()
|
||||
.body.string()
|
||||
.substringAfter("securedLink\":\"")
|
||||
.substringBefore('"')
|
||||
.replace("\\", "")
|
||||
|
||||
return playlistUtils.extractFromHls(masterUrl, videoNameGen = { "[$lang] EmbedPlayer - $it" })
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package eu.kanade.tachiyomi.animeextension.pt.pobreflix.extractors
|
||||
|
||||
import eu.kanade.tachiyomi.animesource.model.Video
|
||||
import eu.kanade.tachiyomi.lib.playlistutils.PlaylistUtils
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import okhttp3.Headers
|
||||
import okhttp3.OkHttpClient
|
||||
|
||||
// From animeworldindia
|
||||
class MyStreamExtractor(private val client: OkHttpClient, private val headers: Headers) {
|
||||
|
||||
private val playlistUtils by lazy { PlaylistUtils(client, headers) }
|
||||
|
||||
fun videosFromUrl(url: String, language: String): List<Video> {
|
||||
val host = url.substringBefore("/watch?")
|
||||
|
||||
val response = client.newCall(GET(url, headers)).execute()
|
||||
val body = response.body.string()
|
||||
|
||||
val codePart = body
|
||||
.substringAfter("sniff(") // Video function
|
||||
.substringBefore(",[")
|
||||
|
||||
val streamCode = codePart
|
||||
.substringAfterLast(",\"") // our beloved hash
|
||||
.substringBefore('"')
|
||||
|
||||
val id = codePart.substringAfter(",\"").substringBefore('"') // required ID
|
||||
|
||||
val streamUrl = "$host/m3u8/$id/$streamCode/master.txt?s=1&cache=1"
|
||||
|
||||
val cookie = response.headers.firstOrNull {
|
||||
it.first.startsWith("set-cookie", true) && it.second.startsWith("PHPSESSID", true)
|
||||
}?.second?.substringBefore(";") ?: ""
|
||||
|
||||
val newHeaders = headers.newBuilder()
|
||||
.set("cookie", cookie)
|
||||
.set("accept", "*/*")
|
||||
.build()
|
||||
|
||||
return playlistUtils.extractFromHls(
|
||||
streamUrl,
|
||||
masterHeaders = newHeaders,
|
||||
videoHeaders = newHeaders,
|
||||
videoNameGen = { "[$language] MyStream: $it" },
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,134 @@
|
|||
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.network.await
|
||||
import eu.kanade.tachiyomi.util.asJsoup
|
||||
import eu.kanade.tachiyomi.util.parallelCatchingFlatMapBlocking
|
||||
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 host: String = "https://superflixapi.top",
|
||||
) {
|
||||
private val json: Json by injectLazy()
|
||||
|
||||
fun videosFromUrl(url: String): List<Video> {
|
||||
val links = linksFromUrl(url)
|
||||
|
||||
val fixedLinks = links.parallelCatchingFlatMapBlocking {
|
||||
val (language, linkUrl) = it
|
||||
when {
|
||||
linkUrl.contains("?vid=") -> linksFromPlayer(linkUrl, language)
|
||||
else -> listOf(it)
|
||||
}
|
||||
}
|
||||
|
||||
return fixedLinks.parallelCatchingFlatMapBlocking { genericExtractor(it.second, it.first) }
|
||||
}
|
||||
|
||||
private suspend 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", "$host/")
|
||||
.set("origin", host)
|
||||
.build()
|
||||
|
||||
val doc = client.newCall(GET(url, headers)).await().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)).await()
|
||||
.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().asJsoup()
|
||||
|
||||
val items = doc.select("div.select_languages").mapNotNull {
|
||||
val id = it.nextElementSibling()
|
||||
?.selectFirst("div[data-id]")
|
||||
?.attr("data-id")
|
||||
?: return@mapNotNull null
|
||||
|
||||
val language = it.text()
|
||||
.replace("Disponível", "")
|
||||
.replace("disponível", "")
|
||||
.replace("Apenas", "")
|
||||
.replace("em", "")
|
||||
.trim()
|
||||
|
||||
language to id // (Language, videoId)
|
||||
}
|
||||
|
||||
val headers = defaultHeaders.newBuilder()
|
||||
.set("Origin", host)
|
||||
.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("$host/api", headers, body)).execute()
|
||||
.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)
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue