diff --git a/app/src/main/java/org/mosad/teapod/parser/AoDParser.kt b/app/src/main/java/org/mosad/teapod/parser/AoDParser.kt index a283a68..a5397d3 100644 --- a/app/src/main/java/org/mosad/teapod/parser/AoDParser.kt +++ b/app/src/main/java/org/mosad/teapod/parser/AoDParser.kt @@ -110,18 +110,18 @@ object AoDParser { * get a media by it's ID (int) * @return Media */ - fun getMediaById(mediaId: Int): Media { + suspend fun getMediaById(mediaId: Int): Media { val media = mediaList.first { it.id == mediaId } if (media.episodes.isEmpty()) { - loadStreams(media) + loadStreams(media).join() } return media } // TODO don't use jsoup here - fun sendCallback(callbackPath: String) = GlobalScope.launch { + fun sendCallback(callbackPath: String) = GlobalScope.launch(Dispatchers.IO) { val headers = mutableMapOf( Pair("Accept", "application/json, text/javascript, */*; q=0.01"), Pair("Accept-Language", "de,en-US;q=0.7,en;q=0.3"), @@ -131,13 +131,11 @@ object AoDParser { ) try { - withContext(Dispatchers.IO) { - Jsoup.connect(baseUrl + callbackPath) - .ignoreContentType(true) - .cookies(sessionCookies) - .headers(headers) - .execute() - } + Jsoup.connect(baseUrl + callbackPath) + .ignoreContentType(true) + .cookies(sessionCookies) + .headers(headers) + .execute() } catch (ex: IOException) { Log.e(javaClass.name, "Callback for $callbackPath failed.", ex) } @@ -213,38 +211,62 @@ object AoDParser { * load streams for the media path, movies have one episode * @param media is used as call ba reference */ - private fun loadStreams(media: Media) = runBlocking { + private suspend fun loadStreams(media: Media) = GlobalScope.launch(Dispatchers.IO) { if (sessionCookies.isEmpty()) login() if (!loginSuccess) { Log.w(javaClass.name, "Login, was not successful.") - return@runBlocking + return@launch } - withContext(Dispatchers.Default) { + // get the media page + val res = Jsoup.connect(baseUrl + media.link) + .cookies(sessionCookies) + .get() - // get the media page - val res = Jsoup.connect(baseUrl + media.link) - .cookies(sessionCookies) - .get() + //println(res) - //println(res) + if (csrfToken.isEmpty()) { + csrfToken = res.select("meta[name=csrf-token]").attr("content") + //Log.i(javaClass.name, "New csrf token is $csrfToken") + } - if (csrfToken.isEmpty()) { - csrfToken = res.select("meta[name=csrf-token]").attr("content") - //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) + + // load primary and secondary playlist + val primaryPlaylist = parsePlaylistAsync(primary) + val secondaryPlaylist = parsePlaylistAsync(secondary) + + primaryPlaylist.await().playlist.forEach { ep -> + val epNumber = if (media.type == MediaType.TVSHOW) { + ep.title.substringAfter(", Ep. ").toInt() + } else { + 0 } - 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) + media.episodes.add( + Episode( + id = ep.mediaid, + priStreamUrl = ep.sources.first().file, + posterUrl = ep.image, + title = ep.title, + description = ep.description, + number = epNumber + ) + ) + } + Log.i(javaClass.name, "Loading primary playlist finished") - // load primary and secondary playlist - val primaryPlaylist = parsePlaylistAsync(primary) - val secondaryPlaylist = parsePlaylistAsync(secondary) + secondaryPlaylist.await().playlist.forEach { ep -> + val episode = media.episodes.firstOrNull { it.id == ep.mediaid } - primaryPlaylist.await().playlist.forEach { ep -> + if (episode != null) { + episode.secStreamUrl = ep.sources.first().file + episode.secStreamOmU = secondaryIsOmU + } else { val epNumber = if (media.type == MediaType.TVSHOW) { ep.title.substringAfter(", Ep. ").toInt() } else { @@ -254,7 +276,8 @@ object AoDParser { media.episodes.add( Episode( id = ep.mediaid, - priStreamUrl = ep.sources.first().file, + secStreamUrl = ep.sources.first().file, + secStreamOmU = secondaryIsOmU, posterUrl = ep.image, title = ep.title, description = ep.description, @@ -262,69 +285,40 @@ object AoDParser { ) ) } - Log.i(javaClass.name, "Loading primary playlist finished") + } + Log.i(javaClass.name, "Loading secondary playlist finished") - secondaryPlaylist.await().playlist.forEach { ep -> - val episode = media.episodes.firstOrNull { it.id == ep.mediaid } - - if (episode != null) { - episode.secStreamUrl = ep.sources.first().file - episode.secStreamOmU = secondaryIsOmU - } else { - val epNumber = if (media.type == MediaType.TVSHOW) { - ep.title.substringAfter(", Ep. ").toInt() - } else { - 0 - } - - media.episodes.add( - Episode( - id = ep.mediaid, - secStreamUrl = ep.sources.first().file, - secStreamOmU = secondaryIsOmU, - posterUrl = ep.image, - title = ep.title, - description = ep.description, - number = epNumber - ) - ) + // parse additional info from the media page + res.select("table.vertical-table").select("tr").forEach { row -> + when (row.select("th").text().toLowerCase(Locale.ROOT)) { + "produktionsjahr" -> media.info.year = row.select("td").text().toInt() + "fsk" -> media.info.age = row.select("td").text().toInt() + "episodenanzahl" -> { + media.info.episodesCount = row.select("td").text() + .substringBefore("/") + .filter { it.isDigit() } + .toInt() } } - Log.i(javaClass.name, "Loading secondary playlist finished") + } - // parse additional info from the media page - res.select("table.vertical-table").select("tr").forEach { row -> - when (row.select("th").text().toLowerCase(Locale.ROOT)) { - "produktionsjahr" -> media.info.year = row.select("td").text().toInt() - "fsk" -> media.info.age = row.select("td").text().toInt() - "episodenanzahl" -> { - media.info.episodesCount = row.select("td").text() - .substringBefore("/") - .filter{ it.isDigit() } - .toInt() + // parse additional information for tv shows + if (media.type == MediaType.TVSHOW) { + res.select("div.three-box-container > div.episodebox").forEach { episodebox -> + // make sure the episode has a streaming link + if (episodebox.select("input.streamstarter_html5").isNotEmpty()) { + val episodeId = episodebox.select("div.flip-front").attr("id").substringAfter("-").toInt() + val episodeShortDesc = episodebox.select("p.episodebox-shorttext").text() + val episodeWatched = episodebox.select("div.episodebox-icons > div").hasClass("status-icon-orange") + val episodeWatchedCallback = episodebox.select("input.streamstarter_html5").eachAttr("data-playlist").first() + + media.episodes.firstOrNull { it.id == episodeId }?.apply { + shortDesc = episodeShortDesc + watched = episodeWatched + watchedCallback = episodeWatchedCallback } } } - - // parse additional information for tv shows - if (media.type == MediaType.TVSHOW) { - res.select("div.three-box-container > div.episodebox").forEach { episodebox -> - // make sure the episode has a streaming link - if (episodebox.select("input.streamstarter_html5").isNotEmpty()) { - val episodeId = episodebox.select("div.flip-front").attr("id").substringAfter("-").toInt() - val episodeShortDesc = episodebox.select("p.episodebox-shorttext").text() - val episodeWatched = episodebox.select("div.episodebox-icons > div").hasClass("status-icon-orange") - val episodeWatchedCallback = episodebox.select("input.streamstarter_html5").eachAttr("data-playlist").first() - - media.episodes.firstOrNull { it.id == episodeId }?.apply { - shortDesc = episodeShortDesc - watched = episodeWatched - watchedCallback = episodeWatchedCallback - } - } - } - } - } } @@ -336,7 +330,7 @@ object AoDParser { return CompletableDeferred(AoDObject(listOf())) } - return GlobalScope.async { + return GlobalScope.async(Dispatchers.IO) { val headers = mutableMapOf( Pair("Accept", "application/json, text/javascript, */*; q=0.01"), Pair("Accept-Language", "de,en-US;q=0.7,en;q=0.3"), diff --git a/app/src/main/java/org/mosad/teapod/player/PlayerViewModel.kt b/app/src/main/java/org/mosad/teapod/player/PlayerViewModel.kt index 45bafd2..196c20b 100644 --- a/app/src/main/java/org/mosad/teapod/player/PlayerViewModel.kt +++ b/app/src/main/java/org/mosad/teapod/player/PlayerViewModel.kt @@ -1,6 +1,7 @@ package org.mosad.teapod.player import androidx.lifecycle.ViewModel +import kotlinx.coroutines.runBlocking import org.mosad.teapod.parser.AoDParser import org.mosad.teapod.ui.fragments.MediaFragment import org.mosad.teapod.util.DataTypes @@ -25,7 +26,10 @@ class PlayerViewModel : ViewModel() { mediaId = iMediaId episodeId = iEpisodeId - media = AoDParser.getMediaById(mediaId) + runBlocking { + media = AoDParser.getMediaById(mediaId) + } + currentEpisode = media.episodes.first { it.id == episodeId } nextEpisode = selectNextEpisode() } diff --git a/app/src/main/java/org/mosad/teapod/ui/fragments/MediaFragment.kt b/app/src/main/java/org/mosad/teapod/ui/fragments/MediaFragment.kt index 978e0df..d9ce5f2 100644 --- a/app/src/main/java/org/mosad/teapod/ui/fragments/MediaFragment.kt +++ b/app/src/main/java/org/mosad/teapod/ui/fragments/MediaFragment.kt @@ -49,12 +49,12 @@ class MediaFragment(private val mediaId: Int) : Fragment() { super.onViewCreated(view, savedInstanceState) binding.frameLoading.visibility = View.VISIBLE - GlobalScope.launch { + GlobalScope.launch(Dispatchers.Main) { // load the streams for the selected media media = AoDParser.getMediaById(mediaId) tmdb = TMDBApiController().search(media.info.title, media.type) - withContext(Dispatchers.Main) { + if (this@MediaFragment.isAdded) { updateGUI() initActions() } diff --git a/app/src/main/java/org/mosad/teapod/util/TMDBApiController.kt b/app/src/main/java/org/mosad/teapod/util/TMDBApiController.kt index 33728c3..bb76090 100644 --- a/app/src/main/java/org/mosad/teapod/util/TMDBApiController.kt +++ b/app/src/main/java/org/mosad/teapod/util/TMDBApiController.kt @@ -3,9 +3,7 @@ package org.mosad.teapod.util import android.util.Log import com.google.gson.JsonObject import com.google.gson.JsonParser -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.async -import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.* import java.net.URL import java.net.URLEncoder import org.mosad.teapod.util.DataTypes.MediaType @@ -22,12 +20,12 @@ class TMDBApiController { private val imageUrl = "https://image.tmdb.org/t/p/w500" - fun search(title: String, type: MediaType): TMDBResponse { + suspend fun search(title: String, type: MediaType): TMDBResponse { val searchTerm = title.replace("(Sub)", "").trim() return when (type) { - MediaType.MOVIE -> searchMovie(searchTerm) - MediaType.TVSHOW -> searchTVShow(searchTerm) + MediaType.MOVIE -> searchMovie(searchTerm).await() + MediaType.TVSHOW -> searchTVShow(searchTerm).await() else -> { Log.e(javaClass.name, "Wrong Type: $type") TMDBResponse() @@ -36,17 +34,17 @@ class TMDBApiController { } - fun searchTVShow(title: String) = runBlocking { + fun searchTVShow(title: String): Deferred { val url = URL("$searchTVUrl$preparedParameters&query=${URLEncoder.encode(title, "UTF-8")}") - GlobalScope.async { + return GlobalScope.async { val response = JsonParser.parseString(url.readText()).asJsonObject //println(response) - return@async if (response.get("total_results").asInt > 0) { + if (response.get("total_results").asInt > 0) { response.get("results").asJsonArray.first().asJsonObject.let { - val id = getStringNotNull(it,"id").toInt() - val overview = getStringNotNull(it,"overview") + val id = getStringNotNull(it, "id").toInt() + val overview = getStringNotNull(it, "overview") val posterPath = getStringNotNullPrefix(it, "poster_path", imageUrl) val backdropPath = getStringNotNullPrefix(it, "backdrop_path", imageUrl) @@ -55,18 +53,17 @@ class TMDBApiController { } else { TMDBResponse() } - }.await() - + } } - fun searchMovie(title: String) = runBlocking { + fun searchMovie(title: String): Deferred { val url = URL("$searchMovieUrl$preparedParameters&query=${URLEncoder.encode(title, "UTF-8")}") - GlobalScope.async { + return GlobalScope.async { val response = JsonParser.parseString(url.readText()).asJsonObject //println(response) - return@async if (response.get("total_results").asInt > 0) { + if (response.get("total_results").asInt > 0) { response.get("results").asJsonArray.first().asJsonObject.let { val id = getStringNotNull(it,"id").toInt() val overview = getStringNotNull(it,"overview") @@ -79,9 +76,7 @@ class TMDBApiController { } else { TMDBResponse() } - - - }.await() + } } /**