fix playback & update to agp 7.4.0
updated the crunchyroll parser to use the new streams endpoint to retrieve the media streams
This commit is contained in:
parent
9380f98098
commit
097383a082
|
@ -12,8 +12,8 @@ android {
|
||||||
applicationId "org.mosad.teapod"
|
applicationId "org.mosad.teapod"
|
||||||
minSdkVersion 23
|
minSdkVersion 23
|
||||||
targetSdkVersion 32
|
targetSdkVersion 32
|
||||||
versionCode 100000 //01.00.000
|
versionCode 100990 //01.00.000
|
||||||
versionName "1.0.0"
|
versionName "1.1.0-beta1"
|
||||||
|
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
resValue "string", "build_time", buildTime()
|
resValue "string", "build_time", buildTime()
|
||||||
|
@ -53,7 +53,7 @@ dependencies {
|
||||||
|
|
||||||
implementation 'androidx.core:core-ktx:1.9.0'
|
implementation 'androidx.core:core-ktx:1.9.0'
|
||||||
implementation 'androidx.core:core-splashscreen:1.0.0'
|
implementation 'androidx.core:core-splashscreen:1.0.0'
|
||||||
implementation 'androidx.appcompat:appcompat:1.5.1'
|
implementation 'androidx.appcompat:appcompat:1.6.0'
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||||||
implementation 'androidx.navigation:navigation-fragment-ktx:2.5.3'
|
implementation 'androidx.navigation:navigation-fragment-ktx:2.5.3'
|
||||||
implementation 'androidx.navigation:navigation-ui-ktx:2.5.3'
|
implementation 'androidx.navigation:navigation-ui-ktx:2.5.3'
|
||||||
|
@ -80,8 +80,8 @@ dependencies {
|
||||||
implementation "io.ktor:ktor-serialization-kotlinx-json:$ktor_version"
|
implementation "io.ktor:ktor-serialization-kotlinx-json:$ktor_version"
|
||||||
|
|
||||||
testImplementation 'junit:junit:4.13.2'
|
testImplementation 'junit:junit:4.13.2'
|
||||||
androidTestImplementation 'androidx.test.ext:junit:1.1.4'
|
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
|
||||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.0'
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -52,6 +52,9 @@
|
||||||
# @Serializable and @Polymorphic are used at runtime for polymorphic serialization.
|
# @Serializable and @Polymorphic are used at runtime for polymorphic serialization.
|
||||||
-keepattributes RuntimeVisibleAnnotations,AnnotationDefault
|
-keepattributes RuntimeVisibleAnnotations,AnnotationDefault
|
||||||
|
|
||||||
|
# This is generated automatically by the Android Gradle plugin.
|
||||||
|
-dontwarn org.slf4j.impl.StaticLoggerBinder
|
||||||
|
|
||||||
#misc
|
#misc
|
||||||
-dontwarn java.lang.instrument.ClassFileTransformer
|
-dontwarn java.lang.instrument.ClassFileTransformer
|
||||||
-dontwarn java.lang.ClassValue
|
-dontwarn java.lang.ClassValue
|
||||||
|
|
|
@ -305,7 +305,8 @@ object Crunchyroll {
|
||||||
|
|
||||||
// if the cache has more than 100 entries clear it, so it doesn't become a memory problem
|
// if the cache has more than 100 entries clear it, so it doesn't become a memory problem
|
||||||
// Note: this value is totally guessed and should be replaced by a properly researched value
|
// Note: this value is totally guessed and should be replaced by a properly researched value
|
||||||
if (browsingCache.size > 100) {
|
// TODO 100 is way to high as it's not the number of items but BrowseResults
|
||||||
|
if (browsingCache.size > 10) {
|
||||||
browsingCache.clear()
|
browsingCache.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -436,13 +437,10 @@ object Crunchyroll {
|
||||||
* @return A **[Seasons]** object with a list of **[Season]**
|
* @return A **[Seasons]** object with a list of **[Season]**
|
||||||
*/
|
*/
|
||||||
suspend fun seasons(seriesId: String): Seasons {
|
suspend fun seasons(seriesId: String): Seasons {
|
||||||
val seasonsEndpoint = "/cms/v2/${token.country}/M3/crunchyroll/seasons"
|
val seasonsEndpoint = "/content/v2/cms/series/$seriesId/seasons"
|
||||||
val parameters = listOf(
|
val parameters = listOf(
|
||||||
"series_id" to seriesId,
|
"preferred_audio_language" to Preferences.preferredLocale.toLanguageTag(),
|
||||||
"locale" to Preferences.preferredLocale.toLanguageTag(),
|
"locale" to Preferences.preferredLocale.toLanguageTag()
|
||||||
"Signature" to signature,
|
|
||||||
"Policy" to policy,
|
|
||||||
"Key-Pair-Id" to keyPairID
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return try {
|
return try {
|
||||||
|
@ -460,13 +458,10 @@ object Crunchyroll {
|
||||||
* @return A **[Episodes]** object with a list of **[Episode]**
|
* @return A **[Episodes]** object with a list of **[Episode]**
|
||||||
*/
|
*/
|
||||||
suspend fun episodes(seasonId: String): Episodes {
|
suspend fun episodes(seasonId: String): Episodes {
|
||||||
val episodesEndpoint = "/cms/v2/${token.country}/M3/crunchyroll/episodes"
|
val episodesEndpoint = "/content/v2/cms/seasons/$seasonId/episodes"
|
||||||
val parameters = listOf(
|
val parameters = listOf(
|
||||||
"season_id" to seasonId,
|
"preferred_audio_language" to Preferences.preferredLocale.toLanguageTag(),
|
||||||
"locale" to Preferences.preferredLocale.toLanguageTag(),
|
"locale" to Preferences.preferredLocale.toLanguageTag()
|
||||||
"Signature" to signature,
|
|
||||||
"Policy" to policy,
|
|
||||||
"Key-Pair-Id" to keyPairID
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return try {
|
return try {
|
||||||
|
@ -480,15 +475,20 @@ object Crunchyroll {
|
||||||
/**
|
/**
|
||||||
* Get all available subtitles and streams of a episode.
|
* Get all available subtitles and streams of a episode.
|
||||||
*
|
*
|
||||||
* @param url The playback url of a episode
|
* @param url The streams url of a episode
|
||||||
* @return A **[Playback]** object
|
* @return A **[Streams]** object
|
||||||
*/
|
*/
|
||||||
suspend fun playback(url: String): Playback {
|
suspend fun streams(url: String): Streams {
|
||||||
|
val parameters = listOf(
|
||||||
|
"preferred_audio_language" to Preferences.preferredLocale.toLanguageTag(),
|
||||||
|
"locale" to Preferences.preferredLocale.toLanguageTag()
|
||||||
|
)
|
||||||
|
|
||||||
return try {
|
return try {
|
||||||
requestGet("", url = url)
|
requestGet(url, parameters)
|
||||||
} catch (ex: SerializationException) {
|
} catch (ex: SerializationException) {
|
||||||
Log.e(TAG, "SerializationException in playback(), with url = $url.", ex)
|
Log.e(TAG, "SerializationException in streams().", ex)
|
||||||
NonePlayback
|
NoneStreams
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -255,16 +255,16 @@ val NoneSeries = Series("", "", "", Images(emptyList(), emptyList()), emptyList(
|
||||||
@Serializable
|
@Serializable
|
||||||
data class Seasons(
|
data class Seasons(
|
||||||
@SerialName("total") val total: Int,
|
@SerialName("total") val total: Int,
|
||||||
@SerialName("items") val items: List<Season>
|
@SerialName("data") val data: List<Season>
|
||||||
) {
|
) {
|
||||||
fun getPreferredSeasonByLocal(local: Locale): Season {
|
fun getPreferredSeasonByLocal(local: Locale): Season {
|
||||||
return items.firstOrNull { season ->
|
return data.firstOrNull { season ->
|
||||||
// try to get the the first seasons which matches the preferred local
|
// try to get the the first seasons which matches the preferred local
|
||||||
season.slugTitle.endsWith("${local.getDisplayLanguage(Locale.ENGLISH)}-dub", true)
|
season.slugTitle.endsWith("${local.getDisplayLanguage(Locale.ENGLISH)}-dub", true)
|
||||||
} ?: items.firstOrNull { season ->
|
} ?: data.firstOrNull { season ->
|
||||||
// if there is no season with the preferred local, try to find a subbed season
|
// if there is no season with the preferred local, try to find a subbed season
|
||||||
season.isSubbed
|
season.isSubbed
|
||||||
} ?: items.first() // if no preferred language and no sub, use the first season
|
} ?: data.first() // if no preferred language and no sub, use the first season
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -289,7 +289,7 @@ val NoneSeason = Season("", "", "", "", 0, isSubbed = false, isDubbed = false)
|
||||||
@Serializable
|
@Serializable
|
||||||
data class Episodes(
|
data class Episodes(
|
||||||
@SerialName("total") val total: Int,
|
@SerialName("total") val total: Int,
|
||||||
@SerialName("items") val items: List<Episode>
|
@SerialName("data") val data: List<Episode>
|
||||||
)
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
|
@ -310,6 +310,7 @@ data class Episode(
|
||||||
@SerialName("images") val images: Thumbnail,
|
@SerialName("images") val images: Thumbnail,
|
||||||
@SerialName("duration_ms") val durationMs: Int,
|
@SerialName("duration_ms") val durationMs: Int,
|
||||||
@SerialName("playback") val playback: String,
|
@SerialName("playback") val playback: String,
|
||||||
|
@SerialName("streams_link") val streamsLink: String,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
|
@ -334,7 +335,8 @@ val NoneEpisode = Episode(
|
||||||
isDubbed = false,
|
isDubbed = false,
|
||||||
images = Thumbnail(listOf()),
|
images = Thumbnail(listOf()),
|
||||||
durationMs = 0,
|
durationMs = 0,
|
||||||
playback = ""
|
playback = "",
|
||||||
|
streamsLink = ""
|
||||||
)
|
)
|
||||||
|
|
||||||
typealias PlayheadsMap = Map<String, PlayheadObject>
|
typealias PlayheadsMap = Map<String, PlayheadObject>
|
||||||
|
@ -366,34 +368,26 @@ val NoneDatalabIntro = DatalabIntro("", 0f, 0f, 0f, "", "", "")
|
||||||
/**
|
/**
|
||||||
* playback/stream data classes
|
* playback/stream data classes
|
||||||
*/
|
*/
|
||||||
@Serializable
|
|
||||||
data class Playback(
|
|
||||||
@SerialName("audio_locale") val audioLocale: String,
|
|
||||||
@SerialName("subtitles") val subtitles: Map<String, Subtitle>,
|
|
||||||
@SerialName("streams") val streams: Streams,
|
|
||||||
)
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class Subtitle(
|
|
||||||
@SerialName("locale") val locale: String,
|
|
||||||
@SerialName("url") val url: String,
|
|
||||||
@SerialName("format") val format: String,
|
|
||||||
)
|
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class Streams(
|
data class Streams(
|
||||||
|
@SerialName("total") val total: Int,
|
||||||
|
@SerialName("data") val data: List<StreamList>,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class StreamList(
|
||||||
@SerialName("adaptive_dash") val adaptive_dash: Map<String, Stream>,
|
@SerialName("adaptive_dash") val adaptive_dash: Map<String, Stream>,
|
||||||
@SerialName("adaptive_hls") val adaptive_hls: Map<String, Stream>,
|
@SerialName("adaptive_hls") val adaptive_hls: Map<String, Stream>,
|
||||||
|
@SerialName("download_dash") val downloadDash: Map<String, Stream>,
|
||||||
@SerialName("download_hls") val download_hls: Map<String, Stream>,
|
@SerialName("download_hls") val download_hls: Map<String, Stream>,
|
||||||
@SerialName("drm_adaptive_dash") val drm_adaptive_dash: Map<String, Stream>,
|
// @SerialName("drm_adaptive_dash") val drmAdaptiveDash: Map<String, Stream>,
|
||||||
@SerialName("drm_adaptive_hls") val drm_adaptive_hls: Map<String, Stream>,
|
// @SerialName("drm_adaptive_hls") val drmAdaptiveHls: Map<String, Stream>,
|
||||||
@SerialName("drm_download_hls") val drm_download_hls: Map<String, Stream>,
|
// @SerialName("drm_download_dash") val drmDownloadDash: Map<String, Stream>,
|
||||||
@SerialName("trailer_dash") val trailer_dash: Map<String, Stream>,
|
// @SerialName("drm_download_hls") val drmDownloadHls: Map<String, Stream>,
|
||||||
@SerialName("trailer_hls") val trailer_hls: Map<String, Stream>,
|
// @SerialName("vo_adaptive_dash") val vo_adaptive_dash: Map<String, Stream>,
|
||||||
@SerialName("vo_adaptive_dash") val vo_adaptive_dash: Map<String, Stream>,
|
// @SerialName("vo_adaptive_hls") val vo_adaptive_hls: Map<String, Stream>,
|
||||||
@SerialName("vo_adaptive_hls") val vo_adaptive_hls: Map<String, Stream>,
|
// @SerialName("vo_drm_adaptive_dash") val vo_drm_adaptive_dash: Map<String, Stream>,
|
||||||
@SerialName("vo_drm_adaptive_dash") val vo_drm_adaptive_dash: Map<String, Stream>,
|
// @SerialName("vo_drm_adaptive_hls") val vo_drm_adaptive_hls: Map<String, Stream>,
|
||||||
@SerialName("vo_drm_adaptive_hls") val vo_drm_adaptive_hls: Map<String, Stream>,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
|
@ -403,13 +397,11 @@ data class Stream(
|
||||||
@SerialName("vcodec") val vcodec: String = "", // default/nullable value since optional
|
@SerialName("vcodec") val vcodec: String = "", // default/nullable value since optional
|
||||||
)
|
)
|
||||||
|
|
||||||
val NonePlayback = Playback(
|
val NoneStreams = Streams(
|
||||||
"",
|
0,
|
||||||
mapOf(),
|
arrayListOf(StreamList(
|
||||||
Streams(
|
mapOf(), mapOf(), mapOf(), mapOf()
|
||||||
mapOf(), mapOf(), mapOf(), mapOf(), mapOf(), mapOf(),
|
))
|
||||||
mapOf(), mapOf(), mapOf(), mapOf(), mapOf(), mapOf(),
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -98,7 +98,7 @@ class MediaFragment(private val mediaIdStr: String) : Fragment() {
|
||||||
.into(binding.imageBackdrop)
|
.into(binding.imageBackdrop)
|
||||||
|
|
||||||
binding.textYear.text = when(tmdbResult) {
|
binding.textYear.text = when(tmdbResult) {
|
||||||
is TMDBTVShow -> (tmdbResult as TMDBTVShow).firstAirDate.substring(0, 4)
|
is TMDBTVShow -> (tmdbResult as TMDBTVShow).firstAirDate?.substring(0, 4)
|
||||||
is TMDBMovie -> (tmdbResult as TMDBMovie).releaseDate.substring(0, 4)
|
is TMDBMovie -> (tmdbResult as TMDBMovie).releaseDate.substring(0, 4)
|
||||||
else -> ""
|
else -> ""
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,7 +67,7 @@ class MediaFragmentEpisodes : Fragment() {
|
||||||
private fun showSeasonSelection(v: View) {
|
private fun showSeasonSelection(v: View) {
|
||||||
// TODO replace with Exposed dropdown menu: https://material.io/components/menus/android#exposed-dropdown-menus
|
// TODO replace with Exposed dropdown menu: https://material.io/components/menus/android#exposed-dropdown-menus
|
||||||
val popup = PopupMenu(requireContext(), v)
|
val popup = PopupMenu(requireContext(), v)
|
||||||
model.seasonsCrunchy.items.forEach { season ->
|
model.seasonsCrunchy.data.forEach { season ->
|
||||||
popup.menu.add(getString(
|
popup.menu.add(getString(
|
||||||
R.string.season_number_title,
|
R.string.season_number_title,
|
||||||
season.seasonNumber,
|
season.seasonNumber,
|
||||||
|
|
|
@ -59,7 +59,7 @@ class MediaFragmentViewModel(application: Application) : AndroidViewModel(applic
|
||||||
|
|
||||||
// load the preferred season:
|
// load the preferred season:
|
||||||
// next episode > preferred language (language per season, not per stream)
|
// next episode > preferred language (language per season, not per stream)
|
||||||
currentSeasonCrunchy = seasonsCrunchy.items.firstOrNull{ season ->
|
currentSeasonCrunchy = seasonsCrunchy.data.firstOrNull{ season ->
|
||||||
season.id == upNextSeries.panel.episodeMetadata.seasonId
|
season.id == upNextSeries.panel.episodeMetadata.seasonId
|
||||||
} ?: seasonsCrunchy.getPreferredSeasonByLocal(Preferences.preferredLocale)
|
} ?: seasonsCrunchy.getPreferredSeasonByLocal(Preferences.preferredLocale)
|
||||||
// Note: if we need to query metaDB, do it now
|
// Note: if we need to query metaDB, do it now
|
||||||
|
@ -67,10 +67,10 @@ class MediaFragmentViewModel(application: Application) : AndroidViewModel(applic
|
||||||
// load episodes and metaDB in parallel (tmdb needs mediaType, which is set via episodes)
|
// load episodes and metaDB in parallel (tmdb needs mediaType, which is set via episodes)
|
||||||
viewModelScope.launch { episodesCrunchy = Crunchyroll.episodes(currentSeasonCrunchy.id) }.join()
|
viewModelScope.launch { episodesCrunchy = Crunchyroll.episodes(currentSeasonCrunchy.id) }.join()
|
||||||
currentEpisodesCrunchy.clear()
|
currentEpisodesCrunchy.clear()
|
||||||
currentEpisodesCrunchy.addAll(episodesCrunchy.items)
|
currentEpisodesCrunchy.addAll(episodesCrunchy.data)
|
||||||
|
|
||||||
// set media type
|
// set media type
|
||||||
mediaType = episodesCrunchy.items.firstOrNull()?.let {
|
mediaType = episodesCrunchy.data.firstOrNull()?.let {
|
||||||
if (it.episodeNumber != null) MediaType.TVSHOW else MediaType.MOVIE
|
if (it.episodeNumber != null) MediaType.TVSHOW else MediaType.MOVIE
|
||||||
} ?: MediaType.OTHER
|
} ?: MediaType.OTHER
|
||||||
|
|
||||||
|
@ -78,7 +78,7 @@ class MediaFragmentViewModel(application: Application) : AndroidViewModel(applic
|
||||||
listOf(
|
listOf(
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
// get playheads (including fully watched state)
|
// get playheads (including fully watched state)
|
||||||
val episodeIDs = episodesCrunchy.items.map { it.id }
|
val episodeIDs = episodesCrunchy.data.map { it.id }
|
||||||
currentPlayheads.clear()
|
currentPlayheads.clear()
|
||||||
currentPlayheads.putAll(Crunchyroll.playheads(episodeIDs))
|
currentPlayheads.putAll(Crunchyroll.playheads(episodeIDs))
|
||||||
},
|
},
|
||||||
|
@ -124,16 +124,16 @@ class MediaFragmentViewModel(application: Application) : AndroidViewModel(applic
|
||||||
|
|
||||||
// set currentSeasonCrunchy to the new season with id == seasonId, if the id isn't found,
|
// set currentSeasonCrunchy to the new season with id == seasonId, if the id isn't found,
|
||||||
// don't change the current season (this should/can never happen)
|
// don't change the current season (this should/can never happen)
|
||||||
currentSeasonCrunchy = seasonsCrunchy.items.firstOrNull {
|
currentSeasonCrunchy = seasonsCrunchy.data.firstOrNull {
|
||||||
it.id == seasonId
|
it.id == seasonId
|
||||||
} ?: currentSeasonCrunchy
|
} ?: currentSeasonCrunchy
|
||||||
|
|
||||||
episodesCrunchy = Crunchyroll.episodes(currentSeasonCrunchy.id)
|
episodesCrunchy = Crunchyroll.episodes(currentSeasonCrunchy.id)
|
||||||
currentEpisodesCrunchy.clear()
|
currentEpisodesCrunchy.clear()
|
||||||
currentEpisodesCrunchy.addAll(episodesCrunchy.items)
|
currentEpisodesCrunchy.addAll(episodesCrunchy.data)
|
||||||
|
|
||||||
// update playheads playheads (including fully watched state)
|
// update playheads playheads (including fully watched state)
|
||||||
val episodeIDs = episodesCrunchy.items.map { it.id }
|
val episodeIDs = episodesCrunchy.data.map { it.id }
|
||||||
currentPlayheads.clear()
|
currentPlayheads.clear()
|
||||||
currentPlayheads.putAll(Crunchyroll.playheads(episodeIDs))
|
currentPlayheads.putAll(Crunchyroll.playheads(episodeIDs))
|
||||||
}
|
}
|
||||||
|
@ -151,7 +151,7 @@ class MediaFragmentViewModel(application: Application) : AndroidViewModel(applic
|
||||||
suspend fun updateOnResume() {
|
suspend fun updateOnResume() {
|
||||||
joinAll(
|
joinAll(
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
val episodeIDs = episodesCrunchy.items.map { it.id }
|
val episodeIDs = episodesCrunchy.data.map { it.id }
|
||||||
currentPlayheads.clear()
|
currentPlayheads.clear()
|
||||||
currentPlayheads.putAll(Crunchyroll.playheads(episodeIDs))
|
currentPlayheads.putAll(Crunchyroll.playheads(episodeIDs))
|
||||||
},
|
},
|
||||||
|
|
|
@ -75,7 +75,7 @@ class PlayerViewModel(application: Application) : AndroidViewModel(application)
|
||||||
internal set
|
internal set
|
||||||
var currentEpisode = NoneEpisode
|
var currentEpisode = NoneEpisode
|
||||||
internal set
|
internal set
|
||||||
var currentPlayback = NonePlayback
|
var currentStreams = NoneStreams
|
||||||
|
|
||||||
// current playback settings
|
// current playback settings
|
||||||
var currentLanguage: Locale = Preferences.preferredLocale
|
var currentLanguage: Locale = Preferences.preferredLocale
|
||||||
|
@ -134,9 +134,9 @@ class PlayerViewModel(application: Application) : AndroidViewModel(application)
|
||||||
episodes = Crunchyroll.episodes(seasonId)
|
episodes = Crunchyroll.episodes(seasonId)
|
||||||
|
|
||||||
listOf(
|
listOf(
|
||||||
viewModelScope.launch { mediaMeta = loadMediaMeta(episodes.items.first().seriesId) },
|
viewModelScope.launch { mediaMeta = loadMediaMeta(episodes.data.first().seriesId) },
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
val episodeIDs = episodes.items.map { it.id }
|
val episodeIDs = episodes.data.map { it.id }
|
||||||
currentPlayheads = Crunchyroll.playheads(episodeIDs)
|
currentPlayheads = Crunchyroll.playheads(episodeIDs)
|
||||||
}
|
}
|
||||||
).joinAll()
|
).joinAll()
|
||||||
|
@ -178,7 +178,7 @@ class PlayerViewModel(application: Application) : AndroidViewModel(application)
|
||||||
* @param episodeId The ID of the episode you want to set currentEpisodeCr to
|
* @param episodeId The ID of the episode you want to set currentEpisodeCr to
|
||||||
*/
|
*/
|
||||||
suspend fun setCurrentEpisode(episodeId: String, startPlayback: Boolean = false) {
|
suspend fun setCurrentEpisode(episodeId: String, startPlayback: Boolean = false) {
|
||||||
currentEpisode = episodes.items.find { episode ->
|
currentEpisode = episodes.data.find { episode ->
|
||||||
episode.id == episodeId
|
episode.id == episodeId
|
||||||
} ?: NoneEpisode
|
} ?: NoneEpisode
|
||||||
|
|
||||||
|
@ -198,7 +198,8 @@ class PlayerViewModel(application: Application) : AndroidViewModel(application)
|
||||||
// needs to be blocking, currentPlayback must be present when calling playCurrentMedia()
|
// needs to be blocking, currentPlayback must be present when calling playCurrentMedia()
|
||||||
joinAll(
|
joinAll(
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
currentPlayback = Crunchyroll.playback(currentEpisode.playback)
|
currentStreams = Crunchyroll.streams(currentEpisode.streamsLink)
|
||||||
|
println("stream: $Streams")
|
||||||
},
|
},
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
Crunchyroll.playheads(listOf(currentEpisode.id))[currentEpisode.id]?.let {
|
Crunchyroll.playheads(listOf(currentEpisode.id))[currentEpisode.id]?.let {
|
||||||
|
@ -211,10 +212,10 @@ class PlayerViewModel(application: Application) : AndroidViewModel(application)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
currentIntroMetadata = Crunchyroll.datalabIntro(currentEpisode.id)
|
currentIntroMetadata = NoneDatalabIntro //Crunchyroll.datalabIntro(currentEpisode.id)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
Log.d(classTag, "playback: ${currentEpisode.playback}")
|
Log.d(classTag, "playback: ${currentEpisode.streamsLink}")
|
||||||
|
|
||||||
if (startPlayback) {
|
if (startPlayback) {
|
||||||
playCurrentMedia()
|
playCurrentMedia()
|
||||||
|
@ -231,17 +232,17 @@ class PlayerViewModel(application: Application) : AndroidViewModel(application)
|
||||||
val preferredLocale = currentLanguage
|
val preferredLocale = currentLanguage
|
||||||
val fallbackLocal = Locale.US
|
val fallbackLocal = Locale.US
|
||||||
val url = when {
|
val url = when {
|
||||||
currentPlayback.streams.adaptive_hls.containsKey(preferredLocale.toLanguageTag()) -> {
|
currentStreams.data[0].adaptive_hls.containsKey(preferredLocale.toLanguageTag()) -> {
|
||||||
currentPlayback.streams.adaptive_hls[preferredLocale.toLanguageTag()]?.url
|
currentStreams.data[0].adaptive_hls[preferredLocale.toLanguageTag()]?.url
|
||||||
}
|
}
|
||||||
currentPlayback.streams.adaptive_hls.containsKey(fallbackLocal.toLanguageTag()) -> {
|
currentStreams.data[0].adaptive_hls.containsKey(fallbackLocal.toLanguageTag()) -> {
|
||||||
currentLanguage = fallbackLocal
|
currentLanguage = fallbackLocal
|
||||||
currentPlayback.streams.adaptive_hls[fallbackLocal.toLanguageTag()]?.url
|
currentStreams.data[0].adaptive_hls[fallbackLocal.toLanguageTag()]?.url
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
// if no language tag is present use the first entry
|
// if no language tag is present use the first entry
|
||||||
currentLanguage = Locale.ROOT
|
currentLanguage = Locale.ROOT
|
||||||
currentPlayback.streams.adaptive_hls.entries.first().value.url
|
currentStreams.data[0].adaptive_hls.entries.first().value.url
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Log.i(classTag, "stream url: $url")
|
Log.i(classTag, "stream url: $url")
|
||||||
|
@ -277,7 +278,7 @@ class PlayerViewModel(application: Application) : AndroidViewModel(application)
|
||||||
* @return Boolean: true if it is the last, else false.
|
* @return Boolean: true if it is the last, else false.
|
||||||
*/
|
*/
|
||||||
fun currentEpisodeIsLastEpisode(): Boolean {
|
fun currentEpisodeIsLastEpisode(): Boolean {
|
||||||
return episodes.items.lastOrNull()?.id == currentEpisode.id
|
return episodes.data.lastOrNull()?.id == currentEpisode.id
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun loadMediaMeta(crSeriesId: String): Meta? {
|
private suspend fun loadMediaMeta(crSeriesId: String): Meta? {
|
||||||
|
@ -297,7 +298,7 @@ class PlayerViewModel(application: Application) : AndroidViewModel(application)
|
||||||
}
|
}
|
||||||
|
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
val episodeIDs = episodes.items.map { it.id }
|
val episodeIDs = episodes.data.map { it.id }
|
||||||
currentPlayheads = Crunchyroll.playheads(episodeIDs)
|
currentPlayheads = Crunchyroll.playheads(episodeIDs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,7 +42,7 @@ class EpisodeListDialogFragment : DialogFragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
val adapterRecEpisodes = EpisodeItemAdapter(
|
val adapterRecEpisodes = EpisodeItemAdapter(
|
||||||
model.episodes.items,
|
model.episodes.data,
|
||||||
null,
|
null,
|
||||||
model.currentPlayheads.toMap(),
|
model.currentPlayheads.toMap(),
|
||||||
EpisodeItemAdapter.OnClickListener { episode ->
|
EpisodeItemAdapter.OnClickListener { episode ->
|
||||||
|
@ -56,7 +56,7 @@ class EpisodeListDialogFragment : DialogFragment() {
|
||||||
)
|
)
|
||||||
|
|
||||||
// get the position/index of the currently playing episode
|
// get the position/index of the currently playing episode
|
||||||
adapterRecEpisodes.currentSelected = model.episodes.items.indexOfFirst { it.id == model.currentEpisode.id }
|
adapterRecEpisodes.currentSelected = model.episodes.data.indexOfFirst { it.id == model.currentEpisode.id }
|
||||||
|
|
||||||
binding.recyclerEpisodesPlayer.adapter = adapterRecEpisodes
|
binding.recyclerEpisodesPlayer.adapter = adapterRecEpisodes
|
||||||
binding.recyclerEpisodesPlayer.scrollToPosition(adapterRecEpisodes.currentSelected)
|
binding.recyclerEpisodesPlayer.scrollToPosition(adapterRecEpisodes.currentSelected)
|
||||||
|
|
|
@ -46,7 +46,7 @@ class LanguageSettingsDialogFragment : DialogFragment() {
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
model.currentPlayback.streams.adaptive_hls.keys.forEach { languageTag ->
|
model.currentStreams.data[0].adaptive_hls.keys.forEach { languageTag ->
|
||||||
val locale = Locale.forLanguageTag(languageTag)
|
val locale = Locale.forLanguageTag(languageTag)
|
||||||
addLanguage(locale, locale == model.currentLanguage) { v ->
|
addLanguage(locale, locale == model.currentLanguage) { v ->
|
||||||
selectedLocale = locale
|
selectedLocale = locale
|
||||||
|
|
|
@ -102,9 +102,9 @@ data class TMDBTVShow(
|
||||||
@SerialName("overview")override val overview: String,
|
@SerialName("overview")override val overview: String,
|
||||||
@SerialName("poster_path") override val posterPath: String?,
|
@SerialName("poster_path") override val posterPath: String?,
|
||||||
@SerialName("backdrop_path") override val backdropPath: String?,
|
@SerialName("backdrop_path") override val backdropPath: String?,
|
||||||
@SerialName("first_air_date") val firstAirDate: String,
|
@SerialName("first_air_date") val firstAirDate: String?,
|
||||||
@SerialName("last_air_date") val lastAirDate: String,
|
@SerialName("last_air_date") val lastAirDate: String?,
|
||||||
@SerialName("status") val status: String,
|
@SerialName("status") val status: String?,
|
||||||
// TODO genres
|
// TODO genres
|
||||||
) : TMDBResult
|
) : TMDBResult
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ buildscript {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:7.3.1'
|
classpath 'com.android.tools.build:gradle:7.4.0'
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
|
|
||||||
// NOTE: Do not place your application dependencies here; they belong
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
|
|
Loading…
Reference in New Issue