Added Mark as watched and fixed clicking episode synopsis

This commit is contained in:
Blatzar 2023-01-20 23:26:46 +01:00
parent 89c5cb8a46
commit b8248d1053
5 changed files with 100 additions and 8 deletions

View File

@ -218,10 +218,18 @@ class EpisodeAdapter(
name//if(card.isFiller == true) episodeText.context.getString(R.string.filler).format(name) else name
episodeText.isSelected = true // is needed for text repeating
val displayPos = card.getDisplayPosition()
episodeProgress?.max = (card.duration / 1000).toInt()
episodeProgress?.progress = (displayPos / 1000).toInt()
episodeProgress?.isVisible = displayPos > 0L
if (card.videoWatchState == VideoWatchState.Watched) {
// This cannot be done in getDisplayPosition() as when you have not watched something
// the duration and position is 0
episodeProgress?.max = 1
episodeProgress?.progress = 1
episodeProgress?.isVisible = true
} else {
val displayPos = card.getDisplayPosition()
episodeProgress?.max = (card.duration / 1000).toInt()
episodeProgress?.progress = (displayPos / 1000).toInt()
episodeProgress?.isVisible = displayPos > 0L
}
episodePoster?.isVisible = episodePoster?.setImage(card.poster) == true

View File

@ -49,6 +49,7 @@ import com.lagradost.cloudstream3.utils.AppUtils.loadCache
import com.lagradost.cloudstream3.utils.AppUtils.openBrowser
import com.lagradost.cloudstream3.utils.Coroutines.ioWorkSafe
import com.lagradost.cloudstream3.utils.Coroutines.main
import com.lagradost.cloudstream3.utils.DataStoreHelper.getVideoWatchState
import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute
@ -106,6 +107,15 @@ import kotlinx.coroutines.runBlocking
const val START_ACTION_RESUME_LATEST = 1
const val START_ACTION_LOAD_EP = 2
/**
* Future proofed way to mark episodes as watched
**/
enum class VideoWatchState {
/** Default value when no key is set */
None,
Watched
}
data class ResultEpisode(
val headerName: String,
val name: String?,
@ -124,6 +134,10 @@ data class ResultEpisode(
val isFiller: Boolean?,
val tvType: TvType,
val parentId: Int,
/**
* Conveys if the episode itself is marked as watched
**/
val videoWatchState: VideoWatchState
)
fun ResultEpisode.getRealPosition(): Long {
@ -160,6 +174,7 @@ fun buildResultEpisode(
parentId: Int,
): ResultEpisode {
val posDur = getViewPos(id)
val videoWatchState = getVideoWatchState(id) ?: VideoWatchState.None
return ResultEpisode(
headerName,
name,
@ -178,6 +193,7 @@ fun buildResultEpisode(
isFiller,
tvType,
parentId,
videoWatchState
)
}
@ -559,6 +575,19 @@ open class ResultFragment : ResultTrailerPlayer() {
)
observe(viewModel.episodeSynopsis) { description ->
view.context?.let { ctx ->
val builder: AlertDialog.Builder =
AlertDialog.Builder(ctx, R.style.AlertDialogCustom)
builder.setMessage(description.html())
.setTitle(R.string.synopsis)
.setOnDismissListener {
viewModel.releaseEpisodeSynopsis()
}
.show()
}
}
observe(viewModel.watchStatus) { watchType ->
result_bookmark_button?.text = getString(watchType.stringRes)
result_bookmark_fab?.text = getString(watchType.stringRes)

View File

@ -406,6 +406,9 @@ class ResultViewModel2 : ViewModel() {
MutableLiveData(Some.None)
val resumeWatching: LiveData<Some<ResumeWatchingStatus>> = _resumeWatching
private val _episodeSynopsis: MutableLiveData<String?> = MutableLiveData(null)
val episodeSynopsis: LiveData<String?> = _episodeSynopsis
companion object {
const val TAG = "RVM2"
private const val EPISODE_RANGE_SIZE = 20
@ -1113,6 +1116,10 @@ class ResultViewModel2 : ViewModel() {
)
)
fun releaseEpisodeSynopsis() {
_episodeSynopsis.postValue(null)
}
private suspend fun handleEpisodeClickEvent(activity: Activity?, click: EpisodeClickEvent) {
when (click.action) {
ACTION_SHOW_OPTIONS -> {
@ -1146,10 +1153,20 @@ class ResultViewModel2 : ViewModel() {
txt(R.string.episode_action_download_mirror) to ACTION_DOWNLOAD_MIRROR,
txt(R.string.episode_action_download_subtitle) to ACTION_DOWNLOAD_EPISODE_SUBTITLE_MIRROR,
txt(R.string.episode_action_reload_links) to ACTION_RELOAD_EPISODE,
// txt(R.string.action_mark_as_watched) to ACTION_MARK_AS_WATCHED,
)
)
// Do not add mark as watched on movies
if (!listOf(TvType.Movie, TvType.AnimeMovie).contains(click.data.tvType)) {
val isWatched =
DataStoreHelper.getVideoWatchState(click.data.id) == VideoWatchState.Watched
val watchedText = if (isWatched) R.string.action_remove_from_watched
else R.string.action_mark_as_watched
options.add(txt(watchedText) to ACTION_MARK_AS_WATCHED)
}
postPopup(
txt(
activity?.getNameFull(
@ -1182,6 +1199,10 @@ class ResultViewModel2 : ViewModel() {
}
}
}
ACTION_SHOW_DESCRIPTION -> {
_episodeSynopsis.postValue(click.data.description)
}
/* not implemented, not used
ACTION_DOWNLOAD_EPISODE_SUBTITLE -> {
loadLinks(click.data, isVisible = false, isCasting = false) { links ->
@ -1378,8 +1399,17 @@ class ResultViewModel2 : ViewModel() {
)
}
ACTION_MARK_AS_WATCHED -> {
// TODO FIX
// DataStoreHelper.setViewPos(click.data.id, 1, 1)
val isWatched =
DataStoreHelper.getVideoWatchState(click.data.id) == VideoWatchState.Watched
if (isWatched) {
DataStoreHelper.setVideoWatchState(click.data.id, VideoWatchState.None)
} else {
DataStoreHelper.setVideoWatchState(click.data.id, VideoWatchState.Watched)
}
// Kinda dirty to reload all episodes :(
reloadEpisodes()
}
}
}
@ -1529,7 +1559,13 @@ class ResultViewModel2 : ViewModel() {
val end = minOf(list.size, start + length)
list.subList(start, end).map {
val posDur = getViewPos(it.id)
it.copy(position = posDur?.position ?: 0, duration = posDur?.duration ?: 0)
val watchState =
DataStoreHelper.getVideoWatchState(it.id) ?: VideoWatchState.None
it.copy(
position = posDur?.position ?: 0,
duration = posDur?.duration ?: 0,
videoWatchState = watchState
)
}
}
?: emptyList()

View File

@ -11,8 +11,10 @@ import com.lagradost.cloudstream3.SearchQuality
import com.lagradost.cloudstream3.SearchResponse
import com.lagradost.cloudstream3.TvType
import com.lagradost.cloudstream3.ui.WatchType
import com.lagradost.cloudstream3.ui.result.VideoWatchState
const val VIDEO_POS_DUR = "video_pos_dur"
const val VIDEO_WATCH_STATE = "video_watch_state"
const val RESULT_WATCH_STATE = "result_watch_state"
const val RESULT_WATCH_STATE_DATA = "result_watch_state_data"
const val RESULT_RESUME_WATCHING = "result_resume_watching_2" // changed due to id changes
@ -193,6 +195,22 @@ object DataStoreHelper {
return getKey("$currentAccount/$VIDEO_POS_DUR", id.toString(), null)
}
fun getVideoWatchState(id: Int?): VideoWatchState? {
if (id == null) return null
return getKey("$currentAccount/$VIDEO_WATCH_STATE", id.toString(), null)
}
fun setVideoWatchState(id: Int?, watchState: VideoWatchState) {
if (id == null) return
// None == No key
if (watchState == VideoWatchState.None) {
removeKey("$currentAccount/$VIDEO_WATCH_STATE", id.toString())
} else {
setKey("$currentAccount/$VIDEO_WATCH_STATE", id.toString(), watchState)
}
}
fun getDub(id: Int): DubStatus? {
return DubStatus.values()
.getOrNull(getKey("$currentAccount/$RESULT_DUB", id.toString(), -1) ?: -1)

View File

@ -605,6 +605,7 @@
<string name="enable_skip_op_from_database_des">Show skip popups for opening/ending</string>
<string name="clipboard_too_large">Too much text. Unable to save to clipboard.</string>
<string name="action_mark_as_watched">Mark as watched</string>
<string name="action_remove_from_watched">Remove from watched</string>
<string name="confirm_exit_dialog">Are you sure you want to exit\?</string>
<string name="yes">Yes</string>
<string name="no">No</string>