rework how different streams/languages per episode are handled
* potentially support more than 2 streams * part of language settings in player
This commit is contained in:
parent
8f60a30d61
commit
8a43567737
|
@ -284,55 +284,45 @@ object AoDParser {
|
||||||
//Log.i(javaClass.name, "New csrf token is $csrfToken")
|
//Log.i(javaClass.name, "New csrf token is $csrfToken")
|
||||||
}
|
}
|
||||||
|
|
||||||
val pl = res.select("input.streamstarter_html5").first()
|
val besides = res.select("div.besides").first()
|
||||||
val primary = pl.attr("data-playlist")
|
val playlists = besides.select("input.streamstarter_html5").map { streamstarter ->
|
||||||
val secondary = pl.attr("data-otherplaylist")
|
parsePlaylistAsync(
|
||||||
val secondaryIsOmU = secondary.contains("OmU", true)
|
streamstarter.attr("data-playlist"),
|
||||||
|
streamstarter.attr("data-lang")
|
||||||
|
)
|
||||||
|
}.awaitAll()
|
||||||
|
|
||||||
// load primary and secondary playlist
|
playlists.forEach { aod ->
|
||||||
val primaryPlaylist = parsePlaylistAsync(primary)
|
// TODO improve language handling
|
||||||
val secondaryPlaylist = parsePlaylistAsync(secondary)
|
val locale = when (aod.extLanguage) {
|
||||||
|
"ger" -> Locale.GERMAN
|
||||||
primaryPlaylist.await().playlist.forEach { ep ->
|
"jap" -> Locale.JAPANESE
|
||||||
try {
|
else -> Locale.ROOT
|
||||||
media.episodes.add(Episode(
|
|
||||||
id = ep.mediaid,
|
|
||||||
priStreamUrl = ep.sources.first().file,
|
|
||||||
posterUrl = ep.image,
|
|
||||||
title = ep.title,
|
|
||||||
description = ep.description,
|
|
||||||
number = getNumberFromTitle(ep.title, media.type)
|
|
||||||
))
|
|
||||||
} catch (ex: Exception) {
|
|
||||||
Log.w(javaClass.name, "Could not parse episode information.", ex)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
Log.i(javaClass.name, "Loading primary playlist finished")
|
|
||||||
|
|
||||||
secondaryPlaylist.await().playlist.forEach { ep ->
|
aod.playlist.forEach { ep ->
|
||||||
try {
|
try {
|
||||||
val episode = media.episodes.firstOrNull { it.id == ep.mediaid }
|
if (media.hasEpisode(ep.mediaid)) {
|
||||||
|
media.getEpisodeById(ep.mediaid).streams.add(
|
||||||
// if media contains already a episode with this id, add as secondary, else add as primary
|
Stream(ep.sources.first().file, locale)
|
||||||
if (episode != null) {
|
)
|
||||||
episode.secStreamUrl = ep.sources.first().file
|
} else {
|
||||||
episode.secStreamOmU = secondaryIsOmU
|
media.episodes.add(Episode(
|
||||||
} else {
|
id = ep.mediaid,
|
||||||
media.episodes.add(Episode(
|
streams = mutableListOf(Stream(ep.sources.first().file, locale)),
|
||||||
id = ep.mediaid,
|
posterUrl = ep.image,
|
||||||
secStreamUrl = ep.sources.first().file,
|
title = ep.title,
|
||||||
secStreamOmU = secondaryIsOmU,
|
description = ep.description,
|
||||||
posterUrl = ep.image,
|
number = getNumberFromTitle(ep.title, media.type)
|
||||||
title = ep.title,
|
))
|
||||||
description = ep.description,
|
println(getNumberFromTitle(ep.title, media.type))
|
||||||
number = getNumberFromTitle(ep.title, media.type)
|
}
|
||||||
))
|
} catch (ex: Exception) {
|
||||||
|
Log.w(javaClass.name, "Could not parse episode information.", ex)
|
||||||
}
|
}
|
||||||
} catch (ex: Exception) {
|
|
||||||
Log.w(javaClass.name, "Could not parse episode information.", ex)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Log.i(javaClass.name, "Loading secondary playlist finished")
|
Log.i(javaClass.name, "Loaded playlists successfully")
|
||||||
|
|
||||||
// parse additional info from the media page
|
// parse additional info from the media page
|
||||||
res.select("table.vertical-table").select("tr").forEach { row ->
|
res.select("table.vertical-table").select("tr").forEach { row ->
|
||||||
|
@ -371,9 +361,9 @@ object AoDParser {
|
||||||
/**
|
/**
|
||||||
* don't use Gson().fromJson() as we don't have any control over the api and it may change
|
* don't use Gson().fromJson() as we don't have any control over the api and it may change
|
||||||
*/
|
*/
|
||||||
private fun parsePlaylistAsync(playlistPath: String): Deferred<AoDObject> {
|
private fun parsePlaylistAsync(playlistPath: String, language: String): Deferred<AoDObject> {
|
||||||
if (playlistPath == "[]") {
|
if (playlistPath == "[]") {
|
||||||
return CompletableDeferred(AoDObject(listOf()))
|
return CompletableDeferred(AoDObject(listOf(), language))
|
||||||
}
|
}
|
||||||
|
|
||||||
return GlobalScope.async(Dispatchers.IO) {
|
return GlobalScope.async(Dispatchers.IO) {
|
||||||
|
@ -406,7 +396,9 @@ object AoDParser {
|
||||||
description = it.asJsonObject.get("description").asString,
|
description = it.asJsonObject.get("description").asString,
|
||||||
mediaid = it.asJsonObject.get("mediaid").asInt
|
mediaid = it.asJsonObject.get("mediaid").asInt
|
||||||
)
|
)
|
||||||
})
|
},
|
||||||
|
language
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,8 @@ import org.mosad.teapod.ui.fragments.MediaFragment
|
||||||
import org.mosad.teapod.util.DataTypes
|
import org.mosad.teapod.util.DataTypes
|
||||||
import org.mosad.teapod.util.Episode
|
import org.mosad.teapod.util.Episode
|
||||||
import org.mosad.teapod.util.Media
|
import org.mosad.teapod.util.Media
|
||||||
|
import java.util.*
|
||||||
|
import kotlin.collections.ArrayList
|
||||||
import kotlin.properties.Delegates
|
import kotlin.properties.Delegates
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -55,13 +57,10 @@ class PlayerViewModel : ViewModel() {
|
||||||
* If no stream is present, return empty string.
|
* If no stream is present, return empty string.
|
||||||
*/
|
*/
|
||||||
fun autoSelectStream(episode: Episode): String {
|
fun autoSelectStream(episode: Episode): String {
|
||||||
return if ((Preferences.preferSecondary || episode.priStreamUrl.isEmpty()) && episode.secStreamOmU) {
|
return if (Preferences.preferSecondary) {
|
||||||
episode.secStreamUrl
|
episode.getPreferredStream(Locale.JAPANESE).url
|
||||||
} else if (episode.priStreamUrl.isNotEmpty()) {
|
|
||||||
episode.priStreamUrl
|
|
||||||
} else {
|
} else {
|
||||||
Log.e(javaClass.name, "No stream url set. ${episode.id}")
|
episode.getPreferredStream(Locale.GERMAN).url
|
||||||
""
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
package org.mosad.teapod.util
|
package org.mosad.teapod.util
|
||||||
|
|
||||||
|
import java.util.*
|
||||||
|
import kotlin.collections.ArrayList
|
||||||
|
|
||||||
class DataTypes {
|
class DataTypes {
|
||||||
enum class MediaType {
|
enum class MediaType {
|
||||||
OTHER,
|
OTHER,
|
||||||
|
@ -47,7 +50,10 @@ data class Media(
|
||||||
val type: DataTypes.MediaType,
|
val type: DataTypes.MediaType,
|
||||||
val info: Info = Info(),
|
val info: Info = Info(),
|
||||||
var episodes: ArrayList<Episode> = arrayListOf()
|
var episodes: ArrayList<Episode> = arrayListOf()
|
||||||
)
|
) {
|
||||||
|
fun hasEpisode(id: Int) = episodes.any { it.id == id }
|
||||||
|
fun getEpisodeById(id: Int) = episodes.first { it.id == id }
|
||||||
|
}
|
||||||
|
|
||||||
data class Info(
|
data class Info(
|
||||||
var title: String = "",
|
var title: String = "",
|
||||||
|
@ -60,23 +66,37 @@ data class Info(
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* if secStreamOmU == true, then a secondary stream is present
|
|
||||||
* number = episode number (0..n)
|
* number = episode number (0..n)
|
||||||
*/
|
*/
|
||||||
data class Episode(
|
data class Episode(
|
||||||
val id: Int = 0,
|
val id: Int = 0,
|
||||||
|
val streams: MutableList<Stream> = mutableListOf(),
|
||||||
var title: String = "",
|
var title: String = "",
|
||||||
var priStreamUrl: String = "",
|
|
||||||
var secStreamUrl: String = "",
|
|
||||||
var secStreamOmU: Boolean = false,
|
|
||||||
var posterUrl: String = "",
|
var posterUrl: String = "",
|
||||||
var description: String = "",
|
var description: String = "",
|
||||||
var shortDesc: String = "",
|
var shortDesc: String = "",
|
||||||
var number: Int = 0,
|
var number: Int = 0,
|
||||||
var watched: Boolean = false,
|
var watched: Boolean = false,
|
||||||
var watchedCallback: String = ""
|
var watchedCallback: String = ""
|
||||||
|
) {
|
||||||
|
/**
|
||||||
|
* get the preferred stream
|
||||||
|
* @return the preferred stream, if not present use the first stream
|
||||||
|
*/
|
||||||
|
fun getPreferredStream(language: Locale) =
|
||||||
|
streams.firstOrNull { it.language == language } ?: streams.first()
|
||||||
|
|
||||||
|
fun hasDub() = streams.any { it.language == Locale.GERMAN }
|
||||||
|
}
|
||||||
|
|
||||||
|
data class Stream(
|
||||||
|
val url: String,
|
||||||
|
val language : Locale
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* this class is used for tmdb responses
|
||||||
|
*/
|
||||||
data class TMDBResponse(
|
data class TMDBResponse(
|
||||||
val id: Int = 0,
|
val id: Int = 0,
|
||||||
val title: String = "",
|
val title: String = "",
|
||||||
|
@ -86,7 +106,13 @@ data class TMDBResponse(
|
||||||
var runtime: Int = 0
|
var runtime: Int = 0
|
||||||
)
|
)
|
||||||
|
|
||||||
data class AoDObject(val playlist: List<Playlist>)
|
/**
|
||||||
|
* this class is used to represent the aod json API?
|
||||||
|
*/
|
||||||
|
data class AoDObject(
|
||||||
|
val playlist: List<Playlist>,
|
||||||
|
val extLanguage: String
|
||||||
|
)
|
||||||
|
|
||||||
data class Playlist(
|
data class Playlist(
|
||||||
val sources: List<Source>,
|
val sources: List<Source>,
|
||||||
|
|
|
@ -23,10 +23,10 @@ class EpisodeItemAdapter(private val episodes: List<Episode>) : RecyclerView.Ada
|
||||||
val context = holder.binding.root.context
|
val context = holder.binding.root.context
|
||||||
val ep = episodes[position]
|
val ep = episodes[position]
|
||||||
|
|
||||||
val titleText = if (ep.priStreamUrl.isEmpty() && ep.secStreamOmU) {
|
val titleText = if (ep.hasDub()) {
|
||||||
context.getString(R.string.component_episode_title_sub, ep.number, ep.description)
|
|
||||||
} else {
|
|
||||||
context.getString(R.string.component_episode_title, ep.number, ep.description)
|
context.getString(R.string.component_episode_title, ep.number, ep.description)
|
||||||
|
} else {
|
||||||
|
context.getString(R.string.component_episode_title_sub, ep.number, ep.description)
|
||||||
}
|
}
|
||||||
|
|
||||||
holder.binding.textEpisodeTitle.text = titleText
|
holder.binding.textEpisodeTitle.text = titleText
|
||||||
|
|
|
@ -22,10 +22,10 @@ class PlayerEpisodeItemAdapter(private val episodes: List<Episode>) : RecyclerVi
|
||||||
val context = holder.binding.root.context
|
val context = holder.binding.root.context
|
||||||
val ep = episodes[position]
|
val ep = episodes[position]
|
||||||
|
|
||||||
val titleText = if (ep.priStreamUrl.isEmpty() && ep.secStreamOmU) {
|
val titleText = if (ep.hasDub()) {
|
||||||
context.getString(R.string.component_episode_title_sub, ep.number, ep.description)
|
|
||||||
} else {
|
|
||||||
context.getString(R.string.component_episode_title, ep.number, ep.description)
|
context.getString(R.string.component_episode_title, ep.number, ep.description)
|
||||||
|
} else {
|
||||||
|
context.getString(R.string.component_episode_title_sub, ep.number, ep.description)
|
||||||
}
|
}
|
||||||
|
|
||||||
holder.binding.textEpisodeTitle2.text = titleText
|
holder.binding.textEpisodeTitle2.text = titleText
|
||||||
|
|
|
@ -28,8 +28,8 @@
|
||||||
<string name="info">Info</string>
|
<string name="info">Info</string>
|
||||||
<string name="info_about_desc">Version %1$s (%2$s)</string>
|
<string name="info_about_desc">Version %1$s (%2$s)</string>
|
||||||
<string name="settings">Einstellungen</string>
|
<string name="settings">Einstellungen</string>
|
||||||
<string name="settings_secondary">Bevorzuge alternativen Stream</string>
|
<string name="settings_secondary">Bevorzuge Japanisch (OmU)</string>
|
||||||
<string name="settings_secondary_desc">Untertitle-Stream verwenden, sofern vorhanden</string>
|
<string name="settings_secondary_desc">Japanisch verwenden, sofern vorhanden</string>
|
||||||
<string name="settings_autoplay">Autoplay</string>
|
<string name="settings_autoplay">Autoplay</string>
|
||||||
<string name="settings_autoplay_desc">Nächste Episode automatisch abspielen</string>
|
<string name="settings_autoplay_desc">Nächste Episode automatisch abspielen</string>
|
||||||
<string name="theme">Design</string>
|
<string name="theme">Design</string>
|
||||||
|
|
|
@ -37,8 +37,8 @@
|
||||||
<string name="info_about" translatable="false">Teapod by @Seil0</string>
|
<string name="info_about" translatable="false">Teapod by @Seil0</string>
|
||||||
<string name="info_about_desc">Version %1$s (%2$s)</string>
|
<string name="info_about_desc">Version %1$s (%2$s)</string>
|
||||||
<string name="settings">Settings</string>
|
<string name="settings">Settings</string>
|
||||||
<string name="settings_secondary">Prefer secondary (sub) stream</string>
|
<string name="settings_secondary">Prefer japanese (sub)</string>
|
||||||
<string name="settings_secondary_desc">Use the subtitles stream if present</string>
|
<string name="settings_secondary_desc">Use the japanese, if present</string>
|
||||||
<string name="settings_autoplay">Autoplay</string>
|
<string name="settings_autoplay">Autoplay</string>
|
||||||
<string name="settings_autoplay_desc">Play next episode automatically</string>
|
<string name="settings_autoplay_desc">Play next episode automatically</string>
|
||||||
<string name="theme">Theme</string>
|
<string name="theme">Theme</string>
|
||||||
|
|
Loading…
Reference in New Issue