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:
		| @ -284,55 +284,45 @@ object AoDParser { | ||||
|             //Log.i(javaClass.name, "New csrf token is $csrfToken") | ||||
|         } | ||||
|  | ||||
|         val pl = res.select("input.streamstarter_html5").first() | ||||
|         val primary = pl.attr("data-playlist") | ||||
|         val secondary = pl.attr("data-otherplaylist") | ||||
|         val secondaryIsOmU = secondary.contains("OmU", true) | ||||
|         val besides = res.select("div.besides").first() | ||||
|         val playlists = besides.select("input.streamstarter_html5").map { streamstarter -> | ||||
|             parsePlaylistAsync( | ||||
|                 streamstarter.attr("data-playlist"), | ||||
|                 streamstarter.attr("data-lang") | ||||
|             ) | ||||
|         }.awaitAll() | ||||
|  | ||||
|         // load primary and secondary playlist | ||||
|         val primaryPlaylist = parsePlaylistAsync(primary) | ||||
|         val secondaryPlaylist = parsePlaylistAsync(secondary) | ||||
|  | ||||
|         primaryPlaylist.await().playlist.forEach { ep -> | ||||
|             try { | ||||
|                 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) | ||||
|         playlists.forEach { aod -> | ||||
|             // TODO improve language handling | ||||
|             val locale = when (aod.extLanguage) { | ||||
|                 "ger" -> Locale.GERMAN | ||||
|                 "jap" -> Locale.JAPANESE | ||||
|                 else -> Locale.ROOT | ||||
|             } | ||||
|         } | ||||
|         Log.i(javaClass.name, "Loading primary playlist finished") | ||||
|  | ||||
|         secondaryPlaylist.await().playlist.forEach { ep -> | ||||
|             try { | ||||
|                 val episode = media.episodes.firstOrNull { it.id == ep.mediaid } | ||||
|  | ||||
|                 // if media contains already a episode with this id, add as secondary, else add as primary | ||||
|                 if (episode != null) { | ||||
|                     episode.secStreamUrl = ep.sources.first().file | ||||
|                     episode.secStreamOmU = secondaryIsOmU | ||||
|                 } else { | ||||
|                     media.episodes.add(Episode( | ||||
|                         id = ep.mediaid, | ||||
|                         secStreamUrl = ep.sources.first().file, | ||||
|                         secStreamOmU = secondaryIsOmU, | ||||
|                         posterUrl = ep.image, | ||||
|                         title = ep.title, | ||||
|                         description = ep.description, | ||||
|                         number = getNumberFromTitle(ep.title, media.type) | ||||
|                     )) | ||||
|             aod.playlist.forEach { ep -> | ||||
|                 try { | ||||
|                     if (media.hasEpisode(ep.mediaid)) { | ||||
|                         media.getEpisodeById(ep.mediaid).streams.add( | ||||
|                             Stream(ep.sources.first().file, locale) | ||||
|                         ) | ||||
|                     } else { | ||||
|                         media.episodes.add(Episode( | ||||
|                             id = ep.mediaid, | ||||
|                             streams = mutableListOf(Stream(ep.sources.first().file, locale)), | ||||
|                             posterUrl = ep.image, | ||||
|                             title = ep.title, | ||||
|                             description = ep.description, | ||||
|                             number = getNumberFromTitle(ep.title, media.type) | ||||
|                         )) | ||||
|                         println(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 | ||||
|         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 | ||||
|      */ | ||||
|     private fun parsePlaylistAsync(playlistPath: String): Deferred<AoDObject> { | ||||
|     private fun parsePlaylistAsync(playlistPath: String, language: String): Deferred<AoDObject> { | ||||
|         if (playlistPath == "[]") { | ||||
|             return CompletableDeferred(AoDObject(listOf())) | ||||
|             return CompletableDeferred(AoDObject(listOf(), language)) | ||||
|         } | ||||
|  | ||||
|         return GlobalScope.async(Dispatchers.IO) { | ||||
| @ -406,7 +396,9 @@ object AoDParser { | ||||
|                         description = it.asJsonObject.get("description").asString, | ||||
|                         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.Episode | ||||
| import org.mosad.teapod.util.Media | ||||
| import java.util.* | ||||
| import kotlin.collections.ArrayList | ||||
| import kotlin.properties.Delegates | ||||
|  | ||||
| /** | ||||
| @ -55,13 +57,10 @@ class PlayerViewModel : ViewModel() { | ||||
|      * If no stream is present, return empty string. | ||||
|      */ | ||||
|     fun autoSelectStream(episode: Episode): String { | ||||
|         return if ((Preferences.preferSecondary || episode.priStreamUrl.isEmpty()) && episode.secStreamOmU) { | ||||
|             episode.secStreamUrl | ||||
|         } else if (episode.priStreamUrl.isNotEmpty()) { | ||||
|             episode.priStreamUrl | ||||
|         return if (Preferences.preferSecondary) { | ||||
|             episode.getPreferredStream(Locale.JAPANESE).url | ||||
|         } 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 | ||||
|  | ||||
| import java.util.* | ||||
| import kotlin.collections.ArrayList | ||||
|  | ||||
| class DataTypes { | ||||
|     enum class MediaType { | ||||
|         OTHER, | ||||
| @ -47,7 +50,10 @@ data class Media( | ||||
|     val type: DataTypes.MediaType, | ||||
|     val info: Info = Info(), | ||||
|     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( | ||||
|     var title: String = "", | ||||
| @ -60,23 +66,37 @@ data class Info( | ||||
| ) | ||||
|  | ||||
| /** | ||||
|  * if secStreamOmU == true, then a secondary stream is present | ||||
|  * number = episode number (0..n) | ||||
|  */ | ||||
| data class Episode( | ||||
|     val id: Int = 0, | ||||
|     val streams: MutableList<Stream> = mutableListOf(), | ||||
|     var title: String = "", | ||||
|     var priStreamUrl: String = "", | ||||
|     var secStreamUrl: String = "", | ||||
|     var secStreamOmU: Boolean = false, | ||||
|     var posterUrl: String = "", | ||||
|     var description: String = "", | ||||
|     var shortDesc: String = "", | ||||
|     var number: Int = 0, | ||||
|     var watched: Boolean = false, | ||||
|     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( | ||||
|     val id: Int = 0, | ||||
|     val title: String = "", | ||||
| @ -86,7 +106,13 @@ data class TMDBResponse( | ||||
|     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( | ||||
|     val sources: List<Source>, | ||||
|  | ||||
| @ -23,10 +23,10 @@ class EpisodeItemAdapter(private val episodes: List<Episode>) : RecyclerView.Ada | ||||
|         val context = holder.binding.root.context | ||||
|         val ep = episodes[position] | ||||
|  | ||||
|         val titleText = if (ep.priStreamUrl.isEmpty() && ep.secStreamOmU) { | ||||
|             context.getString(R.string.component_episode_title_sub, ep.number, ep.description) | ||||
|         } else { | ||||
|         val titleText = if (ep.hasDub()) { | ||||
|             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 | ||||
|  | ||||
| @ -22,10 +22,10 @@ class PlayerEpisodeItemAdapter(private val episodes: List<Episode>) : RecyclerVi | ||||
|         val context = holder.binding.root.context | ||||
|         val ep = episodes[position] | ||||
|  | ||||
|         val titleText = if (ep.priStreamUrl.isEmpty() && ep.secStreamOmU) { | ||||
|             context.getString(R.string.component_episode_title_sub, ep.number, ep.description) | ||||
|         } else { | ||||
|         val titleText = if (ep.hasDub()) { | ||||
|             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 | ||||
|  | ||||
| @ -28,8 +28,8 @@ | ||||
|     <string name="info">Info</string> | ||||
|     <string name="info_about_desc">Version %1$s (%2$s)</string> | ||||
|     <string name="settings">Einstellungen</string> | ||||
|     <string name="settings_secondary">Bevorzuge alternativen Stream</string> | ||||
|     <string name="settings_secondary_desc">Untertitle-Stream verwenden, sofern vorhanden</string> | ||||
|     <string name="settings_secondary">Bevorzuge Japanisch (OmU)</string> | ||||
|     <string name="settings_secondary_desc">Japanisch verwenden, sofern vorhanden</string> | ||||
|     <string name="settings_autoplay">Autoplay</string> | ||||
|     <string name="settings_autoplay_desc">Nächste Episode automatisch abspielen</string> | ||||
|     <string name="theme">Design</string> | ||||
|  | ||||
| @ -37,8 +37,8 @@ | ||||
|     <string name="info_about" translatable="false">Teapod by @Seil0</string> | ||||
|     <string name="info_about_desc">Version %1$s (%2$s)</string> | ||||
|     <string name="settings">Settings</string> | ||||
|     <string name="settings_secondary">Prefer secondary (sub) stream</string> | ||||
|     <string name="settings_secondary_desc">Use the subtitles stream if present</string> | ||||
|     <string name="settings_secondary">Prefer japanese (sub)</string> | ||||
|     <string name="settings_secondary_desc">Use the japanese, if present</string> | ||||
|     <string name="settings_autoplay">Autoplay</string> | ||||
|     <string name="settings_autoplay_desc">Play next episode automatically</string> | ||||
|     <string name="theme">Theme</string> | ||||
|  | ||||
		Reference in New Issue
	
	Block a user