chore: Sync with latest commits

This commit is contained in:
WebDitto 2024-06-23 11:00:51 -03:00
parent 9b96273df0
commit 56c945d716
115 changed files with 2230 additions and 445 deletions

View file

@ -1,7 +1,7 @@
ext {
extName = 'AniDong'
extClass = '.AniDong'
extVersionCode = 1
extVersionCode = 2
}
apply from: "$rootDir/common.gradle"

View file

@ -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)
}

View file

@ -1,7 +1,7 @@
ext {
extName = 'Anime Fire'
extClass = '.AnimeFire'
extVersionCode = 6
extVersionCode = 7
}
apply from: "$rootDir/common.gradle"

View file

@ -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)
}

View 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'))
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View file

@ -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")
}
}

View file

@ -1,7 +1,7 @@
ext {
extName = 'Animes CX'
extClass = '.AnimesCX'
extVersionCode = 1
extVersionCode = 2
isNsfw = true
}

View file

@ -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)
}

View file

@ -1,7 +1,7 @@
ext {
extName = 'Animes Digital'
extClass = '.AnimesDigital'
extVersionCode = 3
extVersionCode = 4
}
apply from: "$rootDir/common.gradle"

View file

@ -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)
}

View file

@ -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'))
}
}

View file

@ -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)
}

View file

@ -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"))
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Before After
Before After

View file

@ -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>> {

View file

@ -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)
}
}
}

View file

@ -1,7 +1,7 @@
ext {
extName = 'AnimesROLL'
extClass = '.AnimesROLL'
extVersionCode = 3
extVersionCode = 4
}
apply from: "$rootDir/common.gradle"

View file

@ -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)
}

View file

@ -1,7 +1,7 @@
ext {
extName = 'AnimesTC'
extClass = '.AnimesTC'
extVersionCode = 6
extVersionCode = 7
}
apply from: "$rootDir/common.gradle"

View file

@ -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)
}

View file

@ -1,7 +1,7 @@
ext {
extName = 'Better Anime'
extClass = '.BetterAnime'
extVersionCode = 10
extVersionCode = 11
}
apply from: "$rootDir/common.gradle"

View file

@ -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)
}

View file

@ -1,7 +1,7 @@
ext {
extName = 'Donghua no Sekai'
extClass = '.DonghuaNoSekai'
extVersionCode = 2
extVersionCode = 3
}
apply from: "$rootDir/common.gradle"

View file

@ -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)
}

View file

@ -1,7 +1,7 @@
ext {
extName = 'Doramogo'
extClass = '.Doramogo'
extVersionCode = 1
extVersionCode = 2
}
apply from: "$rootDir/common.gradle"

View file

@ -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)
}

View file

@ -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"))
}
}

View file

@ -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)
}

View file

@ -1,7 +1,7 @@
ext {
extName = 'HentaisTube'
extClass = '.HentaisTube'
extVersionCode = 2
extVersionCode = 3
isNsfw = true
}

View file

@ -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)
}

View file

@ -1,7 +1,7 @@
ext {
extName = 'Hinata Soul'
extClass = '.HinataSoul'
extVersionCode = 4
extVersionCode = 5
}
apply from: "$rootDir/common.gradle"

View file

@ -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)
}

View file

@ -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")
}
}

View file

@ -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)
}

View 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"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

View file

@ -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()
}
}
}

View file

@ -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" })
}
}

View file

@ -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" },
)
}
}

View file

@ -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)
}

View file

@ -1,7 +1,7 @@
ext {
extName = 'Vizer.tv'
extClass = '.Vizer'
extVersionCode = 15
extVersionCode = 16
isNsfw = true
}

View file

@ -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)
}