chore: Sync with latest commits
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'AniDong'
|
||||
extClass = '.AniDong'
|
||||
extVersionCode = 1
|
||||
extVersionCode = 2
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -75,7 +75,11 @@ class AniDong : ParsedAnimeHttpSource() {
|
|||
}
|
||||
|
||||
private fun searchAnimeByIdParse(response: Response): AnimesPage {
|
||||
val details = animeDetailsParse(response.asJsoup())
|
||||
val details = animeDetailsParse(response.asJsoup()).apply {
|
||||
setUrlWithoutDomain(response.request.url.toString())
|
||||
initialized = true
|
||||
}
|
||||
|
||||
return AnimesPage(listOf(details), false)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'Anime Fire'
|
||||
extClass = '.AnimeFire'
|
||||
extVersionCode = 6
|
||||
extVersionCode = 7
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -85,7 +85,11 @@ class AnimeFire : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||
}
|
||||
|
||||
private fun searchAnimeByIdParse(response: Response): AnimesPage {
|
||||
val details = animeDetailsParse(response)
|
||||
val details = animeDetailsParse(response).apply {
|
||||
setUrlWithoutDomain(response.request.url.toString())
|
||||
initialized = true
|
||||
}
|
||||
|
||||
return AnimesPage(listOf(details), false)
|
||||
}
|
||||
|
||||
|
|
12
src/pt/animeq/build.gradle
Normal file
|
@ -0,0 +1,12 @@
|
|||
ext {
|
||||
extName = 'AnimeQ'
|
||||
extClass = '.AnimeQ'
|
||||
extVersionCode = 1
|
||||
isNsfw = true
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
||||
dependencies {
|
||||
implementation(project(':lib:blogger-extractor'))
|
||||
}
|
BIN
src/pt/animeq/res/mipmap-hdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 3.4 KiB |
BIN
src/pt/animeq/res/mipmap-mdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
src/pt/animeq/res/mipmap-xhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 5 KiB |
BIN
src/pt/animeq/res/mipmap-xxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 8.8 KiB |
BIN
src/pt/animeq/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 13 KiB |
|
@ -0,0 +1,260 @@
|
|||
package eu.kanade.tachiyomi.animeextension.pt.animeq
|
||||
|
||||
import android.app.Application
|
||||
import android.util.Base64
|
||||
import androidx.preference.ListPreference
|
||||
import androidx.preference.PreferenceScreen
|
||||
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
|
||||
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
|
||||
import eu.kanade.tachiyomi.animesource.model.SAnime
|
||||
import eu.kanade.tachiyomi.animesource.model.SEpisode
|
||||
import eu.kanade.tachiyomi.animesource.model.Video
|
||||
import eu.kanade.tachiyomi.animesource.online.ParsedAnimeHttpSource
|
||||
import eu.kanade.tachiyomi.lib.bloggerextractor.BloggerExtractor
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.util.asJsoup
|
||||
import eu.kanade.tachiyomi.util.parallelCatchingFlatMapBlocking
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import org.jsoup.nodes.Document
|
||||
import org.jsoup.nodes.Element
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
||||
class AnimeQ : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
||||
|
||||
override val name = "AnimeQ"
|
||||
|
||||
override val baseUrl = "https://animeq.blog"
|
||||
|
||||
override val lang = "pt-BR"
|
||||
|
||||
override val supportsLatest = true
|
||||
|
||||
private val preferences by lazy {
|
||||
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
|
||||
}
|
||||
|
||||
override fun headersBuilder() = super.headersBuilder()
|
||||
.add("Referer", baseUrl)
|
||||
.add("Origin", baseUrl)
|
||||
|
||||
// ============================== Popular ===============================
|
||||
override fun popularAnimeRequest(page: Int) = GET("$baseUrl/160679", headers)
|
||||
|
||||
override fun popularAnimeSelector() = "div.widget_block:contains(Acessados) a"
|
||||
|
||||
override fun popularAnimeFromElement(element: Element) = SAnime.create().apply {
|
||||
setUrlWithoutDomain(element.selectFirst("a")!!.attr("href"))
|
||||
title = element.selectFirst("a img")!!.attr("title")
|
||||
thumbnail_url = element.selectFirst("a img")?.tryGetAttr("abs:data-src", "abs:src")
|
||||
}
|
||||
|
||||
override fun popularAnimeNextPageSelector() = null
|
||||
|
||||
// =============================== Latest ===============================
|
||||
override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/page/$page", headers)
|
||||
|
||||
override fun latestUpdatesSelector() = "div.ContainerEps > article.EpsItem > a"
|
||||
|
||||
override fun latestUpdatesFromElement(element: Element) = SAnime.create().apply {
|
||||
setUrlWithoutDomain(element.attr("href"))
|
||||
title = element.attr("title")
|
||||
thumbnail_url = element.selectFirst("div.EpsItemImg > img")?.tryGetAttr("abs:data-src", "abs:src")
|
||||
}
|
||||
|
||||
override fun latestUpdatesNextPageSelector() = "div.ContainerEps a.next.page-numbers"
|
||||
|
||||
// =============================== Search ===============================
|
||||
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
|
||||
val url = "$baseUrl/page".toHttpUrl().newBuilder()
|
||||
.addPathSegment(page.toString())
|
||||
.addQueryParameter("s", query)
|
||||
.build()
|
||||
|
||||
return GET(url, headers = headers)
|
||||
}
|
||||
|
||||
override fun searchAnimeSelector() = "div.ContainerEps > article.AniItem > a"
|
||||
|
||||
override fun searchAnimeFromElement(element: Element) = SAnime.create().apply {
|
||||
setUrlWithoutDomain(element.attr("href"))
|
||||
title = element.attr("title").substringBefore(" – Todos os Epis")
|
||||
thumbnail_url = element.selectFirst("div.AniItemImg > img")?.tryGetAttr("abs:data-src", "abs:src")
|
||||
}
|
||||
|
||||
override fun searchAnimeNextPageSelector() = "div.ContainerEps a.next.page-numbers"
|
||||
|
||||
// =========================== Anime Details ============================
|
||||
override fun animeDetailsParse(document: Document): SAnime {
|
||||
val doc = getRealDoc(document)
|
||||
|
||||
return SAnime.create().apply {
|
||||
setUrlWithoutDomain(doc.location())
|
||||
title = doc.title().substringBefore(" – Todos os Epis")
|
||||
thumbnail_url = doc.selectFirst("#capaAnime > img")?.tryGetAttr("abs:data-src", "abs:src")
|
||||
description = doc.selectFirst("#sinopse2")?.text()
|
||||
|
||||
with(doc.selectFirst("div.boxAnimeSobre")!!) {
|
||||
artist = getInfo("Estúdio")
|
||||
author = getInfo("Autor") ?: getInfo("Diretor")
|
||||
genre = getInfo("Tags")
|
||||
status = parseStatus(getInfo("Episódios"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================== Episodes ==============================
|
||||
override fun episodeListParse(response: Response): List<SEpisode> {
|
||||
return getRealDoc(response.asJsoup())
|
||||
.select(episodeListSelector())
|
||||
.map(::episodeFromElement)
|
||||
.reversed()
|
||||
}
|
||||
|
||||
override fun episodeListSelector() = "#lAnimes a"
|
||||
|
||||
override fun episodeFromElement(element: Element) = SEpisode.create().apply {
|
||||
setUrlWithoutDomain(element.selectFirst("a")!!.attr("href"))
|
||||
element.selectFirst("a")!!.attr("title")
|
||||
.substringBeforeLast(" – Final")
|
||||
.substringAfterLast(" ").let {
|
||||
name = it.trim()
|
||||
episode_number = name.toFloatOrNull() ?: 1F
|
||||
}
|
||||
}
|
||||
|
||||
// ============================ Video Links =============================
|
||||
override fun videoListParse(response: Response): List<Video> {
|
||||
val document = response.asJsoup()
|
||||
|
||||
return document.select("div.videoBox div.aba")
|
||||
.parallelCatchingFlatMapBlocking {
|
||||
val format = document.selectFirst("a[href=#" + it.attr("id") + "]")?.text()
|
||||
?: "default"
|
||||
|
||||
val quality = when (format) {
|
||||
"SD" -> "360p"
|
||||
"HD" -> "720p"
|
||||
"FHD" -> "1080p"
|
||||
else -> format
|
||||
}
|
||||
val iframeSrc = it.selectFirst("iframe")?.tryGetAttr("data-litespeed-src", "src")
|
||||
if (!iframeSrc.isNullOrBlank()) {
|
||||
return@parallelCatchingFlatMapBlocking getVideosFromURL(iframeSrc, quality)
|
||||
}
|
||||
it.select("script").mapNotNull {
|
||||
var javascript = it.attr("src")
|
||||
?.substringAfter(";base64,")
|
||||
?.substringBefore('"')
|
||||
?.let { String(Base64.decode(it, Base64.DEFAULT)) }
|
||||
|
||||
if (javascript.isNullOrBlank()) {
|
||||
javascript = it.data()
|
||||
}
|
||||
|
||||
if (javascript.isNullOrBlank() || "file:" !in javascript) {
|
||||
return@mapNotNull null
|
||||
}
|
||||
|
||||
val videoUrl = javascript.substringAfter("file:\"").substringBefore('"')
|
||||
|
||||
Video(videoUrl, quality, videoUrl)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val bloggerExtractor by lazy { BloggerExtractor(client) }
|
||||
private fun getVideosFromURL(url: String, quality: String?): List<Video> {
|
||||
return when {
|
||||
"blogger.com" in url -> bloggerExtractor.videosFromUrl(url, headers)
|
||||
else -> emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
override fun videoListSelector(): String {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override fun videoFromElement(element: Element): Video {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override fun videoUrlParse(document: Document): String {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
// ============================== Settings ==============================
|
||||
override fun setupPreferenceScreen(screen: PreferenceScreen) {
|
||||
ListPreference(screen.context).apply {
|
||||
key = PREF_QUALITY_KEY
|
||||
title = PREF_QUALITY_TITLE
|
||||
entries = PREF_QUALITY_VALUES
|
||||
entryValues = PREF_QUALITY_VALUES
|
||||
setDefaultValue(PREF_QUALITY_DEFAULT)
|
||||
summary = "%s"
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
val selected = newValue as String
|
||||
val index = findIndexOfValue(selected)
|
||||
val entry = entryValues[index] as String
|
||||
preferences.edit().putString(key, entry).commit()
|
||||
}
|
||||
}.also(screen::addPreference)
|
||||
}
|
||||
|
||||
override fun List<Video>.sort(): List<Video> {
|
||||
val quality = preferences.getString(PREF_QUALITY_KEY, PREF_QUALITY_DEFAULT)!!
|
||||
return sortedWith(
|
||||
compareBy(
|
||||
{ it.quality.contains(quality) },
|
||||
{ REGEX_QUALITY.find(it.quality)?.groupValues?.get(1)?.toIntOrNull() ?: 0 },
|
||||
),
|
||||
).reversed()
|
||||
}
|
||||
|
||||
// ============================= Utilities ==============================
|
||||
private fun getRealDoc(document: Document): Document {
|
||||
val menu = document.selectFirst("div.spr.i-lista")
|
||||
if (menu != null) {
|
||||
val originalUrl = menu.parent()!!.attr("href")
|
||||
val response = client.newCall(GET(originalUrl, headers)).execute()
|
||||
return response.asJsoup()
|
||||
}
|
||||
|
||||
return document
|
||||
}
|
||||
|
||||
private fun parseStatus(statusString: String?): Int {
|
||||
return when {
|
||||
statusString?.trim()?.lowercase() == "em lançamento" -> SAnime.ONGOING
|
||||
statusString?.trim()?.lowercase() == "em andamento" -> SAnime.ONGOING
|
||||
statusString?.trim()?.let { REGEX_NUMBER.matches(it) } == true -> SAnime.COMPLETED
|
||||
else -> SAnime.UNKNOWN
|
||||
}
|
||||
}
|
||||
|
||||
private fun Element.getInfo(key: String): String? {
|
||||
return selectFirst("div.boxAnimeSobreLinha:has(b:contains($key))")?.run {
|
||||
text()
|
||||
.substringAfter(":")
|
||||
.trim()
|
||||
.takeUnless { it.isBlank() || it == "???" }
|
||||
}
|
||||
}
|
||||
|
||||
private fun Element.tryGetAttr(vararg attributeKeys: String): String? {
|
||||
val attributeKey = attributeKeys.first { hasAttr(it) }
|
||||
return attributeKey?.let { attr(attributeKey) }
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val REGEX_QUALITY by lazy { Regex("""(\d+)p""") }
|
||||
private val REGEX_NUMBER by lazy { Regex("""\d+""") }
|
||||
|
||||
private const val PREF_QUALITY_KEY = "preferred_quality"
|
||||
private const val PREF_QUALITY_TITLE = "Qualidade preferida"
|
||||
private const val PREF_QUALITY_DEFAULT = "720p"
|
||||
private val PREF_QUALITY_VALUES = arrayOf("360p", "720p", "1080p")
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'Animes CX'
|
||||
extClass = '.AnimesCX'
|
||||
extVersionCode = 1
|
||||
extVersionCode = 2
|
||||
isNsfw = true
|
||||
}
|
||||
|
||||
|
|
|
@ -100,8 +100,11 @@ class AnimesCX : ParsedAnimeHttpSource(), ConfigurableAnimeSource {
|
|||
}
|
||||
|
||||
private fun searchAnimeByIdParse(response: Response): AnimesPage {
|
||||
val details = animeDetailsParse(response.asJsoup())
|
||||
.apply { setUrlWithoutDomain(response.request.url.toString()) }
|
||||
val details = animeDetailsParse(response.asJsoup()).apply {
|
||||
setUrlWithoutDomain(response.request.url.toString())
|
||||
initialized = true
|
||||
}
|
||||
|
||||
return AnimesPage(listOf(details), false)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'Animes Digital'
|
||||
extClass = '.AnimesDigital'
|
||||
extVersionCode = 3
|
||||
extVersionCode = 4
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -80,7 +80,11 @@ class AnimesDigital : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||
}
|
||||
|
||||
private fun searchAnimeByIdParse(response: Response): AnimesPage {
|
||||
val details = animeDetailsParse(response.asJsoup())
|
||||
val details = animeDetailsParse(response.asJsoup()).apply {
|
||||
setUrlWithoutDomain(response.request.url.toString())
|
||||
initialized = true
|
||||
}
|
||||
|
||||
return AnimesPage(listOf(details), false)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
ext {
|
||||
extName = 'Animes Games'
|
||||
extClass = '.AnimesGames'
|
||||
extVersionCode = 2
|
||||
extVersionCode = 3
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
||||
dependencies {
|
||||
implementation(project(':lib:blogger-extractor'))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -79,7 +79,11 @@ class AnimesGames : ParsedAnimeHttpSource() {
|
|||
}
|
||||
|
||||
private fun searchAnimeByIdParse(response: Response): AnimesPage {
|
||||
val details = animeDetailsParse(response.asJsoup())
|
||||
val details = animeDetailsParse(response.asJsoup()).apply {
|
||||
setUrlWithoutDomain(response.request.url.toString())
|
||||
initialized = true
|
||||
}
|
||||
|
||||
return AnimesPage(listOf(details), false)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
ext {
|
||||
extName = 'AnimesOnline'
|
||||
extClass = '.AnimesOnline'
|
||||
extName = 'Bakashi'
|
||||
extClass = '.Bakashi'
|
||||
themePkg = 'dooplay'
|
||||
baseUrl = 'https://animesonline.nz'
|
||||
overrideVersionCode = 9
|
||||
baseUrl = 'https://bakashi.tv'
|
||||
overrideVersionCode = 10
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
@ -14,4 +14,4 @@ dependencies {
|
|||
implementation(project(":lib:streamwish-extractor"))
|
||||
implementation(project(":lib:mixdrop-extractor"))
|
||||
implementation(project(":lib:streamtape-extractor"))
|
||||
}
|
||||
}
|
||||
|
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 4.7 KiB |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 5 KiB After Width: | Height: | Size: 6.7 KiB |
Before Width: | Height: | Size: 9 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 18 KiB |
|
@ -1,6 +1,6 @@
|
|||
package eu.kanade.tachiyomi.animeextension.pt.animesgratis
|
||||
|
||||
import eu.kanade.tachiyomi.animeextension.pt.animesgratis.extractors.AnimesOnlinePlayerExtractor
|
||||
import eu.kanade.tachiyomi.animeextension.pt.animesgratis.extractors.NoaExtractor
|
||||
import eu.kanade.tachiyomi.animeextension.pt.animesgratis.extractors.RuplayExtractor
|
||||
import eu.kanade.tachiyomi.animesource.model.SEpisode
|
||||
import eu.kanade.tachiyomi.animesource.model.Video
|
||||
|
@ -11,26 +11,24 @@ 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.network.POST
|
||||
import eu.kanade.tachiyomi.util.asJsoup
|
||||
import eu.kanade.tachiyomi.util.parallelCatchingFlatMapBlocking
|
||||
import okhttp3.FormBody
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import okhttp3.Response
|
||||
import org.jsoup.nodes.Document
|
||||
import org.jsoup.nodes.Element
|
||||
|
||||
class AnimesOnline : DooPlay(
|
||||
class Bakashi : DooPlay(
|
||||
"pt-BR",
|
||||
"AnimesOnline",
|
||||
"https://animesonline.nz",
|
||||
"Bakashi",
|
||||
"https://bakashi.tv",
|
||||
) {
|
||||
|
||||
override val id: Long = 2969482460524685571L
|
||||
|
||||
// ============================== Popular ===============================
|
||||
override fun popularAnimeSelector() = "div.sidebar.right article > a"
|
||||
override fun popularAnimeRequest(page: Int) = GET("$baseUrl/animes/")
|
||||
override fun popularAnimeSelector() = "div.items.featured article div.poster"
|
||||
override fun popularAnimeRequest(page: Int) = GET("$baseUrl/animes/", headers)
|
||||
|
||||
// =============================== Search ===============================
|
||||
override fun searchAnimeSelector() = "div.result-item article div.thumbnail > a"
|
||||
|
@ -64,7 +62,7 @@ class AnimesOnline : DooPlay(
|
|||
override val prefQualityEntries = prefQualityValues
|
||||
|
||||
private val ruplayExtractor by lazy { RuplayExtractor(client) }
|
||||
private val animesOnlineExtractor by lazy { AnimesOnlinePlayerExtractor(client) }
|
||||
private val noaExtractor by lazy { NoaExtractor(client, headers) }
|
||||
private val bloggerExtractor by lazy { BloggerExtractor(client) }
|
||||
private val filemoonExtractor by lazy { FilemoonExtractor(client) }
|
||||
private val streamTapeExtractor by lazy { StreamTapeExtractor(client) }
|
||||
|
@ -79,45 +77,29 @@ class AnimesOnline : DooPlay(
|
|||
"streamwish" in name -> streamWishExtractor.videosFromUrl(url)
|
||||
"filemoon" in name -> filemoonExtractor.videosFromUrl(url)
|
||||
"mixdrop" in name -> mixDropExtractor.videoFromUrl(url)
|
||||
"streamtape" in name ->
|
||||
streamTapeExtractor.videoFromUrl(url)
|
||||
?.let(::listOf)
|
||||
?: emptyList()
|
||||
"/player1/" in url || "/player2/" in url ->
|
||||
animesOnlineExtractor.videosFromUrl(url)
|
||||
"streamtape" in name -> streamTapeExtractor.videosFromUrl(url)
|
||||
"/noance/" in url || "/noa" in url -> noaExtractor.videosFromUrl(url)
|
||||
"/player/" in url -> bloggerExtractor.videosFromUrl(url, headers)
|
||||
else -> emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
private fun getPlayerUrl(player: Element): String? {
|
||||
val body = FormBody.Builder()
|
||||
.add("action", "doo_player_ajax")
|
||||
.add("post", player.attr("data-post"))
|
||||
.add("nume", player.attr("data-nume"))
|
||||
.add("type", player.attr("data-type"))
|
||||
.build()
|
||||
val playerId = player.attr("data-nume")
|
||||
val iframe = player.root().selectFirst("div#source-player-$playerId iframe")
|
||||
|
||||
return client.newCall(POST("$baseUrl/wp-admin/admin-ajax.php", headers, body))
|
||||
.execute()
|
||||
.let { response ->
|
||||
response.body.string()
|
||||
.substringAfter("\"embed_url\":\"")
|
||||
.substringBefore("\",")
|
||||
.replace("\\", "")
|
||||
.takeIf(String::isNotBlank)
|
||||
?.let {
|
||||
when {
|
||||
it.contains("$baseUrl/aviso/") ->
|
||||
it.toHttpUrl().queryParameter("url")
|
||||
else -> it
|
||||
}
|
||||
}
|
||||
return iframe?.attr("src")?.takeIf(String::isNotBlank)
|
||||
?.let {
|
||||
when {
|
||||
it.contains("/aviso/") ->
|
||||
it.toHttpUrl().queryParameter("url")
|
||||
else -> it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================== Filters ===============================
|
||||
override fun genresListRequest() = GET("$baseUrl/animes/")
|
||||
override fun genresListRequest() = popularAnimeRequest(0)
|
||||
override fun genresListSelector() = "div.filter > div.select:first-child option:not([disabled])"
|
||||
|
||||
override fun genresListParse(document: Document): Array<Pair<String, String>> {
|
|
@ -2,9 +2,10 @@ package eu.kanade.tachiyomi.animeextension.pt.animesgratis.extractors
|
|||
|
||||
import eu.kanade.tachiyomi.animesource.model.Video
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import okhttp3.Headers
|
||||
import okhttp3.OkHttpClient
|
||||
|
||||
class AnimesOnlinePlayerExtractor(private val client: OkHttpClient) {
|
||||
class NoaExtractor(private val client: OkHttpClient, private val headers: Headers) {
|
||||
fun videosFromUrl(url: String): List<Video> {
|
||||
return client.newCall(GET(url)).execute()
|
||||
.body.string()
|
||||
|
@ -18,7 +19,7 @@ class AnimesOnlinePlayerExtractor(private val client: OkHttpClient) {
|
|||
.substringAfter(":\"")
|
||||
.substringBefore('"')
|
||||
.replace("\\", "")
|
||||
Video(videoUrl, "Player - $label", videoUrl)
|
||||
Video(videoUrl, "Player - $label", videoUrl, headers)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'AnimesROLL'
|
||||
extClass = '.AnimesROLL'
|
||||
extVersionCode = 3
|
||||
extVersionCode = 4
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -55,7 +55,11 @@ class AnimesROLL : AnimeHttpSource() {
|
|||
|
||||
// =============================== Search ===============================
|
||||
private fun searchAnimeByPathParse(response: Response): AnimesPage {
|
||||
val details = animeDetailsParse(response)
|
||||
val details = animeDetailsParse(response).apply {
|
||||
setUrlWithoutDomain(response.request.url.toString())
|
||||
initialized = true
|
||||
}
|
||||
|
||||
return AnimesPage(listOf(details), false)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'AnimesTC'
|
||||
extClass = '.AnimesTC'
|
||||
extVersionCode = 6
|
||||
extVersionCode = 7
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -122,7 +122,11 @@ class AnimesTC : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||
}
|
||||
|
||||
private fun searchAnimeBySlugParse(response: Response): AnimesPage {
|
||||
val details = animeDetailsParse(response)
|
||||
val details = animeDetailsParse(response).apply {
|
||||
setUrlWithoutDomain(response.request.url.toString())
|
||||
initialized = true
|
||||
}
|
||||
|
||||
return AnimesPage(listOf(details), false)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'Better Anime'
|
||||
extClass = '.BetterAnime'
|
||||
extVersionCode = 10
|
||||
extVersionCode = 11
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -101,7 +101,11 @@ class BetterAnime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||
}
|
||||
|
||||
private fun searchAnimeByPathParse(response: Response): AnimesPage {
|
||||
val details = animeDetailsParse(response)
|
||||
val details = animeDetailsParse(response).apply {
|
||||
setUrlWithoutDomain(response.request.url.toString())
|
||||
initialized = true
|
||||
}
|
||||
|
||||
return AnimesPage(listOf(details), false)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'Donghua no Sekai'
|
||||
extClass = '.DonghuaNoSekai'
|
||||
extVersionCode = 2
|
||||
extVersionCode = 3
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -92,7 +92,11 @@ class DonghuaNoSekai : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||
}
|
||||
|
||||
private fun searchAnimeByIdParse(response: Response): AnimesPage {
|
||||
val details = animeDetailsParse(response.asJsoup())
|
||||
val details = animeDetailsParse(response.asJsoup()).apply {
|
||||
setUrlWithoutDomain(response.request.url.toString())
|
||||
initialized = true
|
||||
}
|
||||
|
||||
return AnimesPage(listOf(details), false)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'Doramogo'
|
||||
extClass = '.Doramogo'
|
||||
extVersionCode = 1
|
||||
extVersionCode = 2
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -71,7 +71,11 @@ class Doramogo : ParsedAnimeHttpSource() {
|
|||
}
|
||||
|
||||
private fun searchAnimeByIdParse(response: Response): AnimesPage {
|
||||
val details = animeDetailsParse(response.asJsoup())
|
||||
val details = animeDetailsParse(response.asJsoup()).apply {
|
||||
setUrlWithoutDomain(response.request.url.toString())
|
||||
initialized = true
|
||||
}
|
||||
|
||||
return AnimesPage(listOf(details), false)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'Flixei'
|
||||
extClass = '.Flixei'
|
||||
extVersionCode = 6
|
||||
extVersionCode = 7
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
@ -9,4 +9,4 @@ apply from: "$rootDir/common.gradle"
|
|||
dependencies {
|
||||
implementation(project(":lib:streamtape-extractor"))
|
||||
implementation(project(":lib:mixdrop-extractor"))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -105,7 +105,11 @@ class Flixei : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||
}
|
||||
|
||||
private fun searchAnimeByPathParse(response: Response): AnimesPage {
|
||||
val details = animeDetailsParse(response.asJsoup())
|
||||
val details = animeDetailsParse(response.asJsoup()).apply {
|
||||
setUrlWithoutDomain(response.request.url.toString())
|
||||
initialized = true
|
||||
}
|
||||
|
||||
return AnimesPage(listOf(details), false)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'HentaisTube'
|
||||
extClass = '.HentaisTube'
|
||||
extVersionCode = 2
|
||||
extVersionCode = 3
|
||||
isNsfw = true
|
||||
}
|
||||
|
||||
|
|
|
@ -111,7 +111,11 @@ class HentaisTube : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||
override fun getFilterList(): AnimeFilterList = HentaisTubeFilters.FILTER_LIST
|
||||
|
||||
private fun searchAnimeByIdParse(response: Response): AnimesPage {
|
||||
val details = animeDetailsParse(response.asJsoup())
|
||||
val details = animeDetailsParse(response.asJsoup()).apply {
|
||||
setUrlWithoutDomain(response.request.url.toString())
|
||||
initialized = true
|
||||
}
|
||||
|
||||
return AnimesPage(listOf(details), false)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'Hinata Soul'
|
||||
extClass = '.HinataSoul'
|
||||
extVersionCode = 4
|
||||
extVersionCode = 5
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -78,7 +78,11 @@ class HinataSoul : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||
}
|
||||
|
||||
private fun searchAnimeBySlugParse(response: Response): AnimesPage {
|
||||
val details = animeDetailsParse(response)
|
||||
val details = animeDetailsParse(response).apply {
|
||||
setUrlWithoutDomain(response.request.url.toString())
|
||||
initialized = true
|
||||
}
|
||||
|
||||
return AnimesPage(listOf(details), false)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'Megaflix'
|
||||
extClass = '.Megaflix'
|
||||
extVersionCode = 16
|
||||
extVersionCode = 17
|
||||
isNsfw = true
|
||||
}
|
||||
|
||||
|
@ -13,4 +13,4 @@ dependencies {
|
|||
implementation(project(":lib:playlist-utils"))
|
||||
// for mixdrop and megaflix
|
||||
implementation("dev.datlag.jsunpacker:jsunpacker:1.0.1")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -81,7 +81,11 @@ class Megaflix : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
|||
}
|
||||
|
||||
private fun searchAnimeByPathParse(response: Response): AnimesPage {
|
||||
val details = animeDetailsParse(response.asJsoup())
|
||||
val details = animeDetailsParse(response.asJsoup()).apply {
|
||||
setUrlWithoutDomain(response.request.url.toString())
|
||||
initialized = true
|
||||
}
|
||||
|
||||
return AnimesPage(listOf(details), false)
|
||||
}
|
||||
|
||||
|
|
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
After Width: | Height: | Size: 3 KiB |
BIN
src/pt/pobreflix/res/mipmap-mdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
src/pt/pobreflix/res/mipmap-xhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 4 KiB |
BIN
src/pt/pobreflix/res/mipmap-xxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 6.9 KiB |
BIN
src/pt/pobreflix/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
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)
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
ext {
|
||||
extName = 'Vizer.tv'
|
||||
extClass = '.Vizer'
|
||||
extVersionCode = 15
|
||||
extVersionCode = 16
|
||||
isNsfw = true
|
||||
}
|
||||
|
||||
|
|
|
@ -116,7 +116,11 @@ class Vizer : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||
}
|
||||
|
||||
private fun searchAnimeByPathParse(response: Response): AnimesPage {
|
||||
val details = animeDetailsParse(response)
|
||||
val details = animeDetailsParse(response).apply {
|
||||
setUrlWithoutDomain(response.request.url.toString())
|
||||
initialized = true
|
||||
}
|
||||
|
||||
return AnimesPage(listOf(details), false)
|
||||
}
|
||||
|
||||
|
|