add newly added title to HomeFragment
* add support for season_list to crunchyroll parser
This commit is contained in:
parent
04b1ac5a53
commit
22d2d777c8
|
@ -74,7 +74,10 @@ object Crunchyroll {
|
||||||
return@runBlocking false
|
return@runBlocking false
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO get/post difference
|
/**
|
||||||
|
* Requests: get, post, delete
|
||||||
|
*/
|
||||||
|
|
||||||
private suspend fun request(
|
private suspend fun request(
|
||||||
endpoint: String,
|
endpoint: String,
|
||||||
params: Parameters = listOf(),
|
params: Parameters = listOf(),
|
||||||
|
@ -83,7 +86,6 @@ object Crunchyroll {
|
||||||
val path = if (url.isEmpty()) "$baseUrl$endpoint" else url
|
val path = if (url.isEmpty()) "$baseUrl$endpoint" else url
|
||||||
|
|
||||||
// TODO before sending a request, make sure the accessToken is not expired
|
// TODO before sending a request, make sure the accessToken is not expired
|
||||||
|
|
||||||
return@coroutineScope (Dispatchers.IO) {
|
return@coroutineScope (Dispatchers.IO) {
|
||||||
val (request, response, result) = Fuel.get(path, params)
|
val (request, response, result) = Fuel.get(path, params)
|
||||||
.header("Authorization", "$tokenType $accessToken")
|
.header("Authorization", "$tokenType $accessToken")
|
||||||
|
@ -168,7 +170,7 @@ object Crunchyroll {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main media functions: browse, search, series, season, episodes, playback
|
* General element/media functions: browse, search, objects, season_list
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// TODO locale de-DE, categories
|
// TODO locale de-DE, categories
|
||||||
|
@ -190,7 +192,7 @@ object Crunchyroll {
|
||||||
val noneOptParams = 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
|
// if a season tag is present add it to the parameters
|
||||||
val parameters = if (seasonTag.isEmpty()) {
|
val parameters = if (seasonTag.isNotEmpty()) {
|
||||||
concatenate(noneOptParams, listOf("season_tag" to seasonTag))
|
concatenate(noneOptParams, listOf("season_tag" to seasonTag))
|
||||||
} else {
|
} else {
|
||||||
noneOptParams
|
noneOptParams
|
||||||
|
@ -231,7 +233,7 @@ object Crunchyroll {
|
||||||
* @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<Item> {
|
||||||
val episodesEndpoint = "/cms/v2/DE/M3/crunchyroll/objects/${objects.joinToString(",")}"
|
val episodesEndpoint = "/cms/v2/DE/M3/crunchyroll/objects/${objects.joinToString(",")}"
|
||||||
val parameters = listOf(
|
val parameters = listOf(
|
||||||
"locale" to locale,
|
"locale" to locale,
|
||||||
|
@ -247,6 +249,25 @@ object Crunchyroll {
|
||||||
} ?: NoneCollection
|
} ?: NoneCollection
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List all available seasons as **[SeasonListItem]**.
|
||||||
|
*/
|
||||||
|
@Suppress("unused")
|
||||||
|
suspend fun seasonList(): DiscSeasonList {
|
||||||
|
val seasonListEndpoint = "/content/v1/season_list"
|
||||||
|
val parameters = listOf("locale" to locale)
|
||||||
|
|
||||||
|
val result = request(seasonListEndpoint, parameters)
|
||||||
|
|
||||||
|
return result.component1()?.obj()?.let {
|
||||||
|
json.decodeFromString(it.toString())
|
||||||
|
} ?: NoneDiscSeasonList
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main media functions: series, season, episodes, playback
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* series id == crunchyroll id?
|
* series id == crunchyroll id?
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -15,44 +15,25 @@ enum class SortBy(val str: String) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* search, browse, watchlist data types (all collections)
|
* search, browse, DiscSeasonList, Watchlist, ContinueWatchingList data types all use Collection
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// TODO make generic
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class Collection(
|
data class Collection<T>(
|
||||||
@SerialName("total") val total: Int,
|
@SerialName("total") val total: Int,
|
||||||
@SerialName("items") val items: List<Item>
|
@SerialName("items") val items: List<T>
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO don't use aliases
|
typealias SearchResult = Collection<SearchCollection>
|
||||||
typealias SearchCollection = Collection
|
typealias SearchCollection = Collection<Item>
|
||||||
typealias BrowseResult = Collection
|
typealias BrowseResult = Collection<Item>
|
||||||
typealias Watchlist = Collection
|
typealias DiscSeasonList = Collection<SeasonListItem>
|
||||||
|
typealias Watchlist = Collection<Item>
|
||||||
|
typealias ContinueWatchingList = Collection<ContinueWatchingItem>
|
||||||
|
|
||||||
@Serializable
|
/**
|
||||||
data class SearchResult(
|
* panel data classes
|
||||||
@SerialName("total") val total: Int,
|
*/
|
||||||
@SerialName("items") val items: List<SearchCollection>
|
|
||||||
)
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class ContinueWatchingList(
|
|
||||||
@SerialName("total") val total: Int,
|
|
||||||
@SerialName("items") val items: List<ContinueWatchingItem>
|
|
||||||
)
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class ContinueWatchingItem(
|
|
||||||
@SerialName("panel") val panel: EpisodePanel,
|
|
||||||
@SerialName("new") val new: Boolean,
|
|
||||||
@SerialName("new_content") val newContent: Boolean,
|
|
||||||
// not present in up_next_account's continue_watching_item
|
|
||||||
// @SerialName("is_favorite") val isFavorite: Boolean,
|
|
||||||
// @SerialName("never_watched") val neverWatched: Boolean,
|
|
||||||
// @SerialName("completion_status") val completionStatus: Boolean,
|
|
||||||
@SerialName("playhead") val playhead: Int,
|
|
||||||
)
|
|
||||||
|
|
||||||
// the data class Item is used in browse and search
|
// the data class Item is used in browse and search
|
||||||
// TODO rename to MediaPanel
|
// TODO rename to MediaPanel
|
||||||
|
@ -64,7 +45,45 @@ data class Item(
|
||||||
val channel_id: String,
|
val channel_id: String,
|
||||||
val description: String,
|
val description: String,
|
||||||
val images: Images
|
val images: Images
|
||||||
// TODO metadata etc.
|
// TODO series_metadata etc.
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Images(val poster_tall: List<List<Poster>>, val poster_wide: List<List<Poster>>)
|
||||||
|
// crunchyroll why?
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Poster(val height: Int, val width: Int, val source: String, val type: String)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* season list data classes
|
||||||
|
*/
|
||||||
|
@Serializable
|
||||||
|
data class SeasonListItem(
|
||||||
|
@SerialName("id") val id: String,
|
||||||
|
@SerialName("localization") val localization: SeasonListLocalization
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class SeasonListLocalization(
|
||||||
|
@SerialName("title") val title: String,
|
||||||
|
@SerialName("description") val description: String,
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* continue_watching_item data classes
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class ContinueWatchingItem(
|
||||||
|
@SerialName("panel") val panel: EpisodePanel,
|
||||||
|
@SerialName("new") val new: Boolean,
|
||||||
|
@SerialName("new_content") val newContent: Boolean,
|
||||||
|
// not present in up_next_account's continue_watching_item
|
||||||
|
// @SerialName("is_favorite") val isFavorite: Boolean,
|
||||||
|
// @SerialName("never_watched") val neverWatched: Boolean,
|
||||||
|
// @SerialName("completion_status") val completionStatus: Boolean,
|
||||||
|
@SerialName("playhead") val playhead: Int,
|
||||||
)
|
)
|
||||||
|
|
||||||
// EpisodePanel is used in ContinueWatchingItem
|
// EpisodePanel is used in ContinueWatchingItem
|
||||||
|
@ -79,13 +98,6 @@ data class EpisodePanel(
|
||||||
@SerialName("episode_metadata") val episodeMetadata: EpisodeMetadata,
|
@SerialName("episode_metadata") val episodeMetadata: EpisodeMetadata,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class Images(val poster_tall: List<List<Poster>>, val poster_wide: List<List<Poster>>)
|
|
||||||
// crunchyroll why?
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class Poster(val height: Int, val width: Int, val source: String, val type: String)
|
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class EpisodeMetadata(
|
data class EpisodeMetadata(
|
||||||
@SerialName("series_id") val seriesId: String,
|
@SerialName("series_id") val seriesId: String,
|
||||||
|
@ -93,9 +105,11 @@ data class EpisodeMetadata(
|
||||||
)
|
)
|
||||||
|
|
||||||
val NoneItem = Item("", "", "", "", "", Images(emptyList(), emptyList()))
|
val NoneItem = Item("", "", "", "", "", Images(emptyList(), emptyList()))
|
||||||
val NoneCollection = Collection(0, emptyList())
|
val NoneCollection = Collection<Item>(0, emptyList())
|
||||||
val NoneSearchResult = SearchResult(0, emptyList())
|
val NoneSearchResult = SearchResult(0, emptyList())
|
||||||
val NoneBrowseResult = BrowseResult(0, emptyList())
|
val NoneBrowseResult = BrowseResult(0, emptyList())
|
||||||
|
val NoneDiscSeasonList = DiscSeasonList(0, emptyList())
|
||||||
|
val NoneWatchlist = Watchlist(0, emptyList())
|
||||||
val NoneContinueWatchingList = ContinueWatchingList(0, emptyList())
|
val NoneContinueWatchingList = ContinueWatchingList(0, emptyList())
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -11,6 +11,7 @@ import kotlinx.coroutines.joinAll
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.mosad.teapod.databinding.FragmentHomeBinding
|
import org.mosad.teapod.databinding.FragmentHomeBinding
|
||||||
import org.mosad.teapod.parser.crunchyroll.Crunchyroll
|
import org.mosad.teapod.parser.crunchyroll.Crunchyroll
|
||||||
|
import org.mosad.teapod.parser.crunchyroll.SortBy
|
||||||
import org.mosad.teapod.util.ItemMedia
|
import org.mosad.teapod.util.ItemMedia
|
||||||
import org.mosad.teapod.util.adapter.MediaItemAdapter
|
import org.mosad.teapod.util.adapter.MediaItemAdapter
|
||||||
import org.mosad.teapod.util.decoration.MediaItemDecoration
|
import org.mosad.teapod.util.decoration.MediaItemDecoration
|
||||||
|
@ -87,9 +88,17 @@ class HomeFragment : Fragment() {
|
||||||
}
|
}
|
||||||
asyncJobList.add(watchlistJob)
|
asyncJobList.add(watchlistJob)
|
||||||
|
|
||||||
// new titles TODO
|
// new simulcasts TODO replace with new titles? browse(sortBy = SortBy.NEWLY_ADDED, n = 50)
|
||||||
// adapterNewTitles = MediaItemAdapter(AoDParser.newTitlesList)
|
val simulcastsJob = lifecycleScope.launch {
|
||||||
// binding.recyclerNewTitles.adapter = adapterNewTitles
|
// val latestSeasonTag = Crunchyroll.seasonList().items.first().id
|
||||||
|
// val newSimulcasts = Crunchyroll.browse(seasonTag = latestSeasonTag, n = 50)
|
||||||
|
|
||||||
|
val newSimulcasts = Crunchyroll.browse(sortBy = SortBy.NEWLY_ADDED, n = 50)
|
||||||
|
|
||||||
|
adapterNewTitles = MediaItemAdapter(newSimulcasts.toItemMediaList())
|
||||||
|
binding.recyclerNewTitles.adapter = adapterNewTitles
|
||||||
|
}
|
||||||
|
asyncJobList.add(simulcastsJob)
|
||||||
|
|
||||||
// top ten TODO
|
// top ten TODO
|
||||||
// adapterTopTen = MediaItemAdapter(AoDParser.topTenList)
|
// adapterTopTen = MediaItemAdapter(AoDParser.topTenList)
|
||||||
|
@ -135,10 +144,10 @@ class HomeFragment : Fragment() {
|
||||||
activity?.showFragment(MediaFragment(id))
|
activity?.showFragment(MediaFragment(id))
|
||||||
}
|
}
|
||||||
|
|
||||||
// adapterNewTitles.onItemClick = { id, _ ->
|
adapterNewTitles.onItemClick = { id, _ ->
|
||||||
// activity?.showFragment(MediaFragment("")) //(mediaId))
|
activity?.showFragment(MediaFragment(id))
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// adapterTopTen.onItemClick = { id, _ ->
|
// adapterTopTen.onItemClick = { id, _ ->
|
||||||
// activity?.showFragment(MediaFragment("")) //(mediaId))
|
// activity?.showFragment(MediaFragment("")) //(mediaId))
|
||||||
// }
|
// }
|
||||||
|
|
|
@ -2,7 +2,9 @@ package org.mosad.teapod.util
|
||||||
|
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import org.mosad.teapod.parser.crunchyroll.Collection
|
import org.mosad.teapod.parser.crunchyroll.Collection
|
||||||
|
import org.mosad.teapod.parser.crunchyroll.ContinueWatchingItem
|
||||||
import org.mosad.teapod.parser.crunchyroll.ContinueWatchingList
|
import org.mosad.teapod.parser.crunchyroll.ContinueWatchingList
|
||||||
|
import org.mosad.teapod.parser.crunchyroll.Item
|
||||||
|
|
||||||
fun TextView.setDrawableTop(drawable: Int) {
|
fun TextView.setDrawableTop(drawable: Int) {
|
||||||
this.setCompoundDrawablesWithIntrinsicBounds(0, drawable, 0, 0)
|
this.setCompoundDrawablesWithIntrinsicBounds(0, drawable, 0, 0)
|
||||||
|
@ -13,16 +15,15 @@ fun <T> concatenate(vararg lists: List<T>): List<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO move to correct location
|
// TODO move to correct location
|
||||||
fun Collection.toItemMediaList(): List<ItemMedia> {
|
fun Collection<Item>.toItemMediaList(): List<ItemMedia> {
|
||||||
return this.items.map {
|
return this.items.map {
|
||||||
ItemMedia(it.id, it.title, it.images.poster_wide[0][0].source)
|
ItemMedia(it.id, it.title, it.images.poster_wide[0][0].source)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun ContinueWatchingList.toItemMediaList(): List<ItemMedia> {
|
@JvmName("toItemMediaListContinueWatchingItem")
|
||||||
|
fun Collection<ContinueWatchingItem>.toItemMediaList(): List<ItemMedia> {
|
||||||
return this.items.map {
|
return this.items.map {
|
||||||
// TODO add season and episode to title
|
|
||||||
ItemMedia(it.panel.episodeMetadata.seriesId, it.panel.title, it.panel.images.thumbnail[0][0].source)
|
ItemMedia(it.panel.episodeMetadata.seriesId, it.panel.title, it.panel.images.thumbnail[0][0].source)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue