mirror of
https://github.com/libre-tube/LibreTube.git
synced 2025-04-28 07:50:31 +05:30
refactor: improve watch position handling
This commit is contained in:
parent
6a8de0dc6d
commit
f764ef4fc3
@ -37,7 +37,7 @@ object DatabaseHelper {
|
||||
val watchHistoryDao = Database.watchHistoryDao()
|
||||
val historySize = watchHistoryDao.getSize()
|
||||
|
||||
if (historySize < pageSize * (page-1)) return emptyList()
|
||||
if (historySize < pageSize * (page - 1)) return emptyList()
|
||||
|
||||
val offset = historySize - (pageSize * page)
|
||||
val limit = if (offset < 0) {
|
||||
@ -58,16 +58,32 @@ object DatabaseHelper {
|
||||
|
||||
while (searchHistory.size > MAX_SEARCH_HISTORY_SIZE) {
|
||||
Database.searchHistoryDao().delete(searchHistory.first())
|
||||
searchHistory.removeFirst()
|
||||
searchHistory.removeAt(0)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun isVideoWatched(videoId: String, duration: Long): Boolean = withContext(Dispatchers.IO) {
|
||||
val historyItem = Database.watchPositionDao()
|
||||
.findById(videoId) ?: return@withContext false
|
||||
val progress = historyItem.position / 1000
|
||||
suspend fun getWatchPosition(videoId: String) = Database.watchPositionDao().findById(videoId)?.position
|
||||
|
||||
fun getWatchPositionBlocking(videoId: String): Long? = runBlocking(Dispatchers.IO) {
|
||||
getWatchPosition(videoId)
|
||||
}
|
||||
|
||||
fun isVideoWatchedBlocking(videoId: String, duration: Long) =
|
||||
runBlocking { isVideoWatched(videoId, duration) }
|
||||
|
||||
suspend fun isVideoWatched(videoId: String, duration: Long): Boolean =
|
||||
withContext(Dispatchers.IO) {
|
||||
val position = getWatchPosition(videoId) ?: return@withContext false
|
||||
|
||||
return@withContext isVideoWatched(position, duration)
|
||||
}
|
||||
|
||||
fun isVideoWatched(positionMillis: Long, durationSeconds: Long?): Boolean {
|
||||
if (durationSeconds == null) return false
|
||||
|
||||
val progress = positionMillis / 1000
|
||||
// show video only in feed when watched less than 90%
|
||||
return@withContext progress > 0.9f * duration
|
||||
return progress > 0.9f * durationSeconds
|
||||
}
|
||||
|
||||
suspend fun filterUnwatched(streams: List<StreamItem>): List<StreamItem> {
|
||||
@ -85,7 +101,10 @@ object DatabaseHelper {
|
||||
}
|
||||
}
|
||||
|
||||
fun filterByStatusAndWatchPosition(streams: List<StreamItem>, hideWatched: Boolean): List<StreamItem> {
|
||||
fun filterByStatusAndWatchPosition(
|
||||
streams: List<StreamItem>,
|
||||
hideWatched: Boolean
|
||||
): List<StreamItem> {
|
||||
val streamItems = streams.filter {
|
||||
val isVideo = !it.isShort && !it.isLive
|
||||
|
||||
|
@ -49,7 +49,6 @@ import com.github.libretube.util.TextUtils
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import java.util.Locale
|
||||
import java.util.concurrent.Executors
|
||||
import kotlin.math.absoluteValue
|
||||
@ -666,20 +665,6 @@ object PlayerHelper {
|
||||
}
|
||||
}
|
||||
|
||||
fun getStoredWatchPosition(videoId: String, duration: Long?): Long? {
|
||||
if (duration == null) return null
|
||||
|
||||
runCatching {
|
||||
val watchPosition = runBlocking {
|
||||
DatabaseHolder.Database.watchPositionDao().findById(videoId)
|
||||
}
|
||||
if (watchPosition != null && watchPosition.position < duration * 1000 * 0.9) {
|
||||
return watchPosition.position
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the track type string resource corresponding to ExoPlayer role flags used for audio
|
||||
* track types.
|
||||
|
@ -230,6 +230,9 @@ abstract class AbstractPlayerService : MediaLibraryService(), MediaLibrarySessio
|
||||
abstract val isOfflinePlayer: Boolean
|
||||
abstract val isAudioOnlyPlayer: Boolean
|
||||
|
||||
val watchPositionsEnabled get() =
|
||||
(PlayerHelper.watchPositionsAudio && isAudioOnlyPlayer) || (PlayerHelper.watchPositionsVideo && !isAudioOnlyPlayer)
|
||||
|
||||
override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaLibrarySession? =
|
||||
mediaLibrarySession
|
||||
|
||||
@ -313,9 +316,6 @@ abstract class AbstractPlayerService : MediaLibraryService(), MediaLibrarySessio
|
||||
abstract suspend fun startPlayback()
|
||||
|
||||
private fun saveWatchPosition() {
|
||||
val watchPositionsEnabled =
|
||||
(PlayerHelper.watchPositionsAudio && isAudioOnlyPlayer) || (PlayerHelper.watchPositionsVideo && !isAudioOnlyPlayer)
|
||||
|
||||
if (isTransitioning || !watchPositionsEnabled) return
|
||||
|
||||
exoPlayer?.let { PlayerHelper.saveWatchPosition(it, videoId) }
|
||||
|
@ -7,6 +7,7 @@ import androidx.media3.common.MediaItem
|
||||
import androidx.media3.common.Player
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import com.github.libretube.constants.IntentData
|
||||
import com.github.libretube.db.DatabaseHelper
|
||||
import com.github.libretube.db.DatabaseHolder.Database
|
||||
import com.github.libretube.db.obj.DownloadWithItems
|
||||
import com.github.libretube.db.obj.filterByTab
|
||||
@ -92,9 +93,9 @@ open class OfflinePlayerService : AbstractPlayerService() {
|
||||
exoPlayer?.playWhenReady = PlayerHelper.playAutomatically
|
||||
exoPlayer?.prepare()
|
||||
|
||||
if (PlayerHelper.watchPositionsAudio) {
|
||||
PlayerHelper.getStoredWatchPosition(videoId, downloadWithItems.download.duration)?.let {
|
||||
exoPlayer?.seekTo(it)
|
||||
if (watchPositionsEnabled) {
|
||||
DatabaseHelper.getWatchPosition(videoId)?.let {
|
||||
if (!DatabaseHelper.isVideoWatched(it, downloadWithItems.download.duration)) exoPlayer?.seekTo(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -163,9 +163,9 @@ open class OnlinePlayerService : AbstractPlayerService() {
|
||||
// seek to the previous position if available
|
||||
if (seekToPosition != 0L) {
|
||||
exoPlayer?.seekTo(seekToPosition)
|
||||
} else if (PlayerHelper.watchPositionsAudio) {
|
||||
PlayerHelper.getStoredWatchPosition(videoId, streams?.duration)?.let {
|
||||
exoPlayer?.seekTo(it)
|
||||
} else if (watchPositionsEnabled) {
|
||||
DatabaseHelper.getWatchPositionBlocking(videoId)?.let {
|
||||
if (!DatabaseHelper.isVideoWatched(it, streams?.duration)) exoPlayer?.seekTo(it)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@ import com.github.libretube.R
|
||||
import com.github.libretube.constants.IntentData
|
||||
import com.github.libretube.constants.PreferenceKeys
|
||||
import com.github.libretube.databinding.DialogShareBinding
|
||||
import com.github.libretube.db.DatabaseHelper
|
||||
import com.github.libretube.db.DatabaseHolder.Database
|
||||
import com.github.libretube.enums.ShareObjectType
|
||||
import com.github.libretube.extensions.parcelable
|
||||
@ -82,7 +83,8 @@ class ShareDialog : DialogFragment() {
|
||||
binding.timeStamp.addTextChangedListener {
|
||||
binding.linkPreview.text = generateLinkText(binding, customInstanceUrl)
|
||||
}
|
||||
binding.timeStamp.setText((shareData.currentPosition ?: getWatchPosition(id) ?: 0L).toString())
|
||||
val timeStamp = shareData.currentPosition ?: DatabaseHelper.getWatchPositionBlocking(id)?.div(1000)
|
||||
binding.timeStamp.setText((timeStamp ?: 0L).toString())
|
||||
if (binding.timeCodeSwitch.isChecked) {
|
||||
binding.timeStampInputLayout.isVisible = true
|
||||
}
|
||||
@ -145,10 +147,6 @@ class ShareDialog : DialogFragment() {
|
||||
return url
|
||||
}
|
||||
|
||||
private fun getWatchPosition(videoId: String) = runBlocking {
|
||||
Database.watchPositionDao().findById(videoId)
|
||||
}?.position?.div(1000)
|
||||
|
||||
companion object {
|
||||
const val YOUTUBE_FRONTEND_URL = "https://www.youtube.com"
|
||||
const val YOUTUBE_SHORT_URL = "https://youtu.be"
|
||||
|
@ -7,9 +7,8 @@ import androidx.core.graphics.ColorUtils
|
||||
import androidx.core.view.isGone
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import com.github.libretube.db.DatabaseHolder.Database
|
||||
import com.github.libretube.db.DatabaseHelper
|
||||
import com.github.libretube.helpers.ThemeHelper
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
||||
/**
|
||||
* Shows the already watched time under the video
|
||||
@ -35,13 +34,7 @@ fun View.setWatchProgressLength(videoId: String, duration: Long) {
|
||||
return
|
||||
}
|
||||
|
||||
val progress = runCatching {
|
||||
runBlocking {
|
||||
Database.watchPositionDao().findById(videoId)?.position
|
||||
// divide by 1000 to convert ms to seconds
|
||||
?.toFloat()?.div(1000)
|
||||
}
|
||||
}.getOrNull() ?: return
|
||||
val progress = DatabaseHelper.getWatchPositionBlocking(videoId)?.div(1000)?.toFloat() ?: 0f
|
||||
|
||||
updateLayoutParams<ConstraintLayout.LayoutParams> {
|
||||
matchConstraintPercentWidth = progress / duration.toFloat()
|
||||
|
@ -142,22 +142,17 @@ class VideoOptionsBottomSheet : BaseBottomSheet() {
|
||||
|
||||
// show the mark as watched or unwatched option if watch positions are enabled
|
||||
if (PlayerHelper.watchPositionsAny || PlayerHelper.watchHistoryEnabled) {
|
||||
val watchPositionEntry = runBlocking(Dispatchers.IO) {
|
||||
DatabaseHolder.Database.watchPositionDao().findById(videoId)
|
||||
}
|
||||
val watchHistoryEntry = runBlocking(Dispatchers.IO) {
|
||||
DatabaseHolder.Database.watchHistoryDao().findById(videoId)
|
||||
}
|
||||
|
||||
if (streamItem.duration == null ||
|
||||
watchPositionEntry == null ||
|
||||
watchPositionEntry.position < streamItem.duration!! * 1000 * 0.9
|
||||
) {
|
||||
optionsList += R.string.mark_as_watched
|
||||
val isWatched = DatabaseHelper.isVideoWatchedBlocking(videoId, streamItem.duration ?: 0)
|
||||
if (isWatched || watchHistoryEntry != null) {
|
||||
optionsList += R.string.mark_as_unwatched
|
||||
}
|
||||
|
||||
if (watchHistoryEntry != null || watchPositionEntry != null) {
|
||||
optionsList += R.string.mark_as_unwatched
|
||||
if (!isWatched || watchHistoryEntry == null) {
|
||||
R.string.mark_as_watched
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user