forked from recloudstream/cloudstream
not done 2
This commit is contained in:
parent
3f19429805
commit
f57b12d89c
5 changed files with 312 additions and 267 deletions
|
@ -21,7 +21,6 @@ import android.view.View.GONE
|
|||
import android.view.View.VISIBLE
|
||||
import android.view.ViewGroup
|
||||
import android.widget.*
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.content.FileProvider
|
||||
import androidx.core.view.isGone
|
||||
|
@ -39,7 +38,6 @@ import com.google.android.material.button.MaterialButton
|
|||
import com.lagradost.cloudstream3.*
|
||||
import com.lagradost.cloudstream3.APIHolder.getApiFromName
|
||||
import com.lagradost.cloudstream3.APIHolder.getId
|
||||
import com.lagradost.cloudstream3.APIHolder.unixTime
|
||||
import com.lagradost.cloudstream3.APIHolder.updateHasTrailers
|
||||
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
|
||||
import com.lagradost.cloudstream3.CommonActivity.getCastSession
|
||||
|
@ -84,7 +82,6 @@ import com.lagradost.cloudstream3.utils.UIHelper.popCurrentPage
|
|||
import com.lagradost.cloudstream3.utils.UIHelper.popupMenuNoIcons
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.popupMenuNoIconsAndNoStringRes
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.requestRW
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.setImage
|
||||
import com.lagradost.cloudstream3.utils.VideoDownloadManager.getFileName
|
||||
import com.lagradost.cloudstream3.utils.VideoDownloadManager.sanitizeFilename
|
||||
import kotlinx.android.synthetic.main.fragment_result.*
|
||||
|
@ -95,10 +92,8 @@ import kotlinx.android.synthetic.main.result_sync.*
|
|||
import kotlinx.android.synthetic.main.trailer_custom_layout.*
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.File
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
const val START_ACTION_NORMAL = 0
|
||||
const val START_ACTION_RESUME_LATEST = 1
|
||||
|
@ -458,11 +453,6 @@ class ResultFragment : ResultTrailerPlayer() {
|
|||
0 // THIS IS USED TO PREVENT LATE EVENTS, AFTER DISMISS WAS CLICKED
|
||||
private lateinit var viewModel: ResultViewModel2 //by activityViewModels()
|
||||
private lateinit var syncModel: SyncViewModel
|
||||
private var currentHeaderName: String? = null
|
||||
private var currentType: TvType? = null
|
||||
private var currentEpisodes: List<ResultEpisode>? = null
|
||||
private var downloadButton: EasyDownloadButton? = null
|
||||
private var syncdata: Map<String, String>? = null
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
|
@ -490,13 +480,6 @@ class ResultFragment : ResultTrailerPlayer() {
|
|||
super.onDestroyView()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
//requireActivity().viewModelStore.clear() // REMEMBER THE CLEAR
|
||||
|
||||
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
activity?.let {
|
||||
|
@ -563,52 +546,6 @@ class ResultFragment : ResultTrailerPlayer() {
|
|||
var startAction: Int? = null
|
||||
private var startValue: Int? = null
|
||||
|
||||
private fun setFormatText(textView: TextView?, @StringRes format: Int, arg: Any?) {
|
||||
// java.util.IllegalFormatConversionException: f != java.lang.Integer
|
||||
// This can fail with malformed formatting
|
||||
normalSafeApiCall {
|
||||
if (arg == null) {
|
||||
textView?.isVisible = false
|
||||
} else {
|
||||
val text = context?.getString(format)?.format(arg)
|
||||
if (text == null) {
|
||||
textView?.isVisible = false
|
||||
} else {
|
||||
textView?.isVisible = true
|
||||
textView?.text = text
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setDuration(duration: Int?) {
|
||||
setFormatText(result_meta_duration, R.string.duration_format, duration)
|
||||
}
|
||||
|
||||
private fun setShow(showStatus: ShowStatus?) {
|
||||
val status = when (showStatus) {
|
||||
null -> null
|
||||
ShowStatus.Ongoing -> R.string.status_ongoing
|
||||
ShowStatus.Completed -> R.string.status_completed
|
||||
}
|
||||
|
||||
if (status == null) {
|
||||
result_meta_status?.isVisible = false
|
||||
} else {
|
||||
context?.getString(status)?.let {
|
||||
result_meta_status?.text = it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setYear(year: Int?) {
|
||||
setFormatText(result_meta_year, R.string.year_format, year)
|
||||
}
|
||||
|
||||
private fun setRating(rating: Int?) {
|
||||
setFormatText(result_meta_rating, R.string.rating_format, rating?.div(1000f))
|
||||
}
|
||||
|
||||
var currentTrailers: List<ExtractorLink> = emptyList()
|
||||
var currentTrailerIndex = 0
|
||||
|
||||
|
@ -1339,54 +1276,7 @@ class ResultFragment : ResultTrailerPlayer() {
|
|||
result_episode_select?.isFocusableInTouchMode = context?.isTvSettings() == true
|
||||
result_dub_select?.isFocusableInTouchMode = context?.isTvSettings() == true
|
||||
|
||||
observe(viewModel.selectedSeason) { season ->
|
||||
result_season_button?.text = fromIndexToSeasonText(season)
|
||||
}
|
||||
|
||||
observe(viewModel.seasonSelections) { seasonList ->
|
||||
result_season_button?.visibility = if (seasonList.size <= 1) GONE else VISIBLE.also {
|
||||
|
||||
// If the season button is visible the result season button will be next focus down
|
||||
if (result_series_parent?.isVisible == true)
|
||||
setFocusUpAndDown(result_resume_series_button, result_season_button)
|
||||
else
|
||||
setFocusUpAndDown(result_bookmark_button, result_season_button)
|
||||
}
|
||||
|
||||
result_season_button?.setOnClickListener {
|
||||
result_season_button?.popupMenuNoIconsAndNoStringRes(
|
||||
items = seasonList
|
||||
.map { (name, season) ->
|
||||
Pair(
|
||||
season ?: -2,
|
||||
name ?: fromIndexToSeasonText(season)
|
||||
)
|
||||
},
|
||||
) {
|
||||
val id = this.itemId
|
||||
|
||||
viewModel.changeSeason(if (id == -2) null else id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
observe(viewModel.selectedRange) { range ->
|
||||
result_episode_select?.text = range
|
||||
}
|
||||
|
||||
observe(viewModel.rangeOptions) { range ->
|
||||
episodeRanges = range
|
||||
result_episode_select?.visibility = if (range.size <= 1) GONE else VISIBLE.also {
|
||||
|
||||
// If Season button is invisible then the bookmark button next focus is episode select
|
||||
if (result_season_button?.isVisible != true) {
|
||||
if (result_series_parent?.isVisible == true)
|
||||
setFocusUpAndDown(result_resume_series_button, result_episode_select)
|
||||
else
|
||||
setFocusUpAndDown(result_bookmark_button, result_episode_select)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context?.let { ctx ->
|
||||
val arrayAdapter = ArrayAdapter<String>(ctx, R.layout.sort_bottom_single_choice)
|
||||
|
@ -1546,6 +1436,7 @@ class ResultFragment : ResultTrailerPlayer() {
|
|||
result_overlapping_panels?.setStartPanelLockState(if (closed) OverlappingPanelsLayout.LockState.CLOSE else OverlappingPanelsLayout.LockState.UNLOCKED)
|
||||
}
|
||||
|
||||
/*
|
||||
observe(viewModel.episodes) { episodeList ->
|
||||
lateFixDownloadButton(episodeList.size <= 1) // movies can have multible parts but still be *movies* this will fix this
|
||||
var isSeriesVisible = false
|
||||
|
@ -1666,7 +1557,7 @@ class ResultFragment : ResultTrailerPlayer() {
|
|||
startAction = null
|
||||
startValue = null
|
||||
}
|
||||
|
||||
*/
|
||||
observe(viewModel.episodes) { episodes ->
|
||||
when (episodes) {
|
||||
is Resource.Failure -> {
|
||||
|
@ -1689,25 +1580,91 @@ class ResultFragment : ResultTrailerPlayer() {
|
|||
}
|
||||
}
|
||||
|
||||
observe(viewModel.dubStatus) { status ->
|
||||
result_dub_select?.apply {
|
||||
isVisible = status != null
|
||||
status?.toString()?.let {
|
||||
text = it
|
||||
observe(viewModel.selectedSeason) { text ->
|
||||
result_season_button?.setText(text)
|
||||
|
||||
// If the season button is visible the result season button will be next focus down
|
||||
if (result_season_button?.isVisible == true)
|
||||
if (result_series_parent?.isVisible == true)
|
||||
setFocusUpAndDown(result_resume_series_button, result_season_button)
|
||||
else
|
||||
setFocusUpAndDown(result_bookmark_button, result_season_button)
|
||||
}
|
||||
|
||||
observe(viewModel.selectedDubStatus) { status ->
|
||||
result_dub_select?.setText(status)
|
||||
|
||||
if (result_dub_select?.isVisible == true)
|
||||
if (result_season_button?.isVisible != true && result_episode_select?.isVisible != true) {
|
||||
if (result_series_parent?.isVisible == true)
|
||||
setFocusUpAndDown(result_resume_series_button, result_dub_select)
|
||||
else
|
||||
setFocusUpAndDown(result_bookmark_button, result_dub_select)
|
||||
}
|
||||
}
|
||||
|
||||
observe(viewModel.selectedRange) { range ->
|
||||
result_episode_select.setText(range)
|
||||
|
||||
// If Season button is invisible then the bookmark button next focus is episode select
|
||||
if (result_episode_select?.isVisible == true)
|
||||
if (result_season_button?.isVisible != true) {
|
||||
if (result_series_parent?.isVisible == true)
|
||||
setFocusUpAndDown(result_resume_series_button, result_episode_select)
|
||||
else
|
||||
setFocusUpAndDown(result_bookmark_button, result_episode_select)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// val preferDub = context?.getApiDubstatusSettings()?.all { it == DubStatus.Dubbed } == true
|
||||
|
||||
observe(viewModel.dubSubSelections) { range ->
|
||||
result_dub_select?.visibility = if (range.size <= 1) GONE else VISIBLE
|
||||
result_dub_select.setOnClickListener { view ->
|
||||
view?.context?.let { ctx ->
|
||||
view.popupMenuNoIconsAndNoStringRes(range
|
||||
.mapNotNull { (text, status) ->
|
||||
Pair(
|
||||
status.ordinal,
|
||||
text?.asStringNull(ctx) ?: return@mapNotNull null
|
||||
)
|
||||
}) {
|
||||
viewModel.changeDubStatus(DubStatus.values()[itemId])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (result_season_button?.isVisible != true && result_episode_select?.isVisible != true) {
|
||||
if (result_series_parent?.isVisible == true)
|
||||
setFocusUpAndDown(result_resume_series_button, result_dub_select)
|
||||
else
|
||||
setFocusUpAndDown(result_bookmark_button, result_dub_select)
|
||||
observe(viewModel.rangeSelections) { range ->
|
||||
result_episode_select.setOnClickListener { view ->
|
||||
view?.context?.let { ctx ->
|
||||
val names = range
|
||||
.mapNotNull { (text, r) ->
|
||||
r to (text?.asStringNull(ctx) ?: return@mapNotNull null)
|
||||
}
|
||||
|
||||
view.popupMenuNoIconsAndNoStringRes(names.mapIndexed { index, (_, name) ->
|
||||
index to name
|
||||
}) {
|
||||
viewModel.changeRange(names[itemId].first)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
observe(viewModel.seasonSelections) { seasonList ->
|
||||
result_season_button?.setOnClickListener { view ->
|
||||
view?.context?.let { ctx ->
|
||||
val names = seasonList
|
||||
.mapNotNull { (text, r) ->
|
||||
r to (text?.asStringNull(ctx) ?: return@mapNotNull null)
|
||||
}
|
||||
|
||||
view.popupMenuNoIconsAndNoStringRes(names.mapIndexed { index, (_, name) ->
|
||||
index to name
|
||||
}) {
|
||||
viewModel.changeSeason(names[itemId].first)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1716,48 +1673,20 @@ class ResultFragment : ResultTrailerPlayer() {
|
|||
if (hasFocus) result_bookmark_button?.requestFocus()
|
||||
}
|
||||
|
||||
result_dub_select.setOnClickListener {
|
||||
val ranges = dubRange
|
||||
if (ranges != null) {
|
||||
it.popupMenuNoIconsAndNoStringRes(ranges
|
||||
.map { status ->
|
||||
Pair(
|
||||
status.ordinal,
|
||||
status.toString()
|
||||
)
|
||||
}
|
||||
.toList()) {
|
||||
viewModel.changeDubStatus(DubStatus.values()[itemId])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result_episode_select?.setOnClickListener {
|
||||
val ranges = episodeRanges
|
||||
if (ranges != null) {
|
||||
it.popupMenuNoIconsAndNoStringRes(ranges.mapIndexed { index, s -> Pair(index, s) }
|
||||
.toList()) {
|
||||
viewModel.changeRange(itemId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result_sync_set_score?.setOnClickListener {
|
||||
syncModel.publishUserData()
|
||||
}
|
||||
|
||||
observe(viewModel.episodesCount) { count ->
|
||||
if (count < 0) {
|
||||
result_episodes_text?.isVisible = false
|
||||
} else {
|
||||
// result_episodes_text?.isVisible = true
|
||||
result_episodes_text?.text =
|
||||
"$count ${if (count == 1) getString(R.string.episode) else getString(R.string.episodes)}"
|
||||
}
|
||||
observe(viewModel.episodesCountText) { count ->
|
||||
result_episodes_text.setText(count)
|
||||
}
|
||||
|
||||
observe(viewModel.id) {
|
||||
currentId = it
|
||||
observe(viewModel.trailers) { trailers ->
|
||||
setTrailers(trailers.flatMap { it.mirros }) // I dont care about subtitles yet!
|
||||
}
|
||||
|
||||
observe(viewModel.recommendations) { recommendations ->
|
||||
setRecommendations(recommendations, null)
|
||||
}
|
||||
|
||||
observe(viewModel.page) { data ->
|
||||
|
@ -1778,17 +1707,11 @@ class ResultFragment : ResultTrailerPlayer() {
|
|||
result_meta_rating.setText(d.ratingText)
|
||||
result_description.setTextHtml(d.plotText)
|
||||
result_cast_text.setText(d.actorsText)
|
||||
setRecommendations.setText(d.nextAiringEpisode)
|
||||
result_next_airing.setText(d.nextAiringEpisode)
|
||||
result_next_airing_time.setText(d.nextAiringDate)
|
||||
|
||||
result_poster.setImage(d.posterImage)
|
||||
|
||||
if(!d.posterUrl.isNullOrBlank()) {
|
||||
result_poster?.setImage(d.posterUrl, d.posterHeaders)
|
||||
} else {
|
||||
result_poster?.setImageResource(R.drawable.default_cover)
|
||||
}
|
||||
|
||||
|
||||
result_cast_items?.isVisible = d.actors != null
|
||||
(result_cast_items?.adapter as ActorAdaptor?)?.apply {
|
||||
|
@ -1821,11 +1744,6 @@ class ResultFragment : ResultTrailerPlayer() {
|
|||
}
|
||||
}
|
||||
|
||||
setRecommendations(d.recommendations, null)
|
||||
setActors(d.actors)
|
||||
setNextEpisode(if (d is EpisodeResponse) d.nextAiring else null)
|
||||
setTrailers(d.trailers.flatMap { it.mirros }) // I dont care about subtitles yet!
|
||||
|
||||
if (syncModel.addSyncs(d.syncData)) {
|
||||
syncModel.updateMetaAndUser()
|
||||
syncModel.updateSynced()
|
||||
|
@ -1833,70 +1751,20 @@ class ResultFragment : ResultTrailerPlayer() {
|
|||
syncModel.addFromUrl(d.url)
|
||||
}
|
||||
|
||||
result_meta_site?.text = d.apiName
|
||||
val posterImageLink = d.posterUrl
|
||||
if (!posterImageLink.isNullOrEmpty()) {
|
||||
result_play_movie.setText(d.playMovieText)
|
||||
|
||||
//result_poster_blur?.setImageBlur(posterImageLink, 10, 3, d.posterHeaders)
|
||||
//Full screen view of Poster image
|
||||
if (context?.isTrueTvSettings() == false) // Poster not clickable on tv
|
||||
result_poster_holder?.setOnClickListener {
|
||||
try {
|
||||
context?.let { ctx ->
|
||||
runBlocking {
|
||||
val sourceBuilder = AlertDialog.Builder(ctx)
|
||||
sourceBuilder.setView(R.layout.result_poster)
|
||||
|
||||
val sourceDialog = sourceBuilder.create()
|
||||
sourceDialog.show()
|
||||
|
||||
sourceDialog.findViewById<ImageView?>(R.id.imgPoster)
|
||||
?.apply {
|
||||
setImage(posterImageLink)
|
||||
setOnClickListener {
|
||||
sourceDialog.dismissSafe()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
logError(e)
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
result_poster?.setImageResource(R.drawable.default_cover)
|
||||
//result_poster_blur?.setImageResource(R.drawable.default_cover)
|
||||
}
|
||||
|
||||
result_poster_holder?.visibility = VISIBLE
|
||||
|
||||
result_play_movie?.text =
|
||||
if (d.typeText == TvType.Live) getString(R.string.play_livestream_button) else getString(
|
||||
R.string.play_movie_button
|
||||
)
|
||||
//result_plot_header?.text =
|
||||
// if (d.type == TvType.Torrent) getString(R.string.torrent_plot) else getString(R.string.result_plot)
|
||||
val syno = d.plot
|
||||
if (!syno.isNullOrEmpty()) {
|
||||
result_description?.setOnClickListener {
|
||||
result_description?.setOnClickListener { view ->
|
||||
view.context?.let { ctx ->
|
||||
val builder: AlertDialog.Builder =
|
||||
AlertDialog.Builder(requireContext(), R.style.AlertDialogCustom)
|
||||
builder.setMessage(syno.html())
|
||||
.setTitle(if (d.typeText == TvType.Torrent) R.string.torrent_plot else R.string.result_plot)
|
||||
AlertDialog.Builder(ctx, R.style.AlertDialogCustom)
|
||||
builder.setMessage(d.plotText.asString(ctx).html())
|
||||
.setTitle(d.plotHeaderText.asString(ctx))
|
||||
.show()
|
||||
}
|
||||
result_description?.text = syno.html()
|
||||
} else {
|
||||
result_description?.text =
|
||||
if (d.typeText == TvType.Torrent) getString(R.string.torrent_no_plot) else getString(
|
||||
R.string.normal_no_plot
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
result_tag?.removeAllViews()
|
||||
//result_tag_holder?.visibility = GONE
|
||||
// result_status.visibility = GONE
|
||||
|
||||
d.comingSoon.let { soon ->
|
||||
result_coming_soon?.isVisible = soon
|
||||
|
@ -1905,7 +1773,7 @@ class ResultFragment : ResultTrailerPlayer() {
|
|||
|
||||
val tags = d.tags
|
||||
result_tag_holder?.isVisible = tags.isNotEmpty()
|
||||
if (tags.isNotEmpty()) {
|
||||
if (tags.isNotEmpty()) {
|
||||
//result_tag_holder?.visibility = VISIBLE
|
||||
val isOnTv = context?.isTrueTvSettings() == true
|
||||
for ((index, tag) in tags.withIndex()) {
|
||||
|
@ -1918,6 +1786,8 @@ class ResultFragment : ResultTrailerPlayer() {
|
|||
}
|
||||
}
|
||||
|
||||
//TODO FIX
|
||||
/*
|
||||
if (d.typeText.isMovieType()) {
|
||||
val hasDownloadSupport = api.hasDownloadSupport
|
||||
lateFixDownloadButton(true)
|
||||
|
@ -1942,11 +1812,6 @@ class ResultFragment : ResultTrailerPlayer() {
|
|||
return@setOnLongClickListener true
|
||||
}
|
||||
|
||||
// result_options.setOnClickListener {
|
||||
// val card = currentEpisodes?.first() ?: return@setOnClickListener
|
||||
// handleAction(EpisodeClickEvent(ACTION_SHOW_OPTIONS, card))
|
||||
// }
|
||||
|
||||
result_movie_progress_downloaded_holder?.isVisible = hasDownloadSupport
|
||||
if (hasDownloadSupport) {
|
||||
val localId = d.getId()
|
||||
|
@ -2045,19 +1910,7 @@ class ResultFragment : ResultTrailerPlayer() {
|
|||
} else {
|
||||
lateFixDownloadButton(false)
|
||||
}
|
||||
|
||||
|
||||
when (d) {
|
||||
is AnimeLoadResponse -> {
|
||||
|
||||
// val preferEnglish = true
|
||||
//val titleName = (if (preferEnglish) d.engName else d.japName) ?: d.name
|
||||
val titleName = d.name
|
||||
result_title.text = titleName
|
||||
//result_toolbar.title = titleName
|
||||
}
|
||||
else -> result_title.text = d.name
|
||||
}
|
||||
*/
|
||||
}
|
||||
is Resource.Failure -> {
|
||||
result_error_text.text = url?.plus("\n") + data.errorString
|
||||
|
@ -2091,7 +1944,7 @@ class ResultFragment : ResultTrailerPlayer() {
|
|||
val tempUrl = url
|
||||
if (tempUrl != null) {
|
||||
result_reload_connectionerror.setOnClickListener {
|
||||
viewModel.load(tempUrl, apiName, showFillers)
|
||||
viewModel.load(tempUrl, apiName, showFillers, DubStatus.Dubbed, 0, 0) //TODO FIX
|
||||
}
|
||||
|
||||
result_reload_connection_open_in_browser?.setOnClickListener {
|
||||
|
@ -2124,9 +1977,9 @@ class ResultFragment : ResultTrailerPlayer() {
|
|||
result_meta_site?.isFocusable = false
|
||||
}
|
||||
|
||||
if (restart || viewModel.result.value == null) {
|
||||
if (restart || !viewModel.hasLoaded()) {
|
||||
//viewModel.clear()
|
||||
viewModel.load(tempUrl, apiName, showFillers)
|
||||
viewModel.load(tempUrl, apiName, showFillers, DubStatus.Dubbed, 0, 0) //TODO FIX
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.lagradost.cloudstream3.ui.result
|
||||
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
|
@ -7,16 +8,18 @@ import androidx.lifecycle.viewModelScope
|
|||
import com.lagradost.cloudstream3.*
|
||||
import com.lagradost.cloudstream3.APIHolder.getId
|
||||
import com.lagradost.cloudstream3.APIHolder.unixTime
|
||||
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
|
||||
import com.lagradost.cloudstream3.LoadResponse.Companion.getAniListId
|
||||
import com.lagradost.cloudstream3.LoadResponse.Companion.getMalId
|
||||
import com.lagradost.cloudstream3.animeproviders.GogoanimeProvider
|
||||
import com.lagradost.cloudstream3.animeproviders.NineAnimeProvider
|
||||
import com.lagradost.cloudstream3.metaproviders.SyncRedirector
|
||||
import com.lagradost.cloudstream3.mvvm.*
|
||||
import com.lagradost.cloudstream3.syncproviders.SyncAPI
|
||||
import com.lagradost.cloudstream3.syncproviders.providers.Kitsu
|
||||
import com.lagradost.cloudstream3.ui.APIRepository
|
||||
import com.lagradost.cloudstream3.ui.player.IGenerator
|
||||
import com.lagradost.cloudstream3.utils.DOWNLOAD_HEADER_CACHE
|
||||
import com.lagradost.cloudstream3.utils.DataStoreHelper
|
||||
import com.lagradost.cloudstream3.utils.FillerEpisodeCheck
|
||||
import com.lagradost.cloudstream3.utils.VideoDownloadHelper
|
||||
import com.lagradost.cloudstream3.utils.*
|
||||
import kotlinx.coroutines.launch
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
|
@ -55,8 +58,20 @@ data class ResultData(
|
|||
val yearText: UiText?,
|
||||
val nextAiringDate: UiText?,
|
||||
val nextAiringEpisode: UiText?,
|
||||
val playMovieText: UiText?,
|
||||
val plotHeaderText: UiText,
|
||||
)
|
||||
|
||||
fun txt(status: DubStatus?): UiText? {
|
||||
return txt(
|
||||
when (status) {
|
||||
DubStatus.Dubbed -> R.string.app_dubbed_text
|
||||
DubStatus.Subbed -> R.string.app_subbed_text
|
||||
else -> null
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fun LoadResponse.toResultData(repo: APIRepository): ResultData {
|
||||
debugAssert({ repo.name == apiName }) {
|
||||
"Api returned wrong apiName"
|
||||
|
@ -101,6 +116,20 @@ fun LoadResponse.toResultData(repo: APIRepository): ResultData {
|
|||
}
|
||||
|
||||
return ResultData(
|
||||
plotHeaderText = txt(
|
||||
when (this.type) {
|
||||
TvType.Torrent -> R.string.torrent_plot
|
||||
else -> R.string.result_plot
|
||||
}
|
||||
),
|
||||
playMovieText = txt(
|
||||
when (this.type) {
|
||||
TvType.Live -> R.string.play_livestream_button
|
||||
TvType.Torrent -> R.string.play_torrent_button
|
||||
TvType.Movie, TvType.AnimeMovie -> R.string.play_movie_button
|
||||
else -> null
|
||||
}
|
||||
),
|
||||
nextAiringDate = nextAiringDate,
|
||||
nextAiringEpisode = nextAiringEpisode,
|
||||
posterImage = img(
|
||||
|
@ -174,6 +203,8 @@ class ResultViewModel2 : ViewModel() {
|
|||
/** map<dub, map<season, List<episode>>> */
|
||||
private var currentEpisodes: Map<EpisodeIndexer, List<ResultEpisode>> = mapOf()
|
||||
private var currentRanges: Map<EpisodeIndexer, List<EpisodeRange>> = mapOf()
|
||||
private var currentMeta: SyncAPI.SyncResult? = null
|
||||
private var currentSync: Map<String, String>? = null
|
||||
private var currentIndex: EpisodeIndexer? = null
|
||||
private var currentRange: EpisodeRange? = null
|
||||
private var currentShowFillers: Boolean = false
|
||||
|
@ -193,20 +224,42 @@ class ResultViewModel2 : ViewModel() {
|
|||
MutableLiveData(Resource.Loading())
|
||||
val episodes: LiveData<Resource<List<ResultEpisode>>> = _episodes
|
||||
|
||||
private val _episodesCount: MutableLiveData<Int> =
|
||||
MutableLiveData(0)
|
||||
val episodesCount: LiveData<Int> = _episodesCount
|
||||
private val _episodesCountText: MutableLiveData<UiText?> =
|
||||
MutableLiveData(null)
|
||||
val episodesCountText: LiveData<UiText?> = _episodesCountText
|
||||
|
||||
private val _trailers: MutableLiveData<List<TrailerData>> = MutableLiveData(mutableListOf())
|
||||
val trailers: LiveData<List<TrailerData>> = _trailers
|
||||
|
||||
private val _dubStatus: MutableLiveData<DubStatus?> = MutableLiveData(null)
|
||||
val dubStatus: LiveData<DubStatus?> = _dubStatus
|
||||
|
||||
private val _dubSubSelections: MutableLiveData<List<DubStatus>> = MutableLiveData(emptyList())
|
||||
val dubSubSelections: LiveData<List<DubStatus>> = _dubSubSelections
|
||||
private val _dubSubSelections: MutableLiveData<List<Pair<UiText?, DubStatus>>> =
|
||||
MutableLiveData(emptyList())
|
||||
val dubSubSelections: LiveData<List<Pair<UiText?, DubStatus>>> = _dubSubSelections
|
||||
|
||||
private val _rangeSelections: MutableLiveData<List<Pair<UiText?, EpisodeRange>>> = MutableLiveData(emptyList())
|
||||
val rangeSelections: LiveData<List<Pair<UiText?, EpisodeRange>>> = _rangeSelections
|
||||
|
||||
private val _seasonSelections: MutableLiveData<List<Pair<UiText?, Int>>> = MutableLiveData(emptyList())
|
||||
val seasonSelections: LiveData<List<Pair<UiText?, Int>>> = _seasonSelections
|
||||
|
||||
|
||||
private val _recommendations: MutableLiveData<List<SearchResponse>> =
|
||||
MutableLiveData(emptyList())
|
||||
val recommendations: LiveData<List<SearchResponse>> = _recommendations
|
||||
|
||||
private val _selectedRange: MutableLiveData<UiText?> =
|
||||
MutableLiveData(null)
|
||||
val selectedRange: LiveData<UiText?> = _selectedRange
|
||||
|
||||
private val _selectedSeason: MutableLiveData<UiText?> =
|
||||
MutableLiveData(null)
|
||||
val selectedSeason: LiveData<UiText?> = _selectedSeason
|
||||
|
||||
private val _selectedDubStatus: MutableLiveData<UiText?> = MutableLiveData(null)
|
||||
val selectedDubStatus: LiveData<UiText?> = _selectedDubStatus
|
||||
|
||||
companion object {
|
||||
const val TAG = "RVM2"
|
||||
private const val EPISODE_RANGE_SIZE = 50
|
||||
private const val EPISODE_RANGE_OVERLOAD = 60
|
||||
|
||||
|
@ -324,6 +377,102 @@ class ResultViewModel2 : ViewModel() {
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun applyMeta(
|
||||
resp: LoadResponse,
|
||||
meta: SyncAPI.SyncResult?,
|
||||
syncs: Map<String, String>? = null
|
||||
): Pair<LoadResponse, Boolean> {
|
||||
if (meta == null) return resp to false
|
||||
var updateEpisodes = false
|
||||
val out = resp.apply {
|
||||
Log.i(ResultViewModel.TAG, "applyMeta")
|
||||
|
||||
duration = duration ?: meta.duration
|
||||
rating = rating ?: meta.publicScore
|
||||
tags = tags ?: meta.genres
|
||||
plot = if (plot.isNullOrBlank()) meta.synopsis else plot
|
||||
posterUrl = posterUrl ?: meta.posterUrl ?: meta.backgroundPosterUrl
|
||||
actors = actors ?: meta.actors
|
||||
|
||||
if (this is EpisodeResponse) {
|
||||
nextAiring = nextAiring ?: meta.nextAiring
|
||||
}
|
||||
|
||||
for ((k, v) in syncs ?: emptyMap()) {
|
||||
syncData[k] = v
|
||||
}
|
||||
|
||||
val realRecommendations = ArrayList<SearchResponse>()
|
||||
val apiNames = listOf(GogoanimeProvider().name, NineAnimeProvider().name)
|
||||
meta.recommendations?.forEach { rec ->
|
||||
apiNames.forEach { name ->
|
||||
realRecommendations.add(rec.copy(apiName = name))
|
||||
}
|
||||
}
|
||||
|
||||
recommendations = recommendations?.union(realRecommendations)?.toList()
|
||||
?: realRecommendations
|
||||
|
||||
argamap({
|
||||
addTrailer(meta.trailers)
|
||||
}, {
|
||||
if (this !is AnimeLoadResponse) return@argamap
|
||||
val map =
|
||||
Kitsu.getEpisodesDetails(getMalId(), getAniListId(), isResponseRequired = false)
|
||||
if (map.isNullOrEmpty()) return@argamap
|
||||
updateEpisodes = DubStatus.values().map { dubStatus ->
|
||||
val current =
|
||||
this.episodes[dubStatus]?.mapIndexed { index, episode ->
|
||||
episode.apply {
|
||||
this.episode = this.episode ?: (index + 1)
|
||||
}
|
||||
}?.sortedBy { it.episode ?: 0 }?.toMutableList()
|
||||
if (current.isNullOrEmpty()) return@map false
|
||||
val episodeNumbers = current.map { ep -> ep.episode!! }
|
||||
var updateCount = 0
|
||||
map.forEach { (episode, node) ->
|
||||
episodeNumbers.binarySearch(episode).let { index ->
|
||||
current.getOrNull(index)?.let { currentEp ->
|
||||
current[index] = currentEp.apply {
|
||||
updateCount++
|
||||
val currentBack = this
|
||||
this.description = this.description ?: node.description?.en
|
||||
this.name = this.name ?: node.titles?.canonical
|
||||
this.episode = this.episode ?: node.num ?: episodeNumbers[index]
|
||||
this.posterUrl = this.posterUrl ?: node.thumbnail?.original?.url
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this.episodes[dubStatus] = current
|
||||
updateCount > 0
|
||||
}.any { it }
|
||||
})
|
||||
}
|
||||
return out to updateEpisodes
|
||||
}
|
||||
|
||||
fun setMeta(meta: SyncAPI.SyncResult, syncs: Map<String, String>?) =
|
||||
viewModelScope.launch {
|
||||
Log.i(TAG, "setMeta")
|
||||
currentMeta = meta
|
||||
currentSync = syncs
|
||||
val (value, updateEpisodes) = Coroutines.ioWork {
|
||||
currentResponse?.let { resp ->
|
||||
return@ioWork applyMeta(resp, meta, syncs)
|
||||
}
|
||||
return@ioWork null to null
|
||||
}
|
||||
|
||||
postSuccessful(
|
||||
value ?: return@launch,
|
||||
currentRepo ?: return@launch,
|
||||
updateEpisodes ?: return@launch,
|
||||
false
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
private suspend fun updateFillers(name: String) {
|
||||
fillers =
|
||||
try {
|
||||
|
@ -343,6 +492,10 @@ class ResultViewModel2 : ViewModel() {
|
|||
postEpisodeRange(currentIndex, range)
|
||||
}
|
||||
|
||||
fun changeSeason(season: Int) {
|
||||
postEpisodeRange(currentIndex?.copy(season = season), currentRange)
|
||||
}
|
||||
|
||||
private fun getEpisodes(indexer: EpisodeIndexer, range: EpisodeRange): List<ResultEpisode> {
|
||||
//TODO ADD GENERATOR
|
||||
|
||||
|
@ -377,9 +530,34 @@ class ResultViewModel2 : ViewModel() {
|
|||
return
|
||||
}
|
||||
|
||||
val size = currentEpisodes[indexer]?.size
|
||||
|
||||
_episodesCountText.postValue(
|
||||
txt(
|
||||
R.string.episode_format,
|
||||
if (size == 1) R.string.episode else R.string.episodes,
|
||||
size
|
||||
)
|
||||
)
|
||||
|
||||
currentIndex = indexer
|
||||
currentRange = range
|
||||
|
||||
_selectedSeason.postValue(
|
||||
when (indexer.season) {
|
||||
0 -> txt(R.string.no_season)
|
||||
else -> txt(R.string.season_format, R.string.season, indexer.season) //TODO FIX
|
||||
}
|
||||
)
|
||||
_selectedRange.postValue(
|
||||
if ((currentRanges[indexer]?.size ?: 0) > 1) {
|
||||
txt(R.string.episodes_range, range.startEpisode, range.endEpisode)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
)
|
||||
_selectedDubStatus.postValue(txt(indexer.dubStatus))
|
||||
|
||||
//TODO SET KEYS
|
||||
preferStartEpisode = range.startEpisode
|
||||
preferStartSeason = indexer.season
|
||||
|
@ -587,10 +765,13 @@ class ResultViewModel2 : ViewModel() {
|
|||
|
||||
// this instantly updates the metadata on the page
|
||||
private fun postPage(loadResponse: LoadResponse, apiRepository: APIRepository) {
|
||||
_recommendations.postValue(loadResponse.recommendations ?: emptyList())
|
||||
_page.postValue(Resource.Success(loadResponse.toResultData(apiRepository)))
|
||||
_trailers.postValue(loadResponse.trailers)
|
||||
}
|
||||
|
||||
fun hasLoaded() = currentResponse != null
|
||||
|
||||
fun load(
|
||||
url: String,
|
||||
apiName: String,
|
||||
|
@ -647,7 +828,9 @@ class ResultViewModel2 : ViewModel() {
|
|||
_page.postValue(data)
|
||||
}
|
||||
is Resource.Success -> {
|
||||
val loadResponse = data.value
|
||||
val loadResponse = Coroutines.ioWork {
|
||||
applyMeta(data.value, currentMeta, currentSync).first
|
||||
}
|
||||
val mainId = loadResponse.getId()
|
||||
|
||||
AcraApplication.setKey(
|
||||
|
|
|
@ -7,6 +7,7 @@ import androidx.annotation.DrawableRes
|
|||
import androidx.annotation.StringRes
|
||||
import androidx.core.view.isGone
|
||||
import androidx.core.view.isVisible
|
||||
import com.lagradost.cloudstream3.mvvm.logError
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.html
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.setImage
|
||||
|
||||
|
@ -18,7 +19,12 @@ sealed class UiText {
|
|||
) : UiText()
|
||||
|
||||
fun asStringNull(context: Context?): String? {
|
||||
return asString(context ?: return null)
|
||||
try {
|
||||
return asString(context ?: return null)
|
||||
} catch (e: Exception) {
|
||||
logError(e)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
fun asString(context: Context): String {
|
||||
|
|
|
@ -290,15 +290,15 @@
|
|||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:id="@+id/result_poster_holder"
|
||||
android:layout_width="100dp"
|
||||
android:layout_height="140dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:cardCornerRadius="@dimen/rounded_image_radius">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/result_poster"
|
||||
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="100dp"
|
||||
android:layout_height="140dp"
|
||||
android:contentDescription="@string/result_poster_img_des"
|
||||
android:foreground="@drawable/outline_drawable"
|
||||
android:scaleType="centerCrop"
|
||||
|
|
|
@ -286,9 +286,12 @@
|
|||
</string>
|
||||
|
||||
<string name="season">Season</string>
|
||||
<string name="season_format">%s %d</string>
|
||||
<string name="no_season">No Season</string>
|
||||
<string name="episode">Episode</string>
|
||||
<string name="episodes">Episodes</string>
|
||||
<string name="episodes_range">%d-%d</string>
|
||||
<string name="episode_format" formatted="true">%d %s</string>
|
||||
<string name="season_short">S</string>
|
||||
<string name="episode_short">E</string>
|
||||
<string name="no_episodes_found">No Episodes found</string>
|
||||
|
|
Loading…
Reference in a new issue