Convert watch positions dao to coroutines, fix holes in the feed

This commit is contained in:
Bnyro 2023-02-21 18:28:21 +01:00
parent 06d6c85b25
commit 2d80de9f52
7 changed files with 52 additions and 44 deletions

View File

@ -10,20 +10,20 @@ import com.github.libretube.db.obj.WatchPosition
@Dao @Dao
interface WatchPositionDao { interface WatchPositionDao {
@Query("SELECT * FROM watchPosition") @Query("SELECT * FROM watchPosition")
fun getAll(): List<WatchPosition> suspend fun getAll(): List<WatchPosition>
@Query("SELECT * FROM watchPosition WHERE videoId LIKE :videoId LIMIT 1") @Query("SELECT * FROM watchPosition WHERE videoId LIKE :videoId LIMIT 1")
fun findById(videoId: String): WatchPosition? suspend fun findById(videoId: String): WatchPosition?
@Insert(onConflict = OnConflictStrategy.REPLACE) @Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertAll(vararg watchPositions: WatchPosition) suspend fun insertAll(vararg watchPositions: WatchPosition)
@Delete @Delete
fun delete(watchPosition: WatchPosition) suspend fun delete(watchPosition: WatchPosition)
@Query("DELETE FROM watchHistoryItem WHERE videoId = :videoId") @Query("DELETE FROM watchHistoryItem WHERE videoId = :videoId")
fun deleteById(videoId: String) suspend fun deleteById(videoId: String)
@Query("DELETE FROM watchPosition") @Query("DELETE FROM watchPosition")
fun deleteAll() suspend fun deleteAll()
} }

View File

@ -25,8 +25,6 @@ import com.github.libretube.constants.PLAYER_NOTIFICATION_ID
import com.github.libretube.db.DatabaseHolder.Database import com.github.libretube.db.DatabaseHolder.Database
import com.github.libretube.db.obj.WatchPosition import com.github.libretube.db.obj.WatchPosition
import com.github.libretube.extensions.TAG import com.github.libretube.extensions.TAG
import com.github.libretube.extensions.awaitQuery
import com.github.libretube.extensions.query
import com.github.libretube.extensions.toID import com.github.libretube.extensions.toID
import com.github.libretube.helpers.PlayerHelper import com.github.libretube.helpers.PlayerHelper
import com.github.libretube.helpers.PlayerHelper.checkForSegments import com.github.libretube.helpers.PlayerHelper.checkForSegments
@ -37,8 +35,10 @@ import com.google.android.exoplayer2.ExoPlayer
import com.google.android.exoplayer2.MediaItem import com.google.android.exoplayer2.MediaItem
import com.google.android.exoplayer2.PlaybackException import com.google.android.exoplayer2.PlaybackException
import com.google.android.exoplayer2.Player import com.google.android.exoplayer2.Player
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlinx.serialization.encodeToString import kotlinx.serialization.encodeToString
@ -154,7 +154,7 @@ class BackgroundMode : LifecycleService() {
// indicator that a new video is getting loaded // indicator that a new video is getting loaded
this.streams ?: return@let this.streams ?: return@let
query { CoroutineScope(Dispatchers.IO).launch {
Database.watchPositionDao().insertAll(watchPosition) Database.watchPositionDao().insertAll(watchPosition)
} }
} }
@ -213,7 +213,7 @@ class BackgroundMode : LifecycleService() {
player?.seekTo(seekToPosition) player?.seekTo(seekToPosition)
} else if (PlayerHelper.watchPositionsAudio) { } else if (PlayerHelper.watchPositionsAudio) {
runCatching { runCatching {
val watchPosition = awaitQuery { val watchPosition = runBlocking {
Database.watchPositionDao().findById(videoId) Database.watchPositionDao().findById(videoId)
} }
streams?.duration?.let { streams?.duration?.let {

View File

@ -33,8 +33,7 @@ import com.github.libretube.util.TextUtils
class VideosAdapter( class VideosAdapter(
private val streamItems: MutableList<StreamItem>, private val streamItems: MutableList<StreamItem>,
private val showAllAtOnce: Boolean = true, private val showAllAtOnce: Boolean = true,
private val forceMode: ForceMode = ForceMode.NONE, private val forceMode: ForceMode = ForceMode.NONE
private val hideWatched: Boolean = false
) : RecyclerView.Adapter<VideosViewHolder>() { ) : RecyclerView.Adapter<VideosViewHolder>() {
private var visibleCount = minOf(10, streamItems.size) private var visibleCount = minOf(10, streamItems.size)
@ -116,13 +115,8 @@ class VideosAdapter(
} }
videoId?.let { videoId?.let {
val shouldHide = (holder.trendingRowBinding?.watchProgress ?: holder.videoRowBinding!!.watchProgress)
(holder.trendingRowBinding?.watchProgress ?: holder.videoRowBinding!!.watchProgress) .setWatchProgressLength(it, video.duration ?: 0L)
.setWatchProgressLength(it, video.duration ?: 0L)
if (hideWatched && shouldHide) {
hideItemView(holder)
return
}
} }
// Trending layout // Trending layout

View File

@ -4,37 +4,34 @@ import android.view.View
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.updateLayoutParams import androidx.core.view.updateLayoutParams
import com.github.libretube.db.DatabaseHolder.Database import com.github.libretube.db.DatabaseHolder.Database
import com.github.libretube.extensions.awaitQuery import kotlinx.coroutines.runBlocking
/** /**
* Shows the already watched time under the video * Shows the already watched time under the video
* @param videoId The id of the video to inspect * @param videoId The id of the video to inspect
* @param duration The duration of the video in seconds * @param duration The duration of the video in seconds
* @return Whether the video is already watched more than 90%
*/ */
fun View.setWatchProgressLength(videoId: String, duration: Long): Boolean { fun View.setWatchProgressLength(videoId: String, duration: Long) {
updateLayoutParams<ConstraintLayout.LayoutParams> { updateLayoutParams<ConstraintLayout.LayoutParams> {
matchConstraintPercentWidth = 0f matchConstraintPercentWidth = 0f
} }
visibility = View.GONE visibility = View.GONE
val progress = try { val progress = try {
awaitQuery { runBlocking {
Database.watchPositionDao().findById(videoId)?.position Database.watchPositionDao().findById(videoId)?.position
} }
} catch (e: Exception) { } catch (e: Exception) {
return false return
} // divide by 1000 to convert ms to seconds } // divide by 1000 to convert ms to seconds
?.toFloat()?.div(1000) ?.toFloat()?.div(1000)
if (progress == null || duration == 0L) { if (progress == null || duration == 0L) {
return false return
} }
updateLayoutParams<ConstraintLayout.LayoutParams> { updateLayoutParams<ConstraintLayout.LayoutParams> {
matchConstraintPercentWidth = progress / duration.toFloat() matchConstraintPercentWidth = progress / duration.toFloat()
} }
visibility = View.VISIBLE visibility = View.VISIBLE
return progress / duration.toFloat() > 0.9
} }

View File

@ -56,10 +56,8 @@ import com.github.libretube.db.DatabaseHolder.Database
import com.github.libretube.db.obj.WatchPosition import com.github.libretube.db.obj.WatchPosition
import com.github.libretube.enums.PlayerEvent import com.github.libretube.enums.PlayerEvent
import com.github.libretube.enums.ShareObjectType import com.github.libretube.enums.ShareObjectType
import com.github.libretube.extensions.awaitQuery
import com.github.libretube.extensions.formatShort import com.github.libretube.extensions.formatShort
import com.github.libretube.extensions.hideKeyboard import com.github.libretube.extensions.hideKeyboard
import com.github.libretube.extensions.query
import com.github.libretube.extensions.toID import com.github.libretube.extensions.toID
import com.github.libretube.extensions.updateParameters import com.github.libretube.extensions.updateParameters
import com.github.libretube.helpers.BackgroundHelper import com.github.libretube.helpers.BackgroundHelper
@ -114,6 +112,7 @@ import kotlin.math.abs
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlinx.serialization.decodeFromString import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString import kotlinx.serialization.encodeToString
@ -609,7 +608,7 @@ class PlayerFragment : Fragment(R.layout.fragment_player), OnlinePlayerOptions {
private fun saveWatchPosition() { private fun saveWatchPosition() {
if (!PlayerHelper.watchPositionsVideo) return if (!PlayerHelper.watchPositionsVideo) return
val watchPosition = WatchPosition(videoId!!, exoPlayer.currentPosition) val watchPosition = WatchPosition(videoId!!, exoPlayer.currentPosition)
query { CoroutineScope(Dispatchers.IO).launch {
Database.watchPositionDao().insertAll(watchPosition) Database.watchPositionDao().insertAll(watchPosition)
} }
} }
@ -765,7 +764,7 @@ class PlayerFragment : Fragment(R.layout.fragment_player), OnlinePlayerOptions {
private fun seekToWatchPosition() { private fun seekToWatchPosition() {
// browse the watch positions // browse the watch positions
val position = try { val position = try {
awaitQuery { runBlocking {
Database.watchPositionDao().findById(videoId!!)?.position Database.watchPositionDao().findById(videoId!!)?.position
} }
} catch (e: Exception) { } catch (e: Exception) {

View File

@ -13,6 +13,8 @@ import com.github.libretube.R
import com.github.libretube.api.obj.StreamItem import com.github.libretube.api.obj.StreamItem
import com.github.libretube.constants.PreferenceKeys import com.github.libretube.constants.PreferenceKeys
import com.github.libretube.databinding.FragmentSubscriptionsBinding import com.github.libretube.databinding.FragmentSubscriptionsBinding
import com.github.libretube.db.DatabaseHolder
import com.github.libretube.extensions.toID
import com.github.libretube.helpers.PreferenceHelper import com.github.libretube.helpers.PreferenceHelper
import com.github.libretube.ui.adapters.LegacySubscriptionAdapter import com.github.libretube.ui.adapters.LegacySubscriptionAdapter
import com.github.libretube.ui.adapters.SubscriptionChannelAdapter import com.github.libretube.ui.adapters.SubscriptionChannelAdapter
@ -20,6 +22,8 @@ import com.github.libretube.ui.adapters.VideosAdapter
import com.github.libretube.ui.base.BaseFragment import com.github.libretube.ui.base.BaseFragment
import com.github.libretube.ui.models.SubscriptionsViewModel import com.github.libretube.ui.models.SubscriptionsViewModel
import com.github.libretube.ui.sheets.BaseBottomSheet import com.github.libretube.ui.sheets.BaseBottomSheet
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
class SubscriptionsFragment : BaseFragment() { class SubscriptionsFragment : BaseFragment() {
private lateinit var binding: FragmentSubscriptionsBinding private lateinit var binding: FragmentSubscriptionsBinding
@ -159,7 +163,16 @@ class SubscriptionsFragment : BaseFragment() {
2 -> it.isShort 2 -> it.isShort
else -> throw IllegalArgumentException() else -> throw IllegalArgumentException()
} }
}.let { streams ->
runBlocking {
if (!PreferenceHelper.getBoolean(PreferenceKeys.HIDE_WATCHED_FROM_FEED, false)) {
streams
} else {
removeWatchVideosFromFeed(streams)
}
}
} }
// sort the feed // sort the feed
val sortedFeed = when (selectedSortOrder) { val sortedFeed = when (selectedSortOrder) {
0 -> feed 0 -> feed
@ -191,14 +204,27 @@ class SubscriptionsFragment : BaseFragment() {
binding.subProgress.visibility = View.GONE binding.subProgress.visibility = View.GONE
subscriptionsAdapter = VideosAdapter( subscriptionsAdapter = VideosAdapter(
sortedFeed.toMutableList(), sortedFeed.toMutableList(),
showAllAtOnce = false, showAllAtOnce = false
hideWatched = PreferenceHelper.getBoolean(PreferenceKeys.HIDE_WATCHED_FROM_FEED, false)
) )
binding.subFeed.adapter = subscriptionsAdapter binding.subFeed.adapter = subscriptionsAdapter
PreferenceHelper.updateLastFeedWatchedTime() PreferenceHelper.updateLastFeedWatchedTime()
} }
private fun removeWatchVideosFromFeed(streams: List<StreamItem>): List<StreamItem> {
return runBlocking {
streams.filter {
runBlocking(Dispatchers.IO) {
runCatching {
val watchPosition = DatabaseHolder.Database.watchPositionDao()
.findById(it.url.orEmpty().toID())?.position?.div(1000)
(watchPosition ?: 0) < 0.9 * (it.duration ?: 1L)
}.getOrDefault(false)
}
}
}
}
private fun showSubscriptions() { private fun showSubscriptions() {
if (viewModel.subscriptions.value == null) return if (viewModel.subscriptions.value == null) return

View File

@ -1,7 +1,6 @@
package com.github.libretube.ui.sheets package com.github.libretube.ui.sheets
import android.os.Bundle import android.os.Bundle
import android.util.Log
import androidx.navigation.fragment.NavHostFragment import androidx.navigation.fragment.NavHostFragment
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.api.RetrofitInstance import com.github.libretube.api.RetrofitInstance
@ -9,7 +8,6 @@ import com.github.libretube.constants.PreferenceKeys
import com.github.libretube.db.DatabaseHolder import com.github.libretube.db.DatabaseHolder
import com.github.libretube.db.obj.WatchPosition import com.github.libretube.db.obj.WatchPosition
import com.github.libretube.enums.ShareObjectType import com.github.libretube.enums.ShareObjectType
import com.github.libretube.extensions.awaitQuery
import com.github.libretube.helpers.BackgroundHelper import com.github.libretube.helpers.BackgroundHelper
import com.github.libretube.helpers.PlayerHelper import com.github.libretube.helpers.PlayerHelper
import com.github.libretube.helpers.PreferenceHelper import com.github.libretube.helpers.PreferenceHelper
@ -102,22 +100,16 @@ class VideoOptionsBottomSheet(
} }
getString(R.string.mark_as_watched) -> { getString(R.string.mark_as_watched) -> {
val watchPosition = WatchPosition(videoId, Long.MAX_VALUE) val watchPosition = WatchPosition(videoId, Long.MAX_VALUE)
awaitQuery { CoroutineScope(Dispatchers.IO).launch {
DatabaseHolder.Database.watchPositionDao().insertAll(watchPosition) DatabaseHolder.Database.watchPositionDao().insertAll(watchPosition)
} }
if (PreferenceHelper.getBoolean(PreferenceKeys.HIDE_WATCHED_FROM_FEED, false)) { if (PreferenceHelper.getBoolean(PreferenceKeys.HIDE_WATCHED_FROM_FEED, false)) {
// get the host fragment containing the current fragment // get the host fragment containing the current fragment
val navHostFragment = val navHostFragment =
(context as MainActivity).supportFragmentManager.findFragmentById( (context as MainActivity).supportFragmentManager
R.id.fragment .findFragmentById(R.id.fragment) as NavHostFragment?
) as NavHostFragment?
// get the current fragment // get the current fragment
val fragment = navHostFragment?.childFragmentManager?.fragments?.firstOrNull() val fragment = navHostFragment?.childFragmentManager?.fragments?.firstOrNull()
Log.e(
"fragments",
navHostFragment?.childFragmentManager?.fragments.orEmpty()
.joinToString(", ") { it::class.java.name.toString() }
)
(fragment as? SubscriptionsFragment)?.subscriptionsAdapter?.removeItemById( (fragment as? SubscriptionsFragment)?.subscriptionsAdapter?.removeItemById(
videoId videoId
) )