mirror of
https://github.com/libre-tube/LibreTube.git
synced 2024-12-15 14:50:30 +05:30
feat: add 'Continue watching' section to home tab
This commit is contained in:
parent
e75bbc868e
commit
110d29c50a
@ -1,5 +1,6 @@
|
||||
package com.github.libretube.db
|
||||
|
||||
import com.github.libretube.api.obj.StreamItem
|
||||
import com.github.libretube.api.obj.Streams
|
||||
import com.github.libretube.constants.PreferenceKeys
|
||||
import com.github.libretube.db.DatabaseHolder.Database
|
||||
@ -50,4 +51,17 @@ object DatabaseHelper {
|
||||
searchHistory.removeFirst()
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun filterUnwatched(streams: List<StreamItem>): List<StreamItem> {
|
||||
return streams.filter {
|
||||
withContext(Dispatchers.IO) {
|
||||
val historyItem = Database.watchPositionDao()
|
||||
.findById(it.url.orEmpty().toID()) ?: return@withContext true
|
||||
val progress = historyItem.position / 1000
|
||||
val duration = it.duration ?: 0
|
||||
// show video only in feed when watched less than 90%
|
||||
progress < 0.9f * duration
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,8 @@ package com.github.libretube.db.obj
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import com.github.libretube.api.obj.StreamItem
|
||||
import com.github.libretube.extensions.toMillis
|
||||
import kotlinx.datetime.LocalDate
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@ -17,4 +19,16 @@ data class WatchHistoryItem(
|
||||
@ColumnInfo var uploaderAvatar: String? = null,
|
||||
@ColumnInfo var thumbnailUrl: String? = null,
|
||||
@ColumnInfo val duration: Long? = null
|
||||
) {
|
||||
fun toStreamItem() = StreamItem(
|
||||
url = videoId,
|
||||
type = "stream",
|
||||
title = title,
|
||||
thumbnail = thumbnailUrl,
|
||||
uploaderName = uploader,
|
||||
uploaded = uploadDate?.toMillis(),
|
||||
uploaderAvatar = uploaderAvatar,
|
||||
uploaderUrl = uploaderUrl,
|
||||
duration = duration
|
||||
)
|
||||
}
|
||||
|
@ -0,0 +1,7 @@
|
||||
package com.github.libretube.extensions
|
||||
|
||||
import kotlinx.datetime.LocalDate
|
||||
import kotlinx.datetime.TimeZone
|
||||
import kotlinx.datetime.atStartOfDayIn
|
||||
|
||||
fun LocalDate.toMillis() = this.atStartOfDayIn(TimeZone.UTC).toEpochMilliseconds()
|
@ -21,8 +21,10 @@ import com.github.libretube.api.RetrofitInstance
|
||||
import com.github.libretube.api.SubscriptionHelper
|
||||
import com.github.libretube.constants.PreferenceKeys
|
||||
import com.github.libretube.databinding.FragmentHomeBinding
|
||||
import com.github.libretube.db.DatabaseHelper
|
||||
import com.github.libretube.db.DatabaseHolder
|
||||
import com.github.libretube.helpers.LocaleHelper
|
||||
import com.github.libretube.helpers.PlayerHelper
|
||||
import com.github.libretube.helpers.PreferenceHelper
|
||||
import com.github.libretube.ui.adapters.PlaylistBookmarkAdapter
|
||||
import com.github.libretube.ui.adapters.PlaylistsAdapter
|
||||
@ -56,6 +58,10 @@ class HomeFragment : Fragment() {
|
||||
findNavController().navigate(R.id.subscriptionsFragment)
|
||||
}
|
||||
|
||||
binding.watchingTV.setOnClickListener {
|
||||
findNavController().navigate(R.id.watchHistoryFragment)
|
||||
}
|
||||
|
||||
binding.trendingTV.setOnClickListener {
|
||||
findNavController().navigate(R.id.trendsFragment)
|
||||
}
|
||||
@ -90,6 +96,7 @@ class HomeFragment : Fragment() {
|
||||
.getStringSet(PreferenceKeys.HOME_TAB_CONTENT, defaultItems.toSet())
|
||||
awaitAll(
|
||||
async { if (visibleItems.contains(TRENDING)) loadTrending() },
|
||||
async { if (visibleItems.contains(WATCHING)) loadVideosToContinueWatching() },
|
||||
async { if (visibleItems.contains(BOOKMARKS)) loadBookmarks() },
|
||||
async { if (visibleItems.contains(FEATURED)) loadFeed() },
|
||||
async { if (visibleItems.contains(PLAYLISTS)) loadPlaylists() }
|
||||
@ -200,6 +207,30 @@ class HomeFragment : Fragment() {
|
||||
})
|
||||
}
|
||||
|
||||
private suspend fun loadVideosToContinueWatching() {
|
||||
if (!PlayerHelper.watchHistoryEnabled) return
|
||||
|
||||
val videos = withContext(Dispatchers.IO) {
|
||||
DatabaseHolder.Database.watchHistoryDao().getAll()
|
||||
}
|
||||
val unwatchedVideos = DatabaseHelper.filterUnwatched(videos.map { it.toStreamItem() })
|
||||
.reversed()
|
||||
.take(20)
|
||||
if (unwatchedVideos.isEmpty()) return
|
||||
val binding = _binding ?: return
|
||||
|
||||
makeVisible(binding.watchingRV, binding.watchingTV)
|
||||
binding.watchingRV.layoutManager = LinearLayoutManager(
|
||||
context,
|
||||
LinearLayoutManager.HORIZONTAL,
|
||||
false
|
||||
)
|
||||
binding.watchingRV.adapter = VideosAdapter(
|
||||
unwatchedVideos.toMutableList(),
|
||||
forceMode = VideosAdapter.Companion.ForceMode.HOME
|
||||
)
|
||||
}
|
||||
|
||||
private fun makeVisible(vararg views: View) {
|
||||
views.forEach {
|
||||
it.isVisible = true
|
||||
@ -213,6 +244,7 @@ class HomeFragment : Fragment() {
|
||||
companion object {
|
||||
// The values of the preference entries for the home tab content
|
||||
private const val FEATURED = "featured"
|
||||
private const val WATCHING = "watching"
|
||||
private const val TRENDING = "trending"
|
||||
private const val BOOKMARKS = "bookmarks"
|
||||
private const val PLAYLISTS = "playlists"
|
||||
|
@ -20,6 +20,7 @@ import com.github.libretube.R
|
||||
import com.github.libretube.api.obj.StreamItem
|
||||
import com.github.libretube.constants.PreferenceKeys
|
||||
import com.github.libretube.databinding.FragmentSubscriptionsBinding
|
||||
import com.github.libretube.db.DatabaseHelper
|
||||
import com.github.libretube.db.DatabaseHolder
|
||||
import com.github.libretube.db.obj.SubscriptionGroup
|
||||
import com.github.libretube.extensions.dpToPx
|
||||
@ -33,7 +34,6 @@ import com.github.libretube.ui.models.SubscriptionsViewModel
|
||||
import com.github.libretube.ui.sheets.BaseBottomSheet
|
||||
import com.github.libretube.ui.sheets.ChannelGroupsSheet
|
||||
import com.google.android.material.chip.Chip
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
||||
@ -243,7 +243,7 @@ class SubscriptionsFragment : Fragment() {
|
||||
else -> throw IllegalArgumentException()
|
||||
}
|
||||
}.let { streams ->
|
||||
runBlocking {
|
||||
|
||||
if (!PreferenceHelper.getBoolean(
|
||||
PreferenceKeys.HIDE_WATCHED_FROM_FEED,
|
||||
false
|
||||
@ -251,7 +251,8 @@ class SubscriptionsFragment : Fragment() {
|
||||
) {
|
||||
streams
|
||||
} else {
|
||||
removeWatchVideosFromFeed(streams)
|
||||
runBlocking {
|
||||
DatabaseHelper.filterUnwatched(streams)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -294,19 +295,6 @@ class SubscriptionsFragment : Fragment() {
|
||||
PreferenceHelper.updateLastFeedWatchedTime()
|
||||
}
|
||||
|
||||
private fun removeWatchVideosFromFeed(streams: List<StreamItem>): List<StreamItem> {
|
||||
return streams.filter {
|
||||
runBlocking(Dispatchers.IO) {
|
||||
val historyItem = DatabaseHolder.Database.watchPositionDao()
|
||||
.findById(it.url.orEmpty().toID()) ?: return@runBlocking true
|
||||
val progress = historyItem.position / 1000
|
||||
val duration = it.duration ?: 0
|
||||
// show video only in feed when watched less than 1/4
|
||||
progress < 0.9f * duration
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showSubscriptions() {
|
||||
if (viewModel.subscriptions.value == null) return
|
||||
|
||||
|
@ -39,6 +39,19 @@
|
||||
android:nestedScrollingEnabled="false"
|
||||
android:visibility="gone" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/watchingTV"
|
||||
style="@style/HomeCategoryTitle"
|
||||
android:text="@string/continue_watching" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/watchingRV"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="10dp"
|
||||
android:nestedScrollingEnabled="false"
|
||||
android:visibility="gone" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/trendingTV"
|
||||
style="@style/HomeCategoryTitle"
|
||||
|
@ -427,6 +427,7 @@
|
||||
|
||||
<string-array name="homeTabItems">
|
||||
<item>@string/featured</item>
|
||||
<item>@string/continue_watching</item>
|
||||
<item>@string/trending</item>
|
||||
<item>@string/bookmarks</item>
|
||||
<item>@string/playlists</item>
|
||||
@ -434,6 +435,7 @@
|
||||
|
||||
<string-array name="homeTabItemsValues">
|
||||
<item>featured</item>
|
||||
<item>watching</item>
|
||||
<item>trending</item>
|
||||
<item>bookmarks</item>
|
||||
<item>playlists</item>
|
||||
|
@ -452,6 +452,7 @@
|
||||
<string name="descriptive_audio_track">descriptive</string>
|
||||
<string name="default_or_unknown_audio_track">default or unknown</string>
|
||||
<string name="unknown_or_no_audio">unknown or no audio</string>
|
||||
<string name="continue_watching">Continue watching</string>
|
||||
|
||||
<!-- Notification channel strings -->
|
||||
<string name="download_channel_name">Download Service</string>
|
||||
|
Loading…
Reference in New Issue
Block a user