added episode description and is watched status to MediaFragment
This commit is contained in:
parent
6fb8f56faf
commit
5b7d2cd26e
|
@ -131,14 +131,24 @@ class AoDParser {
|
||||||
"episodenanzahl" -> media.info.episodesCount = it.select("td").text().toInt()
|
"episodenanzahl" -> media.info.episodesCount = it.select("td").text().toInt()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO tv show specific for each episode (div.episodebox)
|
* TODO tv show specific for each episode (div.episodebox)
|
||||||
* * episode id (div.flip-front -> id.substringafter("-"))
|
|
||||||
* * watched (if div.status-icon-orange present = true)
|
|
||||||
* * watchedCallback
|
* * watchedCallback
|
||||||
* * episodebox-shorttext
|
|
||||||
*/
|
*/
|
||||||
|
val episodes = if (media.type == MediaType.TVSHOW) {
|
||||||
|
res.select("div.three-box-container > div.episodebox").map { episodebox ->
|
||||||
|
val episodeId = episodebox.select("div.flip-front").attr("id").substringAfter("-").toInt()
|
||||||
|
val episodeWatched = episodebox.select("div.episodebox-icons > div").hasClass("status-icon-orange")
|
||||||
|
val episodeShortDesc = episodebox.select("p.episodebox-shorttext").text()
|
||||||
|
|
||||||
|
Episode(id = episodeId, watched = episodeWatched, shortDesc = episodeShortDesc)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
listOf(Episode())
|
||||||
|
}
|
||||||
|
|
||||||
|
// has attr data-lag (ger or jap)
|
||||||
val playlists = res.select("input.streamstarter_html5").eachAttr("data-playlist")
|
val playlists = res.select("input.streamstarter_html5").eachAttr("data-playlist")
|
||||||
val csrfToken = res.select("meta[name=csrf-token]").attr("content")
|
val csrfToken = res.select("meta[name=csrf-token]").attr("content")
|
||||||
|
|
||||||
|
@ -146,7 +156,7 @@ class AoDParser {
|
||||||
//println("csrf token is: $csrfToken")
|
//println("csrf token is: $csrfToken")
|
||||||
|
|
||||||
return@withContext if (playlists.size > 0) {
|
return@withContext if (playlists.size > 0) {
|
||||||
loadStreamInfo(playlists.first(), csrfToken, media.type)
|
loadStreamInfo(playlists.first(), csrfToken, media.type, episodes)
|
||||||
} else {
|
} else {
|
||||||
listOf()
|
listOf()
|
||||||
}
|
}
|
||||||
|
@ -155,8 +165,9 @@ class AoDParser {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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
|
||||||
|
* @param episodes is used as call ba reference, additionally it is passed a return value
|
||||||
*/
|
*/
|
||||||
private fun loadStreamInfo(playlistPath: String, csrfToken: String, type: MediaType): List<Episode> = runBlocking {
|
private fun loadStreamInfo(playlistPath: String, csrfToken: String, type: MediaType, episodes: List<Episode>): 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"),
|
||||||
|
@ -174,44 +185,48 @@ class AoDParser {
|
||||||
|
|
||||||
//println(res.body())
|
//println(res.body())
|
||||||
|
|
||||||
return@withContext when (type) {
|
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
|
||||||
|
|
||||||
movie.first().asJsonObject.get("sources").asJsonArray.toList().map {
|
movie.first().asJsonObject.get("sources").asJsonArray.toList().forEach {
|
||||||
Episode(streamUrl = it.asJsonObject.get("file").asString)
|
episodes.first().streamUrl = it.asJsonObject.get("file").asString
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MediaType.TVSHOW -> {
|
MediaType.TVSHOW -> {
|
||||||
val episodesJson = JsonParser.parseString(res.body()).asJsonObject
|
val episodesJson = JsonParser.parseString(res.body()).asJsonObject
|
||||||
.get("playlist").asJsonArray
|
.get("playlist").asJsonArray
|
||||||
|
|
||||||
|
|
||||||
episodesJson.map {
|
episodesJson.forEach { jsonElement ->
|
||||||
val episodeStream = it.asJsonObject.get("sources").asJsonArray
|
val episodeId = jsonElement.asJsonObject.get("mediaid")
|
||||||
|
val episodeStream = jsonElement.asJsonObject.get("sources").asJsonArray
|
||||||
.first().asJsonObject
|
.first().asJsonObject
|
||||||
.get("file").asString
|
.get("file").asString
|
||||||
val episodeTitle = it.asJsonObject.get("title").asString
|
val episodeTitle = jsonElement.asJsonObject.get("title").asString
|
||||||
val episodePoster = it.asJsonObject.get("image").asString
|
val episodePoster = jsonElement.asJsonObject.get("image").asString
|
||||||
val episodeDescription = it.asJsonObject.get("description").asString
|
val episodeDescription = jsonElement.asJsonObject.get("description").asString
|
||||||
val episodeNumber = episodeTitle.substringAfter(", Ep. ").toInt()
|
val episodeNumber = episodeTitle.substringAfter(", Ep. ").toInt()
|
||||||
|
|
||||||
Episode(
|
episodes.first { it.id == episodeId.asInt }.apply {
|
||||||
episodeTitle,
|
this.title = episodeTitle
|
||||||
episodeStream,
|
this.posterLink = episodePoster
|
||||||
episodePoster,
|
this.streamUrl = episodeStream
|
||||||
episodeDescription,
|
this.description = episodeDescription
|
||||||
episodeNumber
|
this.number = episodeNumber
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
Log.e(javaClass.name, "Wrong Type, please report this issue.")
|
Log.e(javaClass.name, "Wrong Type, please report this issue.")
|
||||||
listOf()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return@withContext episodes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ import org.mosad.teapod.util.Media
|
||||||
|
|
||||||
class SearchFragment : Fragment() {
|
class SearchFragment : Fragment() {
|
||||||
|
|
||||||
private lateinit var adapter : CustomAdapter
|
private var adapter : CustomAdapter? = null
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
return inflater.inflate(R.layout.fragment_search, container, false)
|
return inflater.inflate(R.layout.fragment_search, container, false)
|
||||||
|
@ -45,14 +45,14 @@ class SearchFragment : Fragment() {
|
||||||
private fun initActions() {
|
private fun initActions() {
|
||||||
search_text.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
|
search_text.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
|
||||||
override fun onQueryTextSubmit(query: String?): Boolean {
|
override fun onQueryTextSubmit(query: String?): Boolean {
|
||||||
adapter.filter.filter(query)
|
adapter?.filter?.filter(query)
|
||||||
adapter.notifyDataSetChanged()
|
adapter?.notifyDataSetChanged()
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onQueryTextChange(newText: String?): Boolean {
|
override fun onQueryTextChange(newText: String?): Boolean {
|
||||||
adapter.filter.filter(newText)
|
adapter?.filter?.filter(newText)
|
||||||
adapter.notifyDataSetChanged()
|
adapter?.notifyDataSetChanged()
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -61,7 +61,7 @@ class SearchFragment : Fragment() {
|
||||||
search_text.clearFocus() // remove focus from the SearchView
|
search_text.clearFocus() // remove focus from the SearchView
|
||||||
|
|
||||||
runBlocking {
|
runBlocking {
|
||||||
val media = adapter.getItem(position) as Media
|
val media = adapter?.getItem(position) as Media
|
||||||
println("selected item is: ${media.title}")
|
println("selected item is: ${media.title}")
|
||||||
|
|
||||||
(activity as MainActivity).showDetailFragment(media).join()
|
(activity as MainActivity).showDetailFragment(media).join()
|
||||||
|
|
|
@ -24,10 +24,12 @@ data class Info(
|
||||||
)
|
)
|
||||||
|
|
||||||
data class Episode(
|
data class Episode(
|
||||||
val title: String = "",
|
val id: Int = 0,
|
||||||
val streamUrl: String = "",
|
var title: String = "",
|
||||||
val posterLink: String = "",
|
var streamUrl: String = "",
|
||||||
|
var posterLink: String = "",
|
||||||
var description: String = "",
|
var description: String = "",
|
||||||
|
var shortDesc: String = "",
|
||||||
var number: Int = 0,
|
var number: Int = 0,
|
||||||
var watched: Boolean = false
|
var watched: Boolean = false
|
||||||
)
|
)
|
||||||
|
|
|
@ -9,7 +9,7 @@ import com.bumptech.glide.Glide
|
||||||
import kotlinx.android.synthetic.main.component_episode.view.*
|
import kotlinx.android.synthetic.main.component_episode.view.*
|
||||||
import org.mosad.teapod.R
|
import org.mosad.teapod.R
|
||||||
|
|
||||||
class EpisodesAdapter(private val data: List<Episode>, private val context: Context) : RecyclerView.Adapter<EpisodesAdapter.MyViewHolder>() {
|
class EpisodesAdapter(private val episodes: List<Episode>, private val context: Context) : RecyclerView.Adapter<EpisodesAdapter.MyViewHolder>() {
|
||||||
|
|
||||||
var onItemClick: ((String, Int) -> Unit)? = null
|
var onItemClick: ((String, Int) -> Unit)? = null
|
||||||
|
|
||||||
|
@ -20,21 +20,30 @@ class EpisodesAdapter(private val data: List<Episode>, private val context: Cont
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
|
||||||
holder.view.text_episode_title.text = "Episode ${data[position].number} ${data[position].description}"
|
holder.view.text_episode_title.text = context.getString(
|
||||||
|
R.string.component_episode_title,
|
||||||
|
episodes[position].number,
|
||||||
|
episodes[position].description
|
||||||
|
)
|
||||||
|
holder.view.text_episode_desc.text = episodes[position].shortDesc
|
||||||
|
|
||||||
if (data[position].posterLink.isNotEmpty()) {
|
if (episodes[position].posterLink.isNotEmpty()) {
|
||||||
Glide.with(context).load(data[position].posterLink).into(holder.view.image_episode)
|
Glide.with(context).load(episodes[position].posterLink).into(holder.view.image_episode)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!episodes[position].watched) {
|
||||||
|
holder.view.image_watched.setImageDrawable(null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getItemCount(): Int {
|
override fun getItemCount(): Int {
|
||||||
return data.size
|
return episodes.size
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class MyViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
|
inner class MyViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
|
||||||
init {
|
init {
|
||||||
view.setOnClickListener {
|
view.setOnClickListener {
|
||||||
onItemClick?.invoke(data[adapterPosition].title, adapterPosition)
|
onItemClick?.invoke(episodes[adapterPosition].title, adapterPosition)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:tint="?attr/colorControlNormal">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM10,17l-5,-5 1.41,-1.41L10,14.17l7.59,-7.59L19,8l-9,9z"/>
|
||||||
|
</vector>
|
|
@ -18,20 +18,31 @@
|
||||||
android:id="@+id/image_episode"
|
android:id="@+id/image_episode"
|
||||||
android:layout_width="128dp"
|
android:layout_width="128dp"
|
||||||
android:layout_height="72dp"
|
android:layout_height="72dp"
|
||||||
|
android:contentDescription="@string/component_poster_desc"
|
||||||
app:srcCompat="@drawable/ic_baseline_account_box_24" />
|
app:srcCompat="@drawable/ic_baseline_account_box_24" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/text_episode_title"
|
android:id="@+id/text_episode_title"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="0dp"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_marginStart="7dp"
|
android:layout_marginStart="7dp"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:text="TextView"
|
android:text="@string/component_episode_title"
|
||||||
android:textSize="16sp" />
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/image_watched"
|
||||||
|
android:layout_width="30dp"
|
||||||
|
android:layout_height="30dp"
|
||||||
|
android:layout_margin="2dp"
|
||||||
|
android:contentDescription="@string/component_watched_desc"
|
||||||
|
app:srcCompat="@drawable/ic_baseline_check_circle_24" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/text_episode_desc"
|
android:id="@+id/text_episode_desc"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content" />
|
android:layout_height="wrap_content"
|
||||||
|
android:maxLines="2"
|
||||||
|
android:ellipsize="end"/>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
|
@ -12,6 +12,7 @@
|
||||||
<string name="button_play">Abspielen</string>
|
<string name="button_play">Abspielen</string>
|
||||||
<string name="text_episodes_count">%1$d Episoden</string>
|
<string name="text_episodes_count">%1$d Episoden</string>
|
||||||
<string name="text_runtime">%1$d Minuten</string>
|
<string name="text_runtime">%1$d Minuten</string>
|
||||||
|
<string name="component_episode_title">Episode %1$d %2$s</string>
|
||||||
|
|
||||||
<!-- settings fragment -->
|
<!-- settings fragment -->
|
||||||
<string name="account">Account</string>
|
<string name="account">Account</string>
|
||||||
|
|
|
@ -16,6 +16,9 @@
|
||||||
<string name="text_age_ex" translatable="false">6</string>
|
<string name="text_age_ex" translatable="false">6</string>
|
||||||
<string name="text_episodes_count">%1$d episodes</string>
|
<string name="text_episodes_count">%1$d episodes</string>
|
||||||
<string name="text_runtime">%1$d Minutes</string>
|
<string name="text_runtime">%1$d Minutes</string>
|
||||||
|
<string name="component_episode_title">Episode %1$d %2$s</string>
|
||||||
|
<string name="component_poster_desc" translatable="false">episode poster</string>
|
||||||
|
<string name="component_watched_desc" translatable="false">already watched</string>
|
||||||
|
|
||||||
<!-- settings fragment -->
|
<!-- settings fragment -->
|
||||||
<string name="account">Account</string>
|
<string name="account">Account</string>
|
||||||
|
|
Loading…
Reference in New Issue