diff --git a/app/src/main/java/com/github/libretube/db/DatabaseHelper.kt b/app/src/main/java/com/github/libretube/db/DatabaseHelper.kt index 8b683913f..71543ffb9 100644 --- a/app/src/main/java/com/github/libretube/db/DatabaseHelper.kt +++ b/app/src/main/java/com/github/libretube/db/DatabaseHelper.kt @@ -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): List { @@ -85,7 +101,10 @@ object DatabaseHelper { } } - fun filterByStatusAndWatchPosition(streams: List, hideWatched: Boolean): List { + fun filterByStatusAndWatchPosition( + streams: List, + hideWatched: Boolean + ): List { val streamItems = streams.filter { val isVideo = !it.isShort && !it.isLive diff --git a/app/src/main/java/com/github/libretube/helpers/PlayerHelper.kt b/app/src/main/java/com/github/libretube/helpers/PlayerHelper.kt index 437784ae8..fbc0bcaa4 100644 --- a/app/src/main/java/com/github/libretube/helpers/PlayerHelper.kt +++ b/app/src/main/java/com/github/libretube/helpers/PlayerHelper.kt @@ -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. diff --git a/app/src/main/java/com/github/libretube/services/AbstractPlayerService.kt b/app/src/main/java/com/github/libretube/services/AbstractPlayerService.kt index 7f75dcb9d..12a50da87 100644 --- a/app/src/main/java/com/github/libretube/services/AbstractPlayerService.kt +++ b/app/src/main/java/com/github/libretube/services/AbstractPlayerService.kt @@ -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) } diff --git a/app/src/main/java/com/github/libretube/services/OfflinePlayerService.kt b/app/src/main/java/com/github/libretube/services/OfflinePlayerService.kt index 604a2d0c9..ca5d4472a 100644 --- a/app/src/main/java/com/github/libretube/services/OfflinePlayerService.kt +++ b/app/src/main/java/com/github/libretube/services/OfflinePlayerService.kt @@ -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) } } } diff --git a/app/src/main/java/com/github/libretube/services/OnlinePlayerService.kt b/app/src/main/java/com/github/libretube/services/OnlinePlayerService.kt index 03b883da4..bfc3b9161 100644 --- a/app/src/main/java/com/github/libretube/services/OnlinePlayerService.kt +++ b/app/src/main/java/com/github/libretube/services/OnlinePlayerService.kt @@ -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) } } diff --git a/app/src/main/java/com/github/libretube/ui/dialogs/ShareDialog.kt b/app/src/main/java/com/github/libretube/ui/dialogs/ShareDialog.kt index 05af204a9..10c630ac6 100644 --- a/app/src/main/java/com/github/libretube/ui/dialogs/ShareDialog.kt +++ b/app/src/main/java/com/github/libretube/ui/dialogs/ShareDialog.kt @@ -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" diff --git a/app/src/main/java/com/github/libretube/ui/extensions/SetWatchProgressLength.kt b/app/src/main/java/com/github/libretube/ui/extensions/SetWatchProgressLength.kt index 22912cb4a..9074ada26 100644 --- a/app/src/main/java/com/github/libretube/ui/extensions/SetWatchProgressLength.kt +++ b/app/src/main/java/com/github/libretube/ui/extensions/SetWatchProgressLength.kt @@ -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 { matchConstraintPercentWidth = progress / duration.toFloat() diff --git a/app/src/main/java/com/github/libretube/ui/sheets/VideoOptionsBottomSheet.kt b/app/src/main/java/com/github/libretube/ui/sheets/VideoOptionsBottomSheet.kt index f23dd1d94..327eecdca 100644 --- a/app/src/main/java/com/github/libretube/ui/sheets/VideoOptionsBottomSheet.kt +++ b/app/src/main/java/com/github/libretube/ui/sheets/VideoOptionsBottomSheet.kt @@ -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 } }