Goyabu: Fix popular animes tab, episodes list and extractor (#740)

This commit is contained in:
Claudemirovsky
2022-08-08 08:52:46 -03:00
committed by GitHub
parent c3110f90da
commit 7bfa721848
7 changed files with 164 additions and 86 deletions

View File

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

View File

@ -6,7 +6,7 @@ ext {
extName = 'Goyabu'
pkgNameSuffix = 'pt.goyabu'
extClass = '.Goyabu'
extVersionCode = 2
extVersionCode = 3
libVersion = '13'
}

View File

@ -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: "(.*?)"""")
}

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

View File

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

View File

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

View File

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