add playheads to crunchyroll parser
* show watched icon, if episode has been fully watched * add seasonTag to browse()
This commit is contained in:
parent
2fa5a0aacd
commit
04b1ac5a53
|
@ -14,6 +14,7 @@ import kotlinx.serialization.json.Json
|
||||||
import kotlinx.serialization.json.buildJsonObject
|
import kotlinx.serialization.json.buildJsonObject
|
||||||
import kotlinx.serialization.json.put
|
import kotlinx.serialization.json.put
|
||||||
import org.mosad.teapod.preferences.Preferences
|
import org.mosad.teapod.preferences.Preferences
|
||||||
|
import org.mosad.teapod.util.concatenate
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
private val json = Json { ignoreUnknownKeys = true }
|
private val json = Json { ignoreUnknownKeys = true }
|
||||||
|
@ -179,9 +180,21 @@ object Crunchyroll {
|
||||||
*
|
*
|
||||||
* @return A **[BrowseResult]** object is returned.
|
* @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 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 result = request(browseEndpoint, parameters)
|
||||||
val browseResult = result.component1()?.obj()?.let {
|
val browseResult = result.component1()?.obj()?.let {
|
||||||
|
@ -216,7 +229,7 @@ object Crunchyroll {
|
||||||
* Note: episode objects are currently not supported
|
* Note: episode objects are currently not supported
|
||||||
*
|
*
|
||||||
* @param objects The object IDs as list of Strings
|
* @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 {
|
suspend fun objects(objects: List<String>): Collection {
|
||||||
val episodesEndpoint = "/cms/v2/DE/M3/crunchyroll/objects/${objects.joinToString(",")}"
|
val episodesEndpoint = "/cms/v2/DE/M3/crunchyroll/objects/${objects.joinToString(",")}"
|
||||||
|
@ -228,7 +241,6 @@ object Crunchyroll {
|
||||||
)
|
)
|
||||||
|
|
||||||
val result = request(episodesEndpoint, parameters)
|
val result = request(episodesEndpoint, parameters)
|
||||||
println(result.component1()?.obj()?.toString())
|
|
||||||
|
|
||||||
return result.component1()?.obj()?.let {
|
return result.component1()?.obj()?.let {
|
||||||
json.decodeFromString(it.toString())
|
json.decodeFromString(it.toString())
|
||||||
|
@ -304,7 +316,7 @@ object Crunchyroll {
|
||||||
* Check if a media is in the user's watchlist.
|
* Check if a media is in the user's watchlist.
|
||||||
*
|
*
|
||||||
* @param seriesId The crunchyroll series id of the media to check
|
* @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 {
|
suspend fun isWatchlist(seriesId: String): Boolean {
|
||||||
val watchlistSeriesEndpoint = "/content/v1/watchlist/$accountID/$seriesId"
|
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() {
|
suspend fun playheads(episodeIDs: List<String>): PlayheadsMap {
|
||||||
// implement
|
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
|
* 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 {
|
suspend fun watchlist(n: Int = 20): Watchlist {
|
||||||
val watchlistEndpoint = "/content/v1/$accountID/watchlist"
|
val watchlistEndpoint = "/content/v1/$accountID/watchlist"
|
||||||
val parameters = listOf("locale" to locale, "n" to n)
|
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 {
|
suspend fun upNextAccount(n: Int = 20): ContinueWatchingList {
|
||||||
val watchlistEndpoint = "/content/v1/$accountID/up_next_account"
|
val watchlistEndpoint = "/content/v1/$accountID/up_next_account"
|
||||||
val parameters = listOf("locale" to locale, "n" to n)
|
val parameters = listOf("locale" to locale, "n" to n)
|
||||||
|
|
||||||
val resultUpNextAccount = request(watchlistEndpoint, parameters)
|
val resultUpNextAccount = request(watchlistEndpoint, parameters)
|
||||||
val list: ContinueWatchingList = resultUpNextAccount.component1()?.obj()?.let {
|
return resultUpNextAccount.component1()?.obj()?.let {
|
||||||
json.decodeFromString(it.toString())
|
json.decodeFromString(it.toString())
|
||||||
} ?: NoneContinueWatchingList
|
} ?: NoneContinueWatchingList
|
||||||
|
|
||||||
// val objects = list.items.map{ it.panel.episodeMetadata.seriesId }
|
|
||||||
// return objects(objects)
|
|
||||||
return list
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,6 @@ data class Collection(
|
||||||
typealias SearchCollection = Collection
|
typealias SearchCollection = Collection
|
||||||
typealias BrowseResult = Collection
|
typealias BrowseResult = Collection
|
||||||
typealias Watchlist = Collection
|
typealias Watchlist = Collection
|
||||||
typealias UpNextAccount = Collection
|
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class SearchResult(
|
data class SearchResult(
|
||||||
|
@ -112,7 +111,6 @@ data class Series(
|
||||||
)
|
)
|
||||||
val NoneSeries = Series("", "", "", Images(emptyList(), emptyList()), emptyList())
|
val NoneSeries = Series("", "", "", Images(emptyList(), emptyList()), emptyList())
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Seasons data type
|
* Seasons data type
|
||||||
*/
|
*/
|
||||||
|
@ -209,6 +207,16 @@ val NoneEpisode = Episode(
|
||||||
playback = ""
|
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
|
* Playback/stream data type
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -31,7 +31,11 @@ class MediaFragmentEpisodes : Fragment() {
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
adapterRecEpisodes = EpisodeItemAdapter(model.currentEpisodesCrunchy, model.tmdbTVSeason.episodes)
|
adapterRecEpisodes = EpisodeItemAdapter(
|
||||||
|
model.currentEpisodesCrunchy,
|
||||||
|
model.tmdbTVSeason.episodes,
|
||||||
|
model.currentPlayheads
|
||||||
|
)
|
||||||
binding.recyclerEpisodes.adapter = adapterRecEpisodes
|
binding.recyclerEpisodes.adapter = adapterRecEpisodes
|
||||||
|
|
||||||
// set onItemClick, adapter is initialized
|
// set onItemClick, adapter is initialized
|
||||||
|
|
|
@ -29,6 +29,7 @@ class MediaFragmentViewModel(application: Application) : AndroidViewModel(applic
|
||||||
var episodesCrunchy = NoneEpisodes
|
var episodesCrunchy = NoneEpisodes
|
||||||
internal set
|
internal set
|
||||||
val currentEpisodesCrunchy = arrayListOf<Episode>() // used for EpisodeItemAdapter (easier updates)
|
val currentEpisodesCrunchy = arrayListOf<Episode>() // used for EpisodeItemAdapter (easier updates)
|
||||||
|
var currentPlayheads: PlayheadsMap = emptyMap()
|
||||||
var isWatchlist = false
|
var isWatchlist = false
|
||||||
internal set
|
internal set
|
||||||
|
|
||||||
|
@ -66,6 +67,10 @@ class MediaFragmentViewModel(application: Application) : AndroidViewModel(applic
|
||||||
currentEpisodesCrunchy.addAll(episodesCrunchy.items)
|
currentEpisodesCrunchy.addAll(episodesCrunchy.items)
|
||||||
println("episodes: $episodesCrunchy")
|
println("episodes: $episodesCrunchy")
|
||||||
|
|
||||||
|
// get playheads (including fully watched state)
|
||||||
|
val episodeIDs = episodesCrunchy.items.map { it.id }
|
||||||
|
currentPlayheads = Crunchyroll.playheads(episodeIDs)
|
||||||
|
|
||||||
// set media type
|
// set media type
|
||||||
mediaType = episodesCrunchy.items.firstOrNull()?.let {
|
mediaType = episodesCrunchy.items.firstOrNull()?.let {
|
||||||
if (it.episodeNumber != null) MediaType.TVSHOW else MediaType.MOVIE
|
if (it.episodeNumber != null) MediaType.TVSHOW else MediaType.MOVIE
|
||||||
|
|
|
@ -2,8 +2,10 @@ package org.mosad.teapod.util.adapter
|
||||||
|
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.graphics.drawable.ColorDrawable
|
import android.graphics.drawable.ColorDrawable
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import com.bumptech.glide.request.RequestOptions
|
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.R
|
||||||
import org.mosad.teapod.databinding.ItemEpisodeBinding
|
import org.mosad.teapod.databinding.ItemEpisodeBinding
|
||||||
import org.mosad.teapod.parser.crunchyroll.Episode
|
import org.mosad.teapod.parser.crunchyroll.Episode
|
||||||
|
import org.mosad.teapod.parser.crunchyroll.PlayheadsMap
|
||||||
import org.mosad.teapod.util.tmdb.TMDBTVEpisode
|
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
|
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)
|
.into(holder.binding.imageEpisode)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
// add watched icon to episode, if the episode id is present in playheads and fullyWatched
|
||||||
// if (ep.watched) {
|
val watchedImage: Drawable? = if (playheads[ep.id]?.fullyWatched == true) {
|
||||||
// holder.binding.imageWatched.setImageDrawable(
|
ContextCompat.getDrawable(context, R.drawable.ic_baseline_check_circle_24)
|
||||||
// ContextCompat.getDrawable(context, R.drawable.ic_baseline_check_circle_24)
|
} else {
|
||||||
// )
|
null
|
||||||
// } else {
|
}
|
||||||
// holder.binding.imageWatched.setImageDrawable(null)
|
holder.binding.imageWatched.setImageDrawable(watchedImage)
|
||||||
// }
|
|
||||||
// disable watched icon until implemented
|
|
||||||
holder.binding.imageWatched.setImageDrawable(null)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getItemCount(): Int {
|
override fun getItemCount(): Int {
|
||||||
|
|
Loading…
Reference in New Issue