mirror of
https://github.com/libre-tube/LibreTube.git
synced 2025-04-29 00:10:32 +05:30
Merge pull request #5669 from Bnyro/master
feat: watch positions support for downloaded media
This commit is contained in:
commit
698c0ff865
@ -36,11 +36,15 @@ import com.github.libretube.api.obj.Segment
|
|||||||
import com.github.libretube.api.obj.Streams
|
import com.github.libretube.api.obj.Streams
|
||||||
import com.github.libretube.constants.PreferenceKeys
|
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.enums.PlayerEvent
|
import com.github.libretube.enums.PlayerEvent
|
||||||
import com.github.libretube.enums.SbSkipOptions
|
import com.github.libretube.enums.SbSkipOptions
|
||||||
import com.github.libretube.extensions.updateParameters
|
import com.github.libretube.extensions.updateParameters
|
||||||
import com.github.libretube.obj.VideoStats
|
import com.github.libretube.obj.VideoStats
|
||||||
import com.github.libretube.util.TextUtils
|
import com.github.libretube.util.TextUtils
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
@ -53,6 +57,7 @@ object PlayerHelper {
|
|||||||
const val SPONSOR_HIGHLIGHT_CATEGORY = "poi_highlight"
|
const val SPONSOR_HIGHLIGHT_CATEGORY = "poi_highlight"
|
||||||
const val ROLE_FLAG_AUTO_GEN_SUBTITLE = C.ROLE_FLAG_SUPPLEMENTARY
|
const val ROLE_FLAG_AUTO_GEN_SUBTITLE = C.ROLE_FLAG_SUPPLEMENTARY
|
||||||
private const val MINIMUM_BUFFER_DURATION = 1000 * 10 // exo default is 50s
|
private const val MINIMUM_BUFFER_DURATION = 1000 * 10 // exo default is 50s
|
||||||
|
const val WATCH_POSITION_TIMER_DELAY_MS = 1000L
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The maximum amount of time to wait until the video starts playing: 10 minutes
|
* The maximum amount of time to wait until the video starts playing: 10 minutes
|
||||||
@ -606,7 +611,7 @@ object PlayerHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getPosition(videoId: String, duration: Long?): Long? {
|
fun getStoredWatchPosition(videoId: String, duration: Long?): Long? {
|
||||||
if (duration == null) return null
|
if (duration == null) return null
|
||||||
|
|
||||||
runCatching {
|
runCatching {
|
||||||
@ -780,4 +785,15 @@ object PlayerHelper {
|
|||||||
player.playbackState == Player.STATE_ENDED -> R.drawable.ic_restart
|
player.playbackState == Player.STATE_ENDED -> R.drawable.ic_restart
|
||||||
else -> R.drawable.ic_play
|
else -> R.drawable.ic_play
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun saveWatchPosition(player: ExoPlayer, videoId: String) {
|
||||||
|
if (player.duration == C.TIME_UNSET || player.currentPosition in listOf(0L, C.TIME_UNSET)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val watchPosition = WatchPosition(videoId, player.currentPosition)
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
DatabaseHolder.Database.watchPositionDao().insert(watchPosition)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
package com.github.libretube.services
|
package com.github.libretube.services
|
||||||
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.os.Handler
|
||||||
import android.os.IBinder
|
import android.os.IBinder
|
||||||
|
import android.os.Looper
|
||||||
import androidx.annotation.OptIn
|
import androidx.annotation.OptIn
|
||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
import androidx.core.app.ServiceCompat
|
import androidx.core.app.ServiceCompat
|
||||||
@ -29,17 +31,39 @@ import kotlin.io.path.exists
|
|||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
import java.util.Timer
|
||||||
|
import java.util.TimerTask
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A service to play downloaded audio in the background
|
* A service to play downloaded audio in the background
|
||||||
*/
|
*/
|
||||||
class OfflinePlayerService : LifecycleService() {
|
class OfflinePlayerService : LifecycleService() {
|
||||||
|
val handler = Handler(Looper.getMainLooper())
|
||||||
|
|
||||||
private var player: ExoPlayer? = null
|
private var player: ExoPlayer? = null
|
||||||
private var nowPlayingNotification: NowPlayingNotification? = null
|
private var nowPlayingNotification: NowPlayingNotification? = null
|
||||||
private lateinit var videoId: String
|
private lateinit var videoId: String
|
||||||
private var downloadsWithItems: List<DownloadWithItems> = emptyList()
|
private var downloadsWithItems: List<DownloadWithItems> = emptyList()
|
||||||
|
|
||||||
|
private var watchPositionTimer: Timer? = null
|
||||||
|
|
||||||
private val playerListener = object : Player.Listener {
|
private val playerListener = object : Player.Listener {
|
||||||
|
override fun onIsPlayingChanged(isPlaying: Boolean) {
|
||||||
|
super.onIsPlayingChanged(isPlaying)
|
||||||
|
|
||||||
|
// Start or pause watch position timer
|
||||||
|
if (isPlaying) {
|
||||||
|
watchPositionTimer = Timer()
|
||||||
|
watchPositionTimer!!.scheduleAtFixedRate(object : TimerTask() {
|
||||||
|
override fun run() {
|
||||||
|
handler.post(this@OfflinePlayerService::saveWatchPosition)
|
||||||
|
}
|
||||||
|
}, PlayerHelper.WATCH_POSITION_TIMER_DELAY_MS, PlayerHelper.WATCH_POSITION_TIMER_DELAY_MS)
|
||||||
|
} else {
|
||||||
|
watchPositionTimer?.cancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onPlaybackStateChanged(playbackState: Int) {
|
override fun onPlaybackStateChanged(playbackState: Int) {
|
||||||
super.onPlaybackStateChanged(playbackState)
|
super.onPlaybackStateChanged(playbackState)
|
||||||
|
|
||||||
@ -142,10 +166,24 @@ class OfflinePlayerService : LifecycleService() {
|
|||||||
player?.playWhenReady = PlayerHelper.playAutomatically
|
player?.playWhenReady = PlayerHelper.playAutomatically
|
||||||
player?.prepare()
|
player?.prepare()
|
||||||
|
|
||||||
|
if (PlayerHelper.watchPositionsAudio) {
|
||||||
|
PlayerHelper.getStoredWatchPosition(videoId, downloadWithItems.download.duration)?.let {
|
||||||
|
player?.seekTo(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun saveWatchPosition() {
|
||||||
|
if (!PlayerHelper.watchPositionsVideo) return
|
||||||
|
|
||||||
|
player?.let { PlayerHelper.saveWatchPosition(it, videoId) }
|
||||||
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
|
saveWatchPosition()
|
||||||
|
|
||||||
nowPlayingNotification?.destroySelf()
|
nowPlayingNotification?.destroySelf()
|
||||||
|
|
||||||
player?.stop()
|
player?.stop()
|
||||||
@ -153,6 +191,8 @@ class OfflinePlayerService : LifecycleService() {
|
|||||||
player = null
|
player = null
|
||||||
nowPlayingNotification = null
|
nowPlayingNotification = null
|
||||||
|
|
||||||
|
watchPositionTimer?.cancel()
|
||||||
|
|
||||||
ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE)
|
ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE)
|
||||||
stopSelf()
|
stopSelf()
|
||||||
|
|
||||||
|
@ -28,8 +28,6 @@ import com.github.libretube.api.obj.Segment
|
|||||||
import com.github.libretube.api.obj.Streams
|
import com.github.libretube.api.obj.Streams
|
||||||
import com.github.libretube.constants.IntentData
|
import com.github.libretube.constants.IntentData
|
||||||
import com.github.libretube.db.DatabaseHelper
|
import com.github.libretube.db.DatabaseHelper
|
||||||
import com.github.libretube.db.DatabaseHolder.Database
|
|
||||||
import com.github.libretube.db.obj.WatchPosition
|
|
||||||
import com.github.libretube.enums.NotificationId
|
import com.github.libretube.enums.NotificationId
|
||||||
import com.github.libretube.extensions.parcelableExtra
|
import com.github.libretube.extensions.parcelableExtra
|
||||||
import com.github.libretube.extensions.setMetadata
|
import com.github.libretube.extensions.setMetadata
|
||||||
@ -42,11 +40,12 @@ import com.github.libretube.obj.PlayerNotificationData
|
|||||||
import com.github.libretube.parcelable.PlayerData
|
import com.github.libretube.parcelable.PlayerData
|
||||||
import com.github.libretube.util.NowPlayingNotification
|
import com.github.libretube.util.NowPlayingNotification
|
||||||
import com.github.libretube.util.PlayingQueue
|
import com.github.libretube.util.PlayingQueue
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import kotlinx.serialization.encodeToString
|
import kotlinx.serialization.encodeToString
|
||||||
|
import java.util.Timer
|
||||||
|
import java.util.TimerTask
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads the selected videos audio in background mode with a notification area.
|
* Loads the selected videos audio in background mode with a notification area.
|
||||||
@ -103,10 +102,24 @@ class OnlinePlayerService : LifecycleService() {
|
|||||||
var onStateOrPlayingChanged: ((isPlaying: Boolean) -> Unit)? = null
|
var onStateOrPlayingChanged: ((isPlaying: Boolean) -> Unit)? = null
|
||||||
var onNewVideo: ((streams: Streams, videoId: String) -> Unit)? = null
|
var onNewVideo: ((streams: Streams, videoId: String) -> Unit)? = null
|
||||||
|
|
||||||
|
private var watchPositionTimer: Timer? = null
|
||||||
|
|
||||||
private val playerListener = object : Player.Listener {
|
private val playerListener = object : Player.Listener {
|
||||||
override fun onIsPlayingChanged(isPlaying: Boolean) {
|
override fun onIsPlayingChanged(isPlaying: Boolean) {
|
||||||
super.onIsPlayingChanged(isPlaying)
|
super.onIsPlayingChanged(isPlaying)
|
||||||
onStateOrPlayingChanged?.invoke(isPlaying)
|
onStateOrPlayingChanged?.invoke(isPlaying)
|
||||||
|
|
||||||
|
// Start or pause watch position timer
|
||||||
|
if (isPlaying) {
|
||||||
|
watchPositionTimer = Timer()
|
||||||
|
watchPositionTimer!!.scheduleAtFixedRate(object : TimerTask() {
|
||||||
|
override fun run() {
|
||||||
|
handler.post(this@OnlinePlayerService::saveWatchPosition)
|
||||||
|
}
|
||||||
|
}, PlayerHelper.WATCH_POSITION_TIMER_DELAY_MS, PlayerHelper.WATCH_POSITION_TIMER_DELAY_MS)
|
||||||
|
} else {
|
||||||
|
watchPositionTimer?.cancel()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPlaybackStateChanged(state: Int) {
|
override fun onPlaybackStateChanged(state: Int) {
|
||||||
@ -180,25 +193,14 @@ class OnlinePlayerService : LifecycleService() {
|
|||||||
PlayingQueue.setOnQueueTapListener { streamItem ->
|
PlayingQueue.setOnQueueTapListener { streamItem ->
|
||||||
streamItem.url?.toID()?.let { playNextVideo(it) }
|
streamItem.url?.toID()?.let { playNextVideo(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PlayerHelper.watchPositionsAudio) {
|
|
||||||
updateWatchPosition()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return super.onStartCommand(intent, flags, startId)
|
return super.onStartCommand(intent, flags, startId)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateWatchPosition() {
|
private fun saveWatchPosition() {
|
||||||
player?.currentPosition?.let {
|
if (isTransitioning || !PlayerHelper.watchPositionsAudio) return
|
||||||
if (isTransitioning) return@let
|
|
||||||
|
|
||||||
val watchPosition = WatchPosition(videoId, it)
|
player?.let { PlayerHelper.saveWatchPosition(it, videoId) }
|
||||||
|
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
|
||||||
Database.watchPositionDao().insert(watchPosition)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
handler.postDelayed(this::updateWatchPosition, 500)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -248,7 +250,7 @@ class OnlinePlayerService : LifecycleService() {
|
|||||||
if (seekToPosition != 0L) {
|
if (seekToPosition != 0L) {
|
||||||
player?.seekTo(seekToPosition)
|
player?.seekTo(seekToPosition)
|
||||||
} else if (PlayerHelper.watchPositionsAudio) {
|
} else if (PlayerHelper.watchPositionsAudio) {
|
||||||
PlayerHelper.getPosition(videoId, streams?.duration)?.let {
|
PlayerHelper.getStoredWatchPosition(videoId, streams?.duration)?.let {
|
||||||
player?.seekTo(it)
|
player?.seekTo(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -392,6 +394,8 @@ class OnlinePlayerService : LifecycleService() {
|
|||||||
player?.stop()
|
player?.stop()
|
||||||
player?.release()
|
player?.release()
|
||||||
|
|
||||||
|
watchPositionTimer?.cancel()
|
||||||
|
|
||||||
// called when the user pressed stop in the notification
|
// called when the user pressed stop in the notification
|
||||||
// stop the service from being in the foreground and remove the notification
|
// stop the service from being in the foreground and remove the notification
|
||||||
ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE)
|
ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE)
|
||||||
|
@ -4,6 +4,8 @@ import android.content.pm.ActivityInfo
|
|||||||
import android.media.session.PlaybackState
|
import android.media.session.PlaybackState
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.os.Handler
|
||||||
|
import android.os.Looper
|
||||||
import android.text.format.DateUtils
|
import android.text.format.DateUtils
|
||||||
import android.view.KeyEvent
|
import android.view.KeyEvent
|
||||||
import androidx.activity.viewModels
|
import androidx.activity.viewModels
|
||||||
@ -42,9 +44,13 @@ import kotlin.io.path.exists
|
|||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
import java.util.Timer
|
||||||
|
import java.util.TimerTask
|
||||||
|
|
||||||
@androidx.annotation.OptIn(androidx.media3.common.util.UnstableApi::class)
|
@androidx.annotation.OptIn(androidx.media3.common.util.UnstableApi::class)
|
||||||
class OfflinePlayerActivity : BaseActivity() {
|
class OfflinePlayerActivity : BaseActivity() {
|
||||||
|
private val handler = Handler(Looper.getMainLooper())
|
||||||
|
|
||||||
private lateinit var binding: ActivityOfflinePlayerBinding
|
private lateinit var binding: ActivityOfflinePlayerBinding
|
||||||
private lateinit var videoId: String
|
private lateinit var videoId: String
|
||||||
private lateinit var player: ExoPlayer
|
private lateinit var player: ExoPlayer
|
||||||
@ -55,6 +61,8 @@ class OfflinePlayerActivity : BaseActivity() {
|
|||||||
private lateinit var playerBinding: ExoStyledPlayerControlViewBinding
|
private lateinit var playerBinding: ExoStyledPlayerControlViewBinding
|
||||||
private val playerViewModel: PlayerViewModel by viewModels()
|
private val playerViewModel: PlayerViewModel by viewModels()
|
||||||
|
|
||||||
|
private var watchPositionTimer: Timer? = null
|
||||||
|
|
||||||
private val playerListener = object : Player.Listener {
|
private val playerListener = object : Player.Listener {
|
||||||
override fun onEvents(player: Player, events: Player.Events) {
|
override fun onEvents(player: Player, events: Player.Events) {
|
||||||
super.onEvents(player, events)
|
super.onEvents(player, events)
|
||||||
@ -64,6 +72,22 @@ class OfflinePlayerActivity : BaseActivity() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onIsPlayingChanged(isPlaying: Boolean) {
|
||||||
|
super.onIsPlayingChanged(isPlaying)
|
||||||
|
|
||||||
|
// Start or pause watch position timer
|
||||||
|
if (isPlaying) {
|
||||||
|
watchPositionTimer = Timer()
|
||||||
|
watchPositionTimer!!.scheduleAtFixedRate(object : TimerTask() {
|
||||||
|
override fun run() {
|
||||||
|
handler.post(this@OfflinePlayerActivity::saveWatchPosition)
|
||||||
|
}
|
||||||
|
}, PlayerHelper.WATCH_POSITION_TIMER_DELAY_MS, PlayerHelper.WATCH_POSITION_TIMER_DELAY_MS)
|
||||||
|
} else {
|
||||||
|
watchPositionTimer?.cancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onPlaybackStateChanged(playbackState: Int) {
|
override fun onPlaybackStateChanged(playbackState: Int) {
|
||||||
super.onPlaybackStateChanged(playbackState)
|
super.onPlaybackStateChanged(playbackState)
|
||||||
// setup seekbar preview
|
// setup seekbar preview
|
||||||
@ -154,6 +178,12 @@ class OfflinePlayerActivity : BaseActivity() {
|
|||||||
|
|
||||||
player.playWhenReady = PlayerHelper.playAutomatically
|
player.playWhenReady = PlayerHelper.playAutomatically
|
||||||
player.prepare()
|
player.prepare()
|
||||||
|
|
||||||
|
if (PlayerHelper.watchPositionsVideo) {
|
||||||
|
PlayerHelper.getStoredWatchPosition(videoId, downloadInfo.download.duration)?.let {
|
||||||
|
player.seekTo(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,6 +235,12 @@ class OfflinePlayerActivity : BaseActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun saveWatchPosition() {
|
||||||
|
if (!PlayerHelper.watchPositionsVideo) return
|
||||||
|
|
||||||
|
PlayerHelper.saveWatchPosition(player, videoId)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
playerViewModel.isFullscreen.value = true
|
playerViewModel.isFullscreen.value = true
|
||||||
super.onResume()
|
super.onResume()
|
||||||
@ -216,7 +252,11 @@ class OfflinePlayerActivity : BaseActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
|
saveWatchPosition()
|
||||||
|
|
||||||
player.release()
|
player.release()
|
||||||
|
watchPositionTimer?.cancel()
|
||||||
|
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,8 +62,6 @@ import com.github.libretube.constants.IntentData
|
|||||||
import com.github.libretube.constants.PreferenceKeys
|
import com.github.libretube.constants.PreferenceKeys
|
||||||
import com.github.libretube.databinding.FragmentPlayerBinding
|
import com.github.libretube.databinding.FragmentPlayerBinding
|
||||||
import com.github.libretube.db.DatabaseHelper
|
import com.github.libretube.db.DatabaseHelper
|
||||||
import com.github.libretube.db.DatabaseHolder.Database
|
|
||||||
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.formatShort
|
import com.github.libretube.extensions.formatShort
|
||||||
@ -114,7 +112,6 @@ import com.github.libretube.util.PlayingQueue
|
|||||||
import com.github.libretube.util.TextUtils
|
import com.github.libretube.util.TextUtils
|
||||||
import com.github.libretube.util.TextUtils.toTimeInSeconds
|
import com.github.libretube.util.TextUtils.toTimeInSeconds
|
||||||
import com.github.libretube.util.YoutubeHlsPlaylistParser
|
import com.github.libretube.util.YoutubeHlsPlaylistParser
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
@ -275,7 +272,7 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
|
|||||||
override fun run() {
|
override fun run() {
|
||||||
handler.post(this@PlayerFragment::saveWatchPosition)
|
handler.post(this@PlayerFragment::saveWatchPosition)
|
||||||
}
|
}
|
||||||
}, 1000, 1000)
|
}, PlayerHelper.WATCH_POSITION_TIMER_DELAY_MS, PlayerHelper.WATCH_POSITION_TIMER_DELAY_MS)
|
||||||
} else {
|
} else {
|
||||||
watchPositionTimer?.cancel()
|
watchPositionTimer?.cancel()
|
||||||
}
|
}
|
||||||
@ -861,17 +858,8 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
|
|||||||
|
|
||||||
// save the watch position if video isn't finished and option enabled
|
// save the watch position if video isn't finished and option enabled
|
||||||
private fun saveWatchPosition() {
|
private fun saveWatchPosition() {
|
||||||
if (!this::exoPlayer.isInitialized || !PlayerHelper.watchPositionsVideo || isTransitioning ||
|
if (this::exoPlayer.isInitialized && !isTransitioning && PlayerHelper.watchPositionsVideo) {
|
||||||
exoPlayer.duration == C.TIME_UNSET || exoPlayer.currentPosition in listOf(
|
PlayerHelper.saveWatchPosition(exoPlayer, videoId)
|
||||||
0L,
|
|
||||||
C.TIME_UNSET
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
val watchPosition = WatchPosition(videoId, exoPlayer.currentPosition)
|
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
|
||||||
Database.watchPositionDao().insert(watchPosition)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1290,7 +1278,7 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
|
|||||||
timeStamp = 0L
|
timeStamp = 0L
|
||||||
} else if (!streams.livestream) {
|
} else if (!streams.livestream) {
|
||||||
// seek to the saved watch position
|
// seek to the saved watch position
|
||||||
PlayerHelper.getPosition(videoId, streams.duration)?.let {
|
PlayerHelper.getStoredWatchPosition(videoId, streams.duration)?.let {
|
||||||
exoPlayer.seekTo(it)
|
exoPlayer.seekTo(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user