Goyabu: Fix popular animes tab, episodes list and extractor (#740)
This commit is contained in:
@ -1,2 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest package="eu.kanade.tachiyomi.animeextension"/>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="eu.kanade.tachiyomi.animeextension">
|
||||
|
||||
<application>
|
||||
<activity
|
||||
android:name=".pt.goyabu.GYUrlActivity"
|
||||
android:excludeFromRecents="true"
|
||||
android:exported="true"
|
||||
android:theme="@android:style/Theme.NoDisplay">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
|
||||
<data
|
||||
android:host="goyabu.com"
|
||||
android:pathPattern="/assistir/..*"
|
||||
android:scheme="https" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
||||
|
@ -6,7 +6,7 @@ ext {
|
||||
extName = 'Goyabu'
|
||||
pkgNameSuffix = 'pt.goyabu'
|
||||
extClass = '.Goyabu'
|
||||
extVersionCode = 2
|
||||
extVersionCode = 3
|
||||
libVersion = '13'
|
||||
}
|
||||
|
||||
|
@ -2,10 +2,9 @@ package eu.kanade.tachiyomi.animeextension.pt.goyabu
|
||||
|
||||
object GYConstants {
|
||||
const val ACCEPT_LANGUAGE = "pt-BR,pt;q=0.9,en-US;q=0.8,en;q=0.7"
|
||||
const val USER_AGENT = "Mozilla/5.0 (Linux; Android 10; SM-A307GT Build/QP1A.190711.020;) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/81.0.4044.138 Mobile Safari/537.36"
|
||||
const val USER_AGENT = "Mozilla/5.0 (Linux; Android 10; SM-A307GT Build/QP1A.190711.020) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/103.0.5060.71 Mobile Safari/537.36"
|
||||
const val PREFERRED_QUALITY = "preferred_quality"
|
||||
const val PREFERRED_PLAYER = "preferred_player"
|
||||
const val PREFIX_SEARCH_SLUG = "slug:"
|
||||
val QUALITY_LIST = arrayOf("SD", "HD")
|
||||
val PLAYER_NAMES = arrayOf("Player 1", "Player 2")
|
||||
val PLAYER_REGEX = Regex("""label: "(\w+)",.*file: "(.*?)"""")
|
||||
}
|
||||
|
@ -0,0 +1,42 @@
|
||||
package eu.kanade.tachiyomi.animeextension.pt.goyabu
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
/**
|
||||
* Springboard that accepts https://goyabu.com/assistir/<slug> intents
|
||||
* and redirects them to the main Aniyomi process.
|
||||
*/
|
||||
class GYUrlActivity : Activity() {
|
||||
|
||||
private val TAG = "GYUrlActivity"
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
val pathSegments = intent?.data?.pathSegments
|
||||
if (pathSegments != null && pathSegments.size > 1) {
|
||||
val slug = pathSegments[1]
|
||||
val searchQuery = GYConstants.PREFIX_SEARCH_SLUG + slug
|
||||
val mainIntent = Intent().apply {
|
||||
action = "eu.kanade.tachiyomi.ANIMESEARCH"
|
||||
putExtra("query", searchQuery)
|
||||
putExtra("filter", packageName)
|
||||
}
|
||||
|
||||
try {
|
||||
startActivity(mainIntent)
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
Log.e(TAG, e.toString())
|
||||
}
|
||||
} else {
|
||||
Log.e(TAG, "could not parse uri from intent $intent")
|
||||
}
|
||||
|
||||
finish()
|
||||
exitProcess(0)
|
||||
}
|
||||
}
|
@ -6,7 +6,6 @@ import androidx.preference.ListPreference
|
||||
import androidx.preference.PreferenceScreen
|
||||
import eu.kanade.tachiyomi.animeextension.pt.goyabu.GYFilters.applyFilterParams
|
||||
import eu.kanade.tachiyomi.animeextension.pt.goyabu.extractors.PlayerOneExtractor
|
||||
import eu.kanade.tachiyomi.animeextension.pt.goyabu.extractors.PlayerTwoExtractor
|
||||
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
|
||||
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
|
||||
import eu.kanade.tachiyomi.animesource.model.AnimesPage
|
||||
@ -15,6 +14,7 @@ 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.network.GET
|
||||
import eu.kanade.tachiyomi.network.asObservableSuccess
|
||||
import eu.kanade.tachiyomi.util.asJsoup
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.json.Json
|
||||
@ -28,6 +28,9 @@ import rx.Observable
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import java.lang.Exception
|
||||
import java.text.ParseException
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
|
||||
class Goyabu : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
||||
|
||||
@ -56,7 +59,10 @@ class Goyabu : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
||||
.add("Referer", baseUrl)
|
||||
|
||||
// ============================== Popular ===============================
|
||||
override fun popularAnimeSelector(): String = "div.item > div.anime-episode"
|
||||
private fun popularAnimeContainerSelector(): String = "div.index-size > div.episodes-container"
|
||||
|
||||
override fun popularAnimeSelector(): String = "div.anime-episode"
|
||||
|
||||
override fun popularAnimeRequest(page: Int): Request = GET(baseUrl)
|
||||
|
||||
override fun popularAnimeFromElement(element: Element): SAnime {
|
||||
@ -71,7 +77,7 @@ class Goyabu : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
||||
|
||||
override fun popularAnimeParse(response: Response): AnimesPage {
|
||||
val document = response.asJsoup()
|
||||
val content = document.select("div.episodes-container").get(2)
|
||||
val content = document.selectFirst(popularAnimeContainerSelector())
|
||||
val animes = content.select(popularAnimeSelector()).map { element ->
|
||||
popularAnimeFromElement(element)
|
||||
}
|
||||
@ -79,7 +85,7 @@ class Goyabu : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
||||
}
|
||||
|
||||
// ============================== Episodes ==============================
|
||||
override fun episodeListSelector(): String = "div.episodes-container > div.anime-episode"
|
||||
override fun episodeListSelector(): String = "div.episodes-container > div.row > a"
|
||||
|
||||
private fun getAllEps(response: Response): List<SEpisode> {
|
||||
val epList = mutableListOf<SEpisode>()
|
||||
@ -100,17 +106,20 @@ class Goyabu : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
||||
}
|
||||
|
||||
override fun episodeListParse(response: Response): List<SEpisode> {
|
||||
return getAllEps(response).reversed()
|
||||
return getAllEps(response)
|
||||
}
|
||||
|
||||
override fun episodeFromElement(element: Element): SEpisode {
|
||||
val episode = SEpisode.create()
|
||||
|
||||
episode.setUrlWithoutDomain(element.selectFirst("a").attr("href"))
|
||||
val epName = element.selectFirst("h3").text().substringAfter("– ")
|
||||
episode.name = epName
|
||||
val info_div = element.selectFirst("div.chaps-infs")
|
||||
episode.setUrlWithoutDomain(element.attr("href"))
|
||||
val epName = info_div.ownText()
|
||||
episode.name = epName.substringAfter("– ")
|
||||
episode.date_upload = info_div.selectFirst("small").ownText().toDate()
|
||||
episode.episode_number = try {
|
||||
epName.substringAfter(" ").substringBefore(" ").toFloat()
|
||||
epName.substringAfter("#")
|
||||
.substringBefore(" ")
|
||||
.toFloat()
|
||||
} catch (e: NumberFormatException) { 0F }
|
||||
return episode
|
||||
}
|
||||
@ -119,16 +128,21 @@ class Goyabu : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
||||
override fun videoListParse(response: Response): List<Video> {
|
||||
val document: Document = response.asJsoup()
|
||||
val html: String = document.html()
|
||||
val videoList = PlayerOneExtractor()
|
||||
.videoListFromHtml(html)
|
||||
.toMutableList()
|
||||
val iframe = document.selectFirst("div#tab-2 > iframe")
|
||||
if (iframe != null) {
|
||||
val playerUrl = iframe.attr("src")
|
||||
val video = PlayerTwoExtractor(client).videoFromPlayerUrl(playerUrl)
|
||||
if (video != null)
|
||||
videoList.add(video)
|
||||
val videoList = mutableListOf<Video>()
|
||||
val kanraElement = document.selectFirst("script:containsData(kanra.dev)")
|
||||
if (kanraElement != null) {
|
||||
val kanraUrl = kanraElement.html()
|
||||
.substringAfter("src='")
|
||||
.substringBefore("'")
|
||||
val kanraVideos =
|
||||
PlayerOneExtractor(client).videoListFromKanraUrl(kanraUrl)
|
||||
videoList.addAll(kanraVideos)
|
||||
} else {
|
||||
val extracted = PlayerOneExtractor()
|
||||
.videoListFromHtml(html)
|
||||
videoList.addAll(extracted)
|
||||
}
|
||||
|
||||
return videoList
|
||||
}
|
||||
|
||||
@ -153,7 +167,22 @@ class Goyabu : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
||||
|
||||
override fun searchAnimeParse(response: Response) = throw Exception("not used")
|
||||
|
||||
private fun searchAnimeBySlugParse(response: Response, slug: String): AnimesPage {
|
||||
val details = animeDetailsParse(response)
|
||||
details.url = "/assistir/$slug"
|
||||
return AnimesPage(listOf(details), false)
|
||||
}
|
||||
|
||||
override fun fetchSearchAnime(page: Int, query: String, filters: AnimeFilterList): Observable<AnimesPage> {
|
||||
if (query.startsWith(GYConstants.PREFIX_SEARCH_SLUG)) {
|
||||
val slug = query.removePrefix(GYConstants.PREFIX_SEARCH_SLUG)
|
||||
return client.newCall(GET("$baseUrl/assistir/$slug"))
|
||||
.asObservableSuccess()
|
||||
.map { response ->
|
||||
searchAnimeBySlugParse(response, slug)
|
||||
}
|
||||
}
|
||||
// else
|
||||
val params = GYFilters.getSearchParameters(filters)
|
||||
return Observable.just(searchAnimeRequest(page, query, params))
|
||||
}
|
||||
@ -218,22 +247,6 @@ class Goyabu : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
||||
|
||||
// ============================== Settings ==============================
|
||||
override fun setupPreferenceScreen(screen: PreferenceScreen) {
|
||||
|
||||
val videoPlayerPref = ListPreference(screen.context).apply {
|
||||
key = GYConstants.PREFERRED_PLAYER
|
||||
title = "Player preferido"
|
||||
entries = GYConstants.PLAYER_NAMES
|
||||
entryValues = GYConstants.PLAYER_NAMES
|
||||
setDefaultValue(GYConstants.PLAYER_NAMES.first())
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
val videoQualityPref = ListPreference(screen.context).apply {
|
||||
key = GYConstants.PREFERRED_QUALITY
|
||||
title = "Qualidade preferida"
|
||||
@ -248,7 +261,6 @@ class Goyabu : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
||||
preferences.edit().putString(key, entry).commit()
|
||||
}
|
||||
}
|
||||
screen.addPreference(videoPlayerPref)
|
||||
screen.addPreference(videoQualityPref)
|
||||
}
|
||||
|
||||
@ -268,10 +280,10 @@ class Goyabu : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
||||
|
||||
private fun Element.getInfo(item: String, cut: Boolean = true): String {
|
||||
val text = this.selectFirst("div.anime-info-right div:contains($item)").text()
|
||||
if (cut)
|
||||
return text.substringAfter(": ")
|
||||
else
|
||||
return text.substringAfter(" ")
|
||||
return when {
|
||||
cut -> text.substringAfter(": ")
|
||||
else -> text.substringAfter(" ")
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseStatus(statusString: String?): Int {
|
||||
@ -282,24 +294,35 @@ class Goyabu : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun String.toDate(): Long {
|
||||
return try {
|
||||
DATE_FORMATTER.parse(this)?.time ?: 0L
|
||||
} catch (e: ParseException) {
|
||||
0L
|
||||
}
|
||||
}
|
||||
|
||||
override fun List<Video>.sort(): List<Video> {
|
||||
val quality = preferences.getString(GYConstants.PREFERRED_QUALITY, null)
|
||||
val player = preferences.getString(GYConstants.PREFERRED_PLAYER, null)
|
||||
val newList = mutableListOf<Video>()
|
||||
var preferred = 0
|
||||
for (video in this) {
|
||||
when {
|
||||
quality != null && video.quality.contains(quality) -> {
|
||||
if (quality != null) {
|
||||
val newList = mutableListOf<Video>()
|
||||
var preferred = 0
|
||||
for (video in this) {
|
||||
if (video.quality.equals(quality)) {
|
||||
newList.add(preferred, video)
|
||||
preferred++
|
||||
} else {
|
||||
newList.add(video)
|
||||
}
|
||||
player != null && video.quality.contains(player) -> {
|
||||
newList.add(preferred, video)
|
||||
preferred++
|
||||
}
|
||||
else -> newList.add(video)
|
||||
}
|
||||
return newList
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val DATE_FORMATTER by lazy {
|
||||
SimpleDateFormat("dd/MM/yy", Locale.ENGLISH)
|
||||
}
|
||||
return newList
|
||||
}
|
||||
}
|
||||
|
@ -2,16 +2,33 @@ package eu.kanade.tachiyomi.animeextension.pt.goyabu.extractors
|
||||
|
||||
import eu.kanade.tachiyomi.animeextension.pt.goyabu.GYConstants
|
||||
import eu.kanade.tachiyomi.animesource.model.Video
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import okhttp3.Headers
|
||||
import okhttp3.OkHttpClient
|
||||
|
||||
class PlayerOneExtractor {
|
||||
class PlayerOneExtractor(private val client: OkHttpClient? = null) {
|
||||
|
||||
private val KANRA_REGEX = Regex("""(?s)label: "(\w+)".*?file: "(.*?)"""")
|
||||
private val PREFIX = "Player 1"
|
||||
|
||||
fun videoListFromHtml(html: String): List<Video> {
|
||||
return GYConstants.PLAYER_REGEX.findAll(html).map { it ->
|
||||
fun videoListFromHtml(
|
||||
html: String,
|
||||
regex: Regex = GYConstants.PLAYER_REGEX,
|
||||
headers: Headers? = null
|
||||
): List<Video> {
|
||||
return regex.findAll(html).map { it ->
|
||||
val quality = "$PREFIX (${it.groupValues[1]})"
|
||||
val videoUrl = it.groupValues[2]
|
||||
Video(videoUrl, quality, videoUrl)
|
||||
Video(videoUrl, quality, videoUrl, null, headers)
|
||||
}.toList()
|
||||
}
|
||||
|
||||
fun videoListFromKanraUrl(url: String): List<Video> {
|
||||
val headers = Headers.headersOf(
|
||||
"User-Agent", GYConstants.USER_AGENT
|
||||
)
|
||||
val res = client!!.newCall(GET(url, headers)).execute()
|
||||
val html = res.body?.string().orEmpty()
|
||||
return videoListFromHtml(html, KANRA_REGEX, headers)
|
||||
}
|
||||
}
|
||||
|
@ -1,25 +0,0 @@
|
||||
package eu.kanade.tachiyomi.animeextension.pt.goyabu.extractors
|
||||
|
||||
import eu.kanade.tachiyomi.animeextension.pt.goyabu.GYConstants
|
||||
import eu.kanade.tachiyomi.animesource.model.Video
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import okhttp3.Headers
|
||||
import okhttp3.OkHttpClient
|
||||
|
||||
class PlayerTwoExtractor(private val client: OkHttpClient) {
|
||||
|
||||
private val PREFIX = "Player 2"
|
||||
|
||||
fun videoFromPlayerUrl(url: String): Video? {
|
||||
val headers = Headers.headersOf("User-Agent", GYConstants.USER_AGENT)
|
||||
val res = client.newCall(GET(url, headers)).execute()
|
||||
val html = res.body?.string().orEmpty()
|
||||
val match = GYConstants.PLAYER_REGEX.find(html)
|
||||
if (match == null) {
|
||||
return match
|
||||
}
|
||||
val quality = "$PREFIX (${match.groupValues[1]})"
|
||||
val videoUrl = match.groupValues[2]
|
||||
return Video(videoUrl, quality, videoUrl)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user