use suspending functions for coroutines when possible

* fix crash, when media is selected, but MediaFragment is removed before AoDParser could load data
This commit is contained in:
Jannik 2020-11-27 11:06:16 +01:00
parent bb8c8ca85a
commit d01e87bf14
Signed by: Seil0
GPG Key ID: E8459F3723C52C24
4 changed files with 102 additions and 109 deletions

View File

@ -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"),

View File

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

View File

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

View File

@ -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<TMDBResponse> {
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<TMDBResponse> {
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()
}
}
/**