add playheads to crunchyroll parser

* show watched icon, if episode has been fully watched
* add seasonTag to browse()
This commit is contained in:
Jannik 2022-01-05 01:28:39 +01:00
parent 2fa5a0aacd
commit 04b1ac5a53
Signed by: Seil0
GPG Key ID: E8459F3723C52C24
5 changed files with 78 additions and 28 deletions

View File

@ -14,6 +14,7 @@ import kotlinx.serialization.json.Json
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.put
import org.mosad.teapod.preferences.Preferences
import org.mosad.teapod.util.concatenate
import java.util.*
private val json = Json { ignoreUnknownKeys = true }
@ -179,9 +180,21 @@ object Crunchyroll {
*
* @return A **[BrowseResult]** object is returned.
*/
suspend fun browse(sortBy: SortBy = SortBy.ALPHABETICAL, start: Int = 0, n: Int = 10): BrowseResult {
suspend fun browse(
sortBy: SortBy = SortBy.ALPHABETICAL,
seasonTag: String = "",
start: Int = 0,
n: Int = 10
): BrowseResult {
val browseEndpoint = "/content/v1/browse"
val parameters = listOf("sort_by" to sortBy.str, "start" to start, "n" to n)
val noneOptParams = listOf("sort_by" to sortBy.str, "start" to start, "n" to n)
// if a season tag is present add it to the parameters
val parameters = if (seasonTag.isEmpty()) {
concatenate(noneOptParams, listOf("season_tag" to seasonTag))
} else {
noneOptParams
}
val result = request(browseEndpoint, parameters)
val browseResult = result.component1()?.obj()?.let {
@ -216,7 +229,7 @@ object Crunchyroll {
* Note: episode objects are currently not supported
*
* @param objects The object IDs as list of Strings
* @return A Collection of Panels
* @return A **[Collection]** of Panels
*/
suspend fun objects(objects: List<String>): Collection {
val episodesEndpoint = "/cms/v2/DE/M3/crunchyroll/objects/${objects.joinToString(",")}"
@ -228,7 +241,6 @@ object Crunchyroll {
)
val result = request(episodesEndpoint, parameters)
println(result.component1()?.obj()?.toString())
return result.component1()?.obj()?.let {
json.decodeFromString(it.toString())
@ -304,7 +316,7 @@ object Crunchyroll {
* Check if a media is in the user's watchlist.
*
* @param seriesId The crunchyroll series id of the media to check
* @return Boolean: ture if it was found, else false
* @return **[Boolean]**: ture if it was found, else false
*/
suspend fun isWatchlist(seriesId: String): Boolean {
val watchlistSeriesEndpoint = "/content/v1/watchlist/$accountID/$seriesId"
@ -345,16 +357,34 @@ object Crunchyroll {
}
/**
* TODO
* Get playhead information for all episodes in episodeIDs.
* The Information returned contains the playhead position, watched state
* and last modified date.
*
* @param episodeIDs A **[List]** of episodes IDs as strings.
* @return A **[Map]**<String, **[PlayheadObject]**> containing playback info.
*/
suspend fun playhead() {
// implement
suspend fun playheads(episodeIDs: List<String>): PlayheadsMap {
val playheadsEndpoint = "/content/v1/playheads/$accountID/${episodeIDs.joinToString(",")}"
val parameters = listOf("locale" to locale)
val result = request(playheadsEndpoint, parameters)
return result.component1()?.obj()?.let {
json.decodeFromString(it.toString())
} ?: emptyMap()
}
/**
* Listing functions: watchlist (list), up_next_account
*/
/**
* List items present in the watchlist.
*
* @param n Number of items to return, defaults to 20.
* @return A **[Watchlist]** containing up to n **[Item]**.
*/
suspend fun watchlist(n: Int = 20): Watchlist {
val watchlistEndpoint = "/content/v1/$accountID/watchlist"
val parameters = listOf("locale" to locale, "n" to n)
@ -369,20 +399,19 @@ object Crunchyroll {
}
/**
* TODO
* List the next up episodes for the logged in account.
*
* @param n Number of items to return, defaults to 20.
* @return A **[ContinueWatchingList]** containing up to n **[ContinueWatchingItem]**.
*/
suspend fun upNextAccount(n: Int = 20): ContinueWatchingList {
val watchlistEndpoint = "/content/v1/$accountID/up_next_account"
val parameters = listOf("locale" to locale, "n" to n)
val resultUpNextAccount = request(watchlistEndpoint, parameters)
val list: ContinueWatchingList = resultUpNextAccount.component1()?.obj()?.let {
return resultUpNextAccount.component1()?.obj()?.let {
json.decodeFromString(it.toString())
} ?: NoneContinueWatchingList
// val objects = list.items.map{ it.panel.episodeMetadata.seriesId }
// return objects(objects)
return list
}
}

View File

@ -29,7 +29,6 @@ data class Collection(
typealias SearchCollection = Collection
typealias BrowseResult = Collection
typealias Watchlist = Collection
typealias UpNextAccount = Collection
@Serializable
data class SearchResult(
@ -112,7 +111,6 @@ data class Series(
)
val NoneSeries = Series("", "", "", Images(emptyList(), emptyList()), emptyList())
/**
* Seasons data type
*/
@ -209,6 +207,16 @@ val NoneEpisode = Episode(
playback = ""
)
typealias PlayheadsMap = Map<String, PlayheadObject>
@Serializable
data class PlayheadObject(
@SerialName("playhead") val playhead: Int,
@SerialName("content_id") val contentId: String,
@SerialName("fully_watched") val fullyWatched: Boolean,
@SerialName("last_modified") val lastModified: String,
)
/**
* Playback/stream data type
*/

View File

@ -31,7 +31,11 @@ class MediaFragmentEpisodes : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
adapterRecEpisodes = EpisodeItemAdapter(model.currentEpisodesCrunchy, model.tmdbTVSeason.episodes)
adapterRecEpisodes = EpisodeItemAdapter(
model.currentEpisodesCrunchy,
model.tmdbTVSeason.episodes,
model.currentPlayheads
)
binding.recyclerEpisodes.adapter = adapterRecEpisodes
// set onItemClick, adapter is initialized

View File

@ -29,6 +29,7 @@ class MediaFragmentViewModel(application: Application) : AndroidViewModel(applic
var episodesCrunchy = NoneEpisodes
internal set
val currentEpisodesCrunchy = arrayListOf<Episode>() // used for EpisodeItemAdapter (easier updates)
var currentPlayheads: PlayheadsMap = emptyMap()
var isWatchlist = false
internal set
@ -66,6 +67,10 @@ class MediaFragmentViewModel(application: Application) : AndroidViewModel(applic
currentEpisodesCrunchy.addAll(episodesCrunchy.items)
println("episodes: $episodesCrunchy")
// get playheads (including fully watched state)
val episodeIDs = episodesCrunchy.items.map { it.id }
currentPlayheads = Crunchyroll.playheads(episodeIDs)
// set media type
mediaType = episodesCrunchy.items.firstOrNull()?.let {
if (it.episodeNumber != null) MediaType.TVSHOW else MediaType.MOVIE

View File

@ -2,8 +2,10 @@ package org.mosad.teapod.util.adapter
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.bumptech.glide.request.RequestOptions
@ -11,9 +13,14 @@ import jp.wasabeef.glide.transformations.RoundedCornersTransformation
import org.mosad.teapod.R
import org.mosad.teapod.databinding.ItemEpisodeBinding
import org.mosad.teapod.parser.crunchyroll.Episode
import org.mosad.teapod.parser.crunchyroll.PlayheadsMap
import org.mosad.teapod.util.tmdb.TMDBTVEpisode
class EpisodeItemAdapter(private val episodes: List<Episode>, private val tmdbEpisodes: List<TMDBTVEpisode>?) : RecyclerView.Adapter<EpisodeItemAdapter.EpisodeViewHolder>() {
class EpisodeItemAdapter(
private val episodes: List<Episode>,
private val tmdbEpisodes: List<TMDBTVEpisode>?,
private val playheads: PlayheadsMap
) : RecyclerView.Adapter<EpisodeItemAdapter.EpisodeViewHolder>() {
var onImageClick: ((seasonId: String, episodeId: String) -> Unit)? = null
@ -53,16 +60,13 @@ class EpisodeItemAdapter(private val episodes: List<Episode>, private val tmdbEp
.into(holder.binding.imageEpisode)
}
// TODO
// if (ep.watched) {
// holder.binding.imageWatched.setImageDrawable(
// ContextCompat.getDrawable(context, R.drawable.ic_baseline_check_circle_24)
// )
// } else {
// holder.binding.imageWatched.setImageDrawable(null)
// }
// disable watched icon until implemented
holder.binding.imageWatched.setImageDrawable(null)
// add watched icon to episode, if the episode id is present in playheads and fullyWatched
val watchedImage: Drawable? = if (playheads[ep.id]?.fullyWatched == true) {
ContextCompat.getDrawable(context, R.drawable.ic_baseline_check_circle_24)
} else {
null
}
holder.binding.imageWatched.setImageDrawable(watchedImage)
}
override fun getItemCount(): Int {