merge GUIMedia and StreamMedia into Media, parse episode title from aod
This commit is contained in:
parent
0fc1d8b5c2
commit
ae20e74702
|
@ -17,7 +17,7 @@ import org.mosad.teapod.ui.components.LoginDialog
|
||||||
import org.mosad.teapod.ui.home.HomeFragment
|
import org.mosad.teapod.ui.home.HomeFragment
|
||||||
import org.mosad.teapod.ui.library.LibraryFragment
|
import org.mosad.teapod.ui.library.LibraryFragment
|
||||||
import org.mosad.teapod.ui.search.SearchFragment
|
import org.mosad.teapod.ui.search.SearchFragment
|
||||||
import org.mosad.teapod.util.GUIMedia
|
import org.mosad.teapod.util.Media
|
||||||
|
|
||||||
class MainActivity : AppCompatActivity(), BottomNavigationView.OnNavigationItemSelectedListener {
|
class MainActivity : AppCompatActivity(), BottomNavigationView.OnNavigationItemSelectedListener {
|
||||||
|
|
||||||
|
@ -87,10 +87,10 @@ class MainActivity : AppCompatActivity(), BottomNavigationView.OnNavigationItemS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun showDetailFragment(media: GUIMedia) {
|
fun showDetailFragment(media: Media) {
|
||||||
val streamMedia = AoDParser().loadStreams(media.link) // load the streams for the selected media
|
media.episodes = AoDParser().loadStreams(media) // load the streams for the selected media
|
||||||
|
|
||||||
val mediaFragment = MediaFragment(media, streamMedia)
|
val mediaFragment = MediaFragment(media)
|
||||||
supportFragmentManager.commit {
|
supportFragmentManager.commit {
|
||||||
add(R.id.nav_host_fragment, mediaFragment, "MediaFragment")
|
add(R.id.nav_host_fragment, mediaFragment, "MediaFragment")
|
||||||
addToBackStack(null)
|
addToBackStack(null)
|
||||||
|
|
|
@ -7,8 +7,9 @@ import org.jsoup.Connection
|
||||||
import org.jsoup.Jsoup
|
import org.jsoup.Jsoup
|
||||||
import org.mosad.teapod.preferences.EncryptedPreferences
|
import org.mosad.teapod.preferences.EncryptedPreferences
|
||||||
import org.mosad.teapod.util.DataTypes.MediaType
|
import org.mosad.teapod.util.DataTypes.MediaType
|
||||||
import org.mosad.teapod.util.GUIMedia
|
import org.mosad.teapod.util.Episode
|
||||||
import org.mosad.teapod.util.StreamMedia
|
import org.mosad.teapod.util.Media
|
||||||
|
import java.util.*
|
||||||
import kotlin.collections.ArrayList
|
import kotlin.collections.ArrayList
|
||||||
|
|
||||||
class AoDParser {
|
class AoDParser {
|
||||||
|
@ -21,7 +22,7 @@ class AoDParser {
|
||||||
private var sessionCookies = mutableMapOf<String, String>()
|
private var sessionCookies = mutableMapOf<String, String>()
|
||||||
private var loginSuccess = false
|
private var loginSuccess = false
|
||||||
|
|
||||||
val mediaList = arrayListOf<GUIMedia>()
|
val mediaList = arrayListOf<Media>()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun login() = runBlocking {
|
private fun login() = runBlocking {
|
||||||
|
@ -67,7 +68,7 @@ class AoDParser {
|
||||||
/**
|
/**
|
||||||
* list all animes from the website
|
* list all animes from the website
|
||||||
*/
|
*/
|
||||||
fun listAnimes(): ArrayList<GUIMedia> = runBlocking {
|
fun listAnimes(): ArrayList<Media> = runBlocking {
|
||||||
if (sessionCookies.isEmpty()) login()
|
if (sessionCookies.isEmpty()) login()
|
||||||
|
|
||||||
withContext(Dispatchers.Default) {
|
withContext(Dispatchers.Default) {
|
||||||
|
@ -79,13 +80,19 @@ class AoDParser {
|
||||||
|
|
||||||
mediaList.clear()
|
mediaList.clear()
|
||||||
resAnimes.select("div.animebox").forEach {
|
resAnimes.select("div.animebox").forEach {
|
||||||
val media = GUIMedia(
|
val type = if (it.select("p.animebox-link").select("a").text().toLowerCase(Locale.ROOT) == "zur serie") {
|
||||||
it.select("h3.animebox-title").text(),
|
MediaType.TVSHOW
|
||||||
it.select("p.animebox-image").select("img").attr("src"),
|
} else {
|
||||||
it.select("p.animebox-shorttext").text(),
|
MediaType.MOVIE
|
||||||
it.select("p.animebox-link").select("a").attr("href")
|
}
|
||||||
)
|
|
||||||
|
|
||||||
|
val media = Media(
|
||||||
|
it.select("h3.animebox-title").text(),
|
||||||
|
it.select("p.animebox-link").select("a").attr("href"),
|
||||||
|
type,
|
||||||
|
it.select("p.animebox-image").select("img").attr("src"),
|
||||||
|
it.select("p.animebox-shorttext").text()
|
||||||
|
)
|
||||||
mediaList.add(media)
|
mediaList.add(media)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,17 +105,17 @@ class AoDParser {
|
||||||
/**
|
/**
|
||||||
* load streams for the media path
|
* load streams for the media path
|
||||||
*/
|
*/
|
||||||
fun loadStreams(mediaPath: String): StreamMedia = runBlocking {
|
fun loadStreams(media: Media): List<Episode> = runBlocking {
|
||||||
if (sessionCookies.isEmpty()) login()
|
if (sessionCookies.isEmpty()) login()
|
||||||
|
|
||||||
if (!loginSuccess) {
|
if (!loginSuccess) {
|
||||||
println("please log in") // TODO
|
println("please log in") // TODO
|
||||||
return@runBlocking StreamMedia(MediaType.OTHER)
|
return@runBlocking listOf()
|
||||||
}
|
}
|
||||||
|
|
||||||
withContext(Dispatchers.Default) {
|
withContext(Dispatchers.Default) {
|
||||||
|
|
||||||
val res = Jsoup.connect(baseURL + mediaPath)
|
val res = Jsoup.connect(baseURL + media.link)
|
||||||
.cookies(sessionCookies)
|
.cookies(sessionCookies)
|
||||||
.get()
|
.get()
|
||||||
|
|
||||||
|
@ -120,20 +127,14 @@ class AoDParser {
|
||||||
//println("first entry: ${playlists.first()}")
|
//println("first entry: ${playlists.first()}")
|
||||||
//println("csrf token is: $csrfToken")
|
//println("csrf token is: $csrfToken")
|
||||||
|
|
||||||
val type = if (res.select("h2").eachText().filter { it == "Episoden" }.any()) {
|
return@withContext loadStreamInfo(playlists.first(), csrfToken, media.type)
|
||||||
MediaType.TVSHOW
|
|
||||||
} else {
|
|
||||||
MediaType.MOVIE
|
|
||||||
}
|
|
||||||
|
|
||||||
return@withContext loadStreamInfo(playlists.first(), csrfToken, type)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* load the playlist path and parse it, read the stream info from json
|
* load the playlist path and parse it, read the stream info from json
|
||||||
*/
|
*/
|
||||||
private fun loadStreamInfo(playlistPath: String, csrfToken: String, type: MediaType): StreamMedia = runBlocking {
|
private fun loadStreamInfo(playlistPath: String, csrfToken: String, type: MediaType): List<Episode> = runBlocking {
|
||||||
withContext(Dispatchers.Default) {
|
withContext(Dispatchers.Default) {
|
||||||
val headers = mutableMapOf(
|
val headers = mutableMapOf(
|
||||||
Pair("Accept", "application/json, text/javascript, */*; q=0.01"),
|
Pair("Accept", "application/json, text/javascript, */*; q=0.01"),
|
||||||
|
@ -151,36 +152,36 @@ class AoDParser {
|
||||||
|
|
||||||
//println(res.body())
|
//println(res.body())
|
||||||
|
|
||||||
println(type)
|
|
||||||
return@withContext when (type) {
|
return@withContext when (type) {
|
||||||
MediaType.MOVIE -> {
|
MediaType.MOVIE -> {
|
||||||
val movie = JsonParser.parseString(res.body()).asJsonObject
|
val movie = JsonParser.parseString(res.body()).asJsonObject
|
||||||
.get("playlist").asJsonArray
|
.get("playlist").asJsonArray
|
||||||
|
|
||||||
val streamList = arrayListOf<String>()
|
movie.first().asJsonObject.get("sources").asJsonArray.toList().map {
|
||||||
movie.first().asJsonObject.get("sources").asJsonArray.toList().forEach {
|
Episode(streamUrl = it.asJsonObject.get("file").asString)
|
||||||
streamList.add(it.asJsonObject.get("file").asString)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
StreamMedia(MediaType.MOVIE, streamList)
|
|
||||||
}
|
}
|
||||||
MediaType.TVSHOW -> {
|
MediaType.TVSHOW -> {
|
||||||
val episodes = JsonParser.parseString(res.body()).asJsonObject
|
val episodesJson = JsonParser.parseString(res.body()).asJsonObject
|
||||||
.get("playlist").asJsonArray
|
.get("playlist").asJsonArray
|
||||||
|
|
||||||
val streamList = arrayListOf<String>()
|
|
||||||
episodes.forEach {
|
episodesJson.map {
|
||||||
val streamUrl = it.asJsonObject.get("sources").asJsonArray
|
val episodeStream = it.asJsonObject.get("sources").asJsonArray
|
||||||
.first().asJsonObject
|
.first().asJsonObject
|
||||||
.get("file").asString
|
.get("file").asString
|
||||||
streamList.add(streamUrl)
|
val episodeTitle = it.asJsonObject.get("title").asString
|
||||||
|
|
||||||
|
Episode(
|
||||||
|
episodeTitle,
|
||||||
|
episodeStream
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
StreamMedia(MediaType.TVSHOW, streamList)
|
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
Log.e(javaClass.name, "Wrong Type, please report this issue.")
|
Log.e(javaClass.name, "Wrong Type, please report this issue.")
|
||||||
StreamMedia(MediaType.OTHER)
|
listOf()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,10 +14,9 @@ import org.mosad.teapod.MainActivity
|
||||||
import org.mosad.teapod.R
|
import org.mosad.teapod.R
|
||||||
import org.mosad.teapod.util.DataTypes.MediaType
|
import org.mosad.teapod.util.DataTypes.MediaType
|
||||||
import org.mosad.teapod.util.EpisodesAdapter
|
import org.mosad.teapod.util.EpisodesAdapter
|
||||||
import org.mosad.teapod.util.GUIMedia
|
import org.mosad.teapod.util.Media
|
||||||
import org.mosad.teapod.util.StreamMedia
|
|
||||||
|
|
||||||
class MediaFragment(private val guiMedia: GUIMedia, private val streamMedia: StreamMedia) : Fragment() {
|
class MediaFragment(private val media: Media) : Fragment() {
|
||||||
|
|
||||||
private lateinit var adapterRecEpisodes: EpisodesAdapter
|
private lateinit var adapterRecEpisodes: EpisodesAdapter
|
||||||
private lateinit var viewManager: RecyclerView.LayoutManager
|
private lateinit var viewManager: RecyclerView.LayoutManager
|
||||||
|
@ -31,53 +30,47 @@ class MediaFragment(private val guiMedia: GUIMedia, private val streamMedia: Str
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
// generic gui
|
// generic gui
|
||||||
Glide.with(requireContext()).load(guiMedia.posterLink).into(image_poster)
|
Glide.with(requireContext()).load(media.posterLink).into(image_poster)
|
||||||
text_title.text = guiMedia.title
|
text_title.text = media.title
|
||||||
text_desc.text = guiMedia.shortDesc
|
text_desc.text = media.shortDesc
|
||||||
|
|
||||||
// specific gui
|
// specific gui
|
||||||
if (streamMedia.type == MediaType.TVSHOW) {
|
if (media.type == MediaType.TVSHOW) {
|
||||||
val episodes = streamMedia.streams.mapIndexed { index, _ ->
|
// TODO
|
||||||
"${guiMedia.title} - Ep. ${index + 1}"
|
val episodeTitles = media.episodes.map { it.title }
|
||||||
}
|
|
||||||
|
|
||||||
|
adapterRecEpisodes = EpisodesAdapter(episodeTitles)
|
||||||
adapterRecEpisodes = EpisodesAdapter(episodes)
|
|
||||||
viewManager = LinearLayoutManager(context)
|
viewManager = LinearLayoutManager(context)
|
||||||
recycler_episodes.layoutManager = viewManager
|
recycler_episodes.layoutManager = viewManager
|
||||||
recycler_episodes.adapter = adapterRecEpisodes
|
recycler_episodes.adapter = adapterRecEpisodes
|
||||||
|
|
||||||
} else if (streamMedia.type == MediaType.MOVIE) {
|
} else if (media.type == MediaType.MOVIE) {
|
||||||
recycler_episodes.visibility = View.GONE
|
recycler_episodes.visibility = View.GONE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
println("media streams: ${streamMedia.streams}")
|
println("media streams: ${media.episodes}")
|
||||||
|
|
||||||
initActions()
|
initActions()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initActions() {
|
private fun initActions() {
|
||||||
button_play.setOnClickListener {
|
button_play.setOnClickListener {
|
||||||
onClickButtonPlay()
|
when (media.type) {
|
||||||
|
MediaType.MOVIE -> playStream(media.episodes.first().streamUrl)
|
||||||
|
MediaType.TVSHOW -> playStream(media.episodes.first().streamUrl)
|
||||||
|
MediaType.OTHER -> Log.e(javaClass.name, "Wrong Type, please report this issue.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// set onItemClick only in adapter is initialized
|
// set onItemClick only in adapter is initialized
|
||||||
if (this::adapterRecEpisodes.isInitialized) {
|
if (this::adapterRecEpisodes.isInitialized) {
|
||||||
adapterRecEpisodes.onItemClick = { item, position ->
|
adapterRecEpisodes.onItemClick = { item, position ->
|
||||||
playStream(streamMedia.streams[position])
|
playStream(media.episodes[position].streamUrl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onClickButtonPlay() {
|
|
||||||
when (streamMedia.type) {
|
|
||||||
MediaType.MOVIE -> playStream(streamMedia.streams.first())
|
|
||||||
MediaType.TVSHOW -> playStream(streamMedia.streams.first())
|
|
||||||
MediaType.OTHER -> Log.e(javaClass.name, "Wrong Type, please report this issue.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun playStream(url: String) {
|
private fun playStream(url: String) {
|
||||||
val mainActivity = activity as MainActivity
|
val mainActivity = activity as MainActivity
|
||||||
mainActivity.startPlayer(url)
|
mainActivity.startPlayer(url)
|
||||||
|
|
|
@ -11,7 +11,7 @@ import org.mosad.teapod.MainActivity
|
||||||
import org.mosad.teapod.R
|
import org.mosad.teapod.R
|
||||||
import org.mosad.teapod.parser.AoDParser
|
import org.mosad.teapod.parser.AoDParser
|
||||||
import org.mosad.teapod.util.CustomAdapter
|
import org.mosad.teapod.util.CustomAdapter
|
||||||
import org.mosad.teapod.util.GUIMedia
|
import org.mosad.teapod.util.Media
|
||||||
|
|
||||||
class LibraryFragment : Fragment() {
|
class LibraryFragment : Fragment() {
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ class LibraryFragment : Fragment() {
|
||||||
|
|
||||||
private fun initActions() {
|
private fun initActions() {
|
||||||
list_library.setOnItemClickListener { _, _, position, _ ->
|
list_library.setOnItemClickListener { _, _, position, _ ->
|
||||||
val media = adapter.getItem(position) as GUIMedia
|
val media = adapter.getItem(position) as Media
|
||||||
println("selected item is: ${media.title}")
|
println("selected item is: ${media.title}")
|
||||||
|
|
||||||
val mainActivity = activity as MainActivity
|
val mainActivity = activity as MainActivity
|
||||||
|
|
|
@ -12,7 +12,7 @@ import org.mosad.teapod.MainActivity
|
||||||
import org.mosad.teapod.R
|
import org.mosad.teapod.R
|
||||||
import org.mosad.teapod.parser.AoDParser
|
import org.mosad.teapod.parser.AoDParser
|
||||||
import org.mosad.teapod.util.CustomAdapter
|
import org.mosad.teapod.util.CustomAdapter
|
||||||
import org.mosad.teapod.util.GUIMedia
|
import org.mosad.teapod.util.Media
|
||||||
|
|
||||||
class SearchFragment : Fragment() {
|
class SearchFragment : Fragment() {
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ class SearchFragment : Fragment() {
|
||||||
})
|
})
|
||||||
|
|
||||||
list_search.setOnItemClickListener { _, _, position, _ ->
|
list_search.setOnItemClickListener { _, _, position, _ ->
|
||||||
val media = adapter.getItem(position) as GUIMedia
|
val media = adapter.getItem(position) as Media
|
||||||
|
|
||||||
println("selected item is: ${media.title}")
|
println("selected item is: ${media.title}")
|
||||||
|
|
||||||
|
|
|
@ -1,21 +1,15 @@
|
||||||
package org.mosad.teapod.util
|
package org.mosad.teapod.util
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.Bitmap
|
|
||||||
import android.graphics.drawable.Drawable
|
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.*
|
import android.widget.*
|
||||||
import androidx.core.content.res.ResourcesCompat
|
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import com.bumptech.glide.request.target.CustomTarget
|
|
||||||
import com.bumptech.glide.request.transition.Transition
|
|
||||||
import org.mosad.teapod.R
|
import org.mosad.teapod.R
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.collections.ArrayList
|
|
||||||
|
|
||||||
class CustomAdapter(val context: Context, private val originalMedia: ArrayList<GUIMedia>) : BaseAdapter(), Filterable {
|
class CustomAdapter(val context: Context, private val originalMedia: ArrayList<Media>) : BaseAdapter(), Filterable {
|
||||||
|
|
||||||
private var filteredMedia = originalMedia.map { it.copy() }
|
private var filteredMedia = originalMedia.map { it.copy() }
|
||||||
private val customFilter = CustomFilter()
|
private val customFilter = CustomFilter()
|
||||||
|
@ -68,7 +62,7 @@ class CustomAdapter(val context: Context, private val originalMedia: ArrayList<G
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun publishResults(constraint: CharSequence?, results: FilterResults?) {
|
override fun publishResults(constraint: CharSequence?, results: FilterResults?) {
|
||||||
filteredMedia = results?.values as ArrayList<GUIMedia>
|
filteredMedia = results?.values as ArrayList<Media>
|
||||||
notifyDataSetChanged()
|
notifyDataSetChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,11 +8,10 @@ class DataTypes {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO rework: add type, episodes list with episode title, if type == MOVIE the first episode will be the movie stream
|
data class Media(val title: String, val link: String, val type: DataTypes.MediaType, val posterLink: String, val shortDesc : String, var episodes: List<Episode> = listOf()) {
|
||||||
data class GUIMedia(val title: String, val posterLink: String, val shortDesc : String, val link: String) {
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return title
|
return title
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class StreamMedia(val type: DataTypes.MediaType, val streams: ArrayList<String> = arrayListOf())
|
data class Episode(val title: String = "", val streamUrl: String = "", var watched: Boolean = false)
|
||||||
|
|
Loading…
Reference in New Issue