PurayMoe: Fix video extractor (#966)
* PurayMoe: Fix video extractor and add a new config * PurayMoe: Bump version
This commit is contained in:
@ -6,11 +6,8 @@ ext {
|
|||||||
extName = 'Puray.moe'
|
extName = 'Puray.moe'
|
||||||
pkgNameSuffix = 'pt.puraymoe'
|
pkgNameSuffix = 'pt.puraymoe'
|
||||||
extClass = '.PurayMoe'
|
extClass = '.PurayMoe'
|
||||||
extVersionCode = 3
|
extVersionCode = 4
|
||||||
libVersion = '13'
|
libVersion = '13'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
apply from: "$rootDir/common.gradle"
|
apply from: "$rootDir/common.gradle"
|
||||||
|
@ -4,6 +4,7 @@ import android.app.Application
|
|||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import androidx.preference.ListPreference
|
import androidx.preference.ListPreference
|
||||||
import androidx.preference.PreferenceScreen
|
import androidx.preference.PreferenceScreen
|
||||||
|
import androidx.preference.SwitchPreferenceCompat
|
||||||
import eu.kanade.tachiyomi.animeextension.pt.puraymoe.dto.AnimeDto
|
import eu.kanade.tachiyomi.animeextension.pt.puraymoe.dto.AnimeDto
|
||||||
import eu.kanade.tachiyomi.animeextension.pt.puraymoe.dto.EpisodeDataDto
|
import eu.kanade.tachiyomi.animeextension.pt.puraymoe.dto.EpisodeDataDto
|
||||||
import eu.kanade.tachiyomi.animeextension.pt.puraymoe.dto.MinimalEpisodeDto
|
import eu.kanade.tachiyomi.animeextension.pt.puraymoe.dto.MinimalEpisodeDto
|
||||||
@ -29,7 +30,6 @@ import rx.Observable
|
|||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
import java.lang.Exception
|
import java.lang.Exception
|
||||||
import java.text.ParseException
|
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
|
||||||
@ -80,7 +80,7 @@ class PurayMoe : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
override fun fetchEpisodeList(anime: SAnime): Observable<List<SEpisode>> {
|
override fun fetchEpisodeList(anime: SAnime): Observable<List<SEpisode>> {
|
||||||
val seasonsList: SeasonListDto = getSeasonList(anime)
|
val seasonsList: SeasonListDto = getSeasonList(anime)
|
||||||
|
|
||||||
val showOnly = preferences.getString(CONF_SHOW_ONLY, null) ?: ""
|
val showOnly = preferences.getString(PREF_SHOW_ONLY_KEY, null) ?: ""
|
||||||
val dub_item = ANIME_TYPES_VALUES.elementAt(1)
|
val dub_item = ANIME_TYPES_VALUES.elementAt(1)
|
||||||
val sub_item = ANIME_TYPES_VALUES.last()
|
val sub_item = ANIME_TYPES_VALUES.last()
|
||||||
var filteredSeasons = seasonsList.seasons.filter {
|
var filteredSeasons = seasonsList.seasons.filter {
|
||||||
@ -118,9 +118,9 @@ class PurayMoe : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
return episodesData.episodes.map {
|
return episodesData.episodes.map {
|
||||||
val episode = SEpisode.create()
|
val episode = SEpisode.create()
|
||||||
episode.name = "Temp $seasonNumber ($format) EP ${it.ep_number}: ${it.name}"
|
episode.name = "Temp $seasonNumber ($format) EP ${it.ep_number}: ${it.name}"
|
||||||
episode.episode_number = try {
|
episode.episode_number = runCatching {
|
||||||
it.ep_number.toFloat()
|
it.ep_number.toFloat()
|
||||||
} catch (e: NumberFormatException) { 0F }
|
}.getOrNull() ?: 0F
|
||||||
episode.url = it.id.toString()
|
episode.url = it.id.toString()
|
||||||
episode.date_upload = it.release_date.toDate()
|
episode.date_upload = it.release_date.toDate()
|
||||||
episode
|
episode
|
||||||
@ -129,15 +129,26 @@ class PurayMoe : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
|
|
||||||
// ============================ Video Links =============================
|
// ============================ Video Links =============================
|
||||||
|
|
||||||
override fun videoListRequest(episode: SEpisode): Request =
|
override fun videoListRequest(episode: SEpisode): Request {
|
||||||
GET("$API_URL/episodios/${episode.url}/m3u8/mp4/")
|
val url = "$API_URL/episodios/${episode.url}/m3u8"
|
||||||
|
val usePlaylist = preferences.getBoolean(PREF_USE_PLAYLIST_KEY, true)
|
||||||
|
return if (usePlaylist) GET(url) else GET("$url/mp4/")
|
||||||
|
}
|
||||||
|
|
||||||
override fun videoListParse(response: Response): List<Video> {
|
override fun videoListParse(response: Response): List<Video> {
|
||||||
|
|
||||||
val episodeObject = response.parseAs<MinimalEpisodeDto>()
|
val episodeObject = response.parseAs<MinimalEpisodeDto>()
|
||||||
return episodeObject.streams!!.map {
|
val usePlaylist = preferences.getBoolean(PREF_USE_PLAYLIST_KEY, true)
|
||||||
val quality = "${it.quality.last()}p"
|
return if (usePlaylist) {
|
||||||
Video(it.url, quality, it.url)
|
client.newCall(GET(episodeObject.url))
|
||||||
}.toList()
|
.execute()
|
||||||
|
.toVideoList()
|
||||||
|
} else {
|
||||||
|
episodeObject.streams?.map {
|
||||||
|
val quality = "${it.quality.last()}p"
|
||||||
|
Video(it.url, quality, it.url)
|
||||||
|
} ?: emptyList<Video>()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// =============================== Search ===============================
|
// =============================== Search ===============================
|
||||||
@ -219,7 +230,7 @@ class PurayMoe : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
|
|
||||||
override fun latestUpdatesParse(response: Response): AnimesPage {
|
override fun latestUpdatesParse(response: Response): AnimesPage {
|
||||||
val parsedData = response.parseAs<List<MinimalEpisodeDto>>()
|
val parsedData = response.parseAs<List<MinimalEpisodeDto>>()
|
||||||
val animes = parsedData.map(::getAnimeFromEpisode).toList()
|
val animes = parsedData.map(::getAnimeFromEpisode)
|
||||||
return AnimesPage(animes, false)
|
return AnimesPage(animes, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -234,25 +245,11 @@ class PurayMoe : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
|
|
||||||
override fun setupPreferenceScreen(screen: PreferenceScreen) {
|
override fun setupPreferenceScreen(screen: PreferenceScreen) {
|
||||||
val videoQualityPref = ListPreference(screen.context).apply {
|
val videoQualityPref = ListPreference(screen.context).apply {
|
||||||
key = CONF_PREFERRED_QUALITY
|
key = PREF_QUALITY_KEY
|
||||||
title = "Qualidade preferida"
|
title = PREF_QUALITY_TITLE
|
||||||
entries = QUALITY_LIST
|
entries = PREF_QUALITY_VALUES
|
||||||
entryValues = QUALITY_LIST
|
entryValues = PREF_QUALITY_VALUES
|
||||||
setDefaultValue(QUALITY_LIST.last())
|
setDefaultValue("720p")
|
||||||
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 showOnlyPref = ListPreference(screen.context).apply {
|
|
||||||
key = CONF_SHOW_ONLY
|
|
||||||
title = "Mostrar apenas episódios:"
|
|
||||||
entries = ANIME_TYPES
|
|
||||||
entryValues = ANIME_TYPES_VALUES
|
|
||||||
setDefaultValue(ANIME_TYPES_VALUES.first())
|
|
||||||
summary = "%s"
|
summary = "%s"
|
||||||
setOnPreferenceChangeListener { _, newValue ->
|
setOnPreferenceChangeListener { _, newValue ->
|
||||||
val selected = newValue as String
|
val selected = newValue as String
|
||||||
@ -262,8 +259,35 @@ class PurayMoe : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val showOnlyPref = ListPreference(screen.context).apply {
|
||||||
|
key = PREF_SHOW_ONLY_KEY
|
||||||
|
title = PREF_SHOW_ONLY_TITLE
|
||||||
|
entries = ANIME_TYPES_ENTRIES
|
||||||
|
entryValues = ANIME_TYPES_VALUES
|
||||||
|
setDefaultValue("")
|
||||||
|
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 usePlaylistPref = SwitchPreferenceCompat(screen.context).apply {
|
||||||
|
key = PREF_USE_PLAYLIST_KEY
|
||||||
|
title = PREF_USE_PLAYLIST_TITLE
|
||||||
|
setDefaultValue(true)
|
||||||
|
|
||||||
|
setOnPreferenceChangeListener { _, newValue ->
|
||||||
|
val checkValue = newValue as Boolean
|
||||||
|
preferences.edit().putBoolean(key, checkValue).commit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
screen.addPreference(videoQualityPref)
|
screen.addPreference(videoQualityPref)
|
||||||
screen.addPreference(showOnlyPref)
|
screen.addPreference(showOnlyPref)
|
||||||
|
screen.addPreference(usePlaylistPref)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================= Utilities ==============================
|
// ============================= Utilities ==============================
|
||||||
@ -273,18 +297,29 @@ class PurayMoe : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
return json.decodeFromString(responseBody)
|
return json.decodeFromString(responseBody)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun String.toDate(): Long {
|
private fun Response.toVideoList(): List<Video> {
|
||||||
return try {
|
val responseBody = body?.string().orEmpty()
|
||||||
DATE_FORMATTER.parse(this)?.time ?: 0L
|
val separator = "#EXT-X-STREAM-INF:"
|
||||||
} catch (e: ParseException) {
|
return responseBody.substringAfter(separator).split(separator).map {
|
||||||
0L
|
val quality = it.substringAfter("RESOLUTION=")
|
||||||
|
.substringAfter("x")
|
||||||
|
.substringBefore("\n")
|
||||||
|
.substringBefore(",") + "p"
|
||||||
|
val videoUrl = it.substringAfter("\n").substringBefore("\n")
|
||||||
|
Video(videoUrl, quality, videoUrl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun String.toDate(): Long {
|
||||||
|
return runCatching {
|
||||||
|
DATE_FORMATTER.parse(this)?.time ?: 0L
|
||||||
|
}.getOrNull() ?: 0L
|
||||||
|
}
|
||||||
|
|
||||||
private fun String.getId(): String = this.substringAfterLast("/")
|
private fun String.getId(): String = this.substringAfterLast("/")
|
||||||
|
|
||||||
override fun List<Video>.sort(): List<Video> {
|
override fun List<Video>.sort(): List<Video> {
|
||||||
val quality = preferences.getString(CONF_PREFERRED_QUALITY, null)
|
val quality = preferences.getString(PREF_QUALITY_KEY, null)
|
||||||
if (quality != null) {
|
if (quality != null) {
|
||||||
val newList = mutableListOf<Video>()
|
val newList = mutableListOf<Video>()
|
||||||
var preferred = 0
|
var preferred = 0
|
||||||
@ -302,23 +337,28 @@ class PurayMoe : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val API_URL = "https://api.puray.moe"
|
|
||||||
private const val ACCEPT_LANGUAGE = "pt-BR,pt;q=0.9,en-US;q=0.8,en;q=0.7"
|
|
||||||
private const val CONF_PREFERRED_QUALITY = "preferred_quality"
|
|
||||||
private const val CONF_SHOW_ONLY = "show_only"
|
|
||||||
|
|
||||||
private val DATE_FORMATTER by lazy {
|
private val DATE_FORMATTER by lazy {
|
||||||
SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH)
|
SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val QUALITY_LIST = arrayOf(
|
private const val API_URL = "https://api.puray.moe"
|
||||||
|
private const val ACCEPT_LANGUAGE = "pt-BR,pt;q=0.9,en-US;q=0.8,en;q=0.7"
|
||||||
|
|
||||||
|
private const val PREF_QUALITY_KEY = "preferred_quality"
|
||||||
|
private const val PREF_QUALITY_TITLE = "Qualidade preferida"
|
||||||
|
private val PREF_QUALITY_VALUES = arrayOf(
|
||||||
"240p", "360p",
|
"240p", "360p",
|
||||||
"480p", "720p", "1080p"
|
"480p", "720p", "1080p"
|
||||||
)
|
)
|
||||||
|
|
||||||
private val ANIME_TYPES = arrayOf("Todos", "Dublados", "Legendados")
|
private const val PREF_SHOW_ONLY_KEY = "show_only"
|
||||||
|
private const val PREF_SHOW_ONLY_TITLE = "Mostrar apenas episódios:"
|
||||||
|
private val ANIME_TYPES_ENTRIES = arrayOf("Todos", "Dublados", "Legendados")
|
||||||
private val ANIME_TYPES_VALUES = arrayOf("", "dub", "sub")
|
private val ANIME_TYPES_VALUES = arrayOf("", "dub", "sub")
|
||||||
|
|
||||||
|
private const val PREF_USE_PLAYLIST_KEY = "use_m3u8_playlist"
|
||||||
|
private const val PREF_USE_PLAYLIST_TITLE = "Usar/Ativar playlist M3U8(HLS)"
|
||||||
|
|
||||||
const val PREFIX_SEARCH = "id:"
|
const val PREFIX_SEARCH = "id:"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,7 +66,8 @@ data class EpisodeDto(
|
|||||||
data class MinimalEpisodeDto(
|
data class MinimalEpisodeDto(
|
||||||
@SerialName("temporada")
|
@SerialName("temporada")
|
||||||
val season: MinimalSeasonDto? = null,
|
val season: MinimalSeasonDto? = null,
|
||||||
val streams: List<VideoDto>? = null
|
val streams: List<VideoDto>? = null,
|
||||||
|
val url: String = ""
|
||||||
)
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
|
Reference in New Issue
Block a user