Add PlayerData class.

This commit is contained in:
Isira Seneviratne 2023-06-17 07:57:56 +05:30
parent 829ec7eb57
commit 5bb4f303b0
9 changed files with 81 additions and 76 deletions

View File

@ -2,14 +2,12 @@ package com.github.libretube.constants
object IntentData { object IntentData {
const val downloadData = "downloadData" const val downloadData = "downloadData"
const val playerData = "playerData"
const val videoId = "videoId" const val videoId = "videoId"
const val channelId = "channelId" const val channelId = "channelId"
const val channelName = "channelName" const val channelName = "channelName"
const val playlistId = "playlistId" const val playlistId = "playlistId"
const val timeStamp = "timeStamp" const val timeStamp = "timeStamp"
const val position = "position"
const val fileName = "fileName"
const val keepQueue = "keepQueue"
const val playlistType = "playlistType" const val playlistType = "playlistType"
const val downloading = "downloading" const val downloading = "downloading"
const val openAudioPlayer = "openAudioPlayer" const val openAudioPlayer = "openAudioPlayer"

View File

@ -0,0 +1,9 @@
package com.github.libretube.extensions
import android.os.Bundle
import android.os.Parcelable
import androidx.core.os.BundleCompat
inline fun <reified T : Parcelable> Bundle.parcelable(key: String?): T? {
return BundleCompat.getParcelable(this, key, T::class.java)
}

View File

@ -7,8 +7,8 @@ import androidx.core.content.ContextCompat
import androidx.core.content.getSystemService import androidx.core.content.getSystemService
import androidx.fragment.app.commit import androidx.fragment.app.commit
import com.github.libretube.constants.IntentData import com.github.libretube.constants.IntentData
import com.github.libretube.parcelable.PlayerData
import com.github.libretube.services.OnlinePlayerService import com.github.libretube.services.OnlinePlayerService
import com.github.libretube.ui.activities.MainActivity
import com.github.libretube.ui.fragments.PlayerFragment import com.github.libretube.ui.fragments.PlayerFragment
/** /**
@ -23,10 +23,10 @@ object BackgroundHelper {
fun playOnBackground( fun playOnBackground(
context: Context, context: Context,
videoId: String, videoId: String,
position: Long? = null, position: Long = 0,
playlistId: String? = null, playlistId: String? = null,
channelId: String? = null, channelId: String? = null,
keepQueue: Boolean? = null, keepQueue: Boolean = false,
keepVideoPlayerAlive: Boolean = false, keepVideoPlayerAlive: Boolean = false,
) { ) {
// close the previous video player if open // close the previous video player if open
@ -38,12 +38,9 @@ object BackgroundHelper {
} }
// create an intent for the background mode service // create an intent for the background mode service
val playerData = PlayerData(videoId, playlistId, channelId, keepQueue, position)
val intent = Intent(context, OnlinePlayerService::class.java) val intent = Intent(context, OnlinePlayerService::class.java)
.putExtra(IntentData.videoId, videoId) .putExtra(IntentData.playerData, playerData)
.putExtra(IntentData.playlistId, playlistId)
.putExtra(IntentData.channelId, channelId)
.putExtra(IntentData.position, position)
.putExtra(IntentData.keepQueue, keepQueue)
// start the background mode as foreground service // start the background mode as foreground service
ContextCompat.startForegroundService(context, intent) ContextCompat.startForegroundService(context, intent)

View File

@ -16,6 +16,7 @@ import com.github.libretube.constants.IntentData
import com.github.libretube.constants.PreferenceKeys import com.github.libretube.constants.PreferenceKeys
import com.github.libretube.enums.PlaylistType import com.github.libretube.enums.PlaylistType
import com.github.libretube.extensions.toID import com.github.libretube.extensions.toID
import com.github.libretube.parcelable.PlayerData
import com.github.libretube.ui.fragments.AudioPlayerFragment import com.github.libretube.ui.fragments.AudioPlayerFragment
import com.github.libretube.ui.fragments.PlayerFragment import com.github.libretube.ui.fragments.PlayerFragment
import com.github.libretube.ui.views.SingleViewTouchableMotionLayout import com.github.libretube.ui.views.SingleViewTouchableMotionLayout
@ -53,7 +54,7 @@ object NavigationHelper {
playlistId: String? = null, playlistId: String? = null,
channelId: String? = null, channelId: String? = null,
keepQueue: Boolean = false, keepQueue: Boolean = false,
timeStamp: Long? = null, timestamp: Long = 0,
forceVideo: Boolean = false, forceVideo: Boolean = false,
) { ) {
if (videoId == null) return if (videoId == null) return
@ -63,7 +64,7 @@ object NavigationHelper {
BackgroundHelper.playOnBackground( BackgroundHelper.playOnBackground(
context, context,
videoId.toID(), videoId.toID(),
timeStamp, timestamp,
playlistId, playlistId,
channelId, channelId,
keepQueue, keepQueue,
@ -74,13 +75,8 @@ object NavigationHelper {
return return
} }
val bundle = bundleOf( val playerData = PlayerData(videoId.toID(), playlistId, channelId, keepQueue, timestamp)
IntentData.videoId to videoId.toID(), val bundle = bundleOf(IntentData.playerData to playerData)
IntentData.playlistId to playlistId,
IntentData.channelId to channelId,
IntentData.keepQueue to keepQueue,
IntentData.timeStamp to timeStamp,
)
val activity = ContextHelper.unwrapActivity(context) val activity = ContextHelper.unwrapActivity(context)
activity.supportFragmentManager.commitNow { activity.supportFragmentManager.commitNow {

View File

@ -0,0 +1,13 @@
package com.github.libretube.parcelable
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
@Parcelize
data class PlayerData(
val videoId: String,
val playlistId: String? = null,
val channelId: String? = null,
val keepQueue: Boolean = false,
val timestamp: Long = 0
) : Parcelable

View File

@ -6,7 +6,6 @@ import android.os.Binder
import android.os.Handler import android.os.Handler
import android.os.IBinder import android.os.IBinder
import android.os.Looper import android.os.Looper
import android.util.Log
import android.widget.Toast import android.widget.Toast
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import androidx.core.app.ServiceCompat import androidx.core.app.ServiceCompat
@ -28,7 +27,7 @@ import com.github.libretube.constants.IntentData
import com.github.libretube.constants.PLAYER_NOTIFICATION_ID 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.parcelableExtra
import com.github.libretube.extensions.setMetadata import com.github.libretube.extensions.setMetadata
import com.github.libretube.extensions.toID import com.github.libretube.extensions.toID
import com.github.libretube.helpers.PlayerHelper import com.github.libretube.helpers.PlayerHelper
@ -36,6 +35,7 @@ import com.github.libretube.helpers.PlayerHelper.checkForSegments
import com.github.libretube.helpers.PlayerHelper.loadPlaybackParams import com.github.libretube.helpers.PlayerHelper.loadPlaybackParams
import com.github.libretube.helpers.ProxyHelper import com.github.libretube.helpers.ProxyHelper
import com.github.libretube.obj.PlayerNotificationData import com.github.libretube.obj.PlayerNotificationData
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.CoroutineScope
@ -117,27 +117,24 @@ class OnlinePlayerService : LifecycleService() {
* Initializes the [player] with the [MediaItem]. * Initializes the [player] with the [MediaItem].
*/ */
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
try {
// reset the playing queue listeners // reset the playing queue listeners
PlayingQueue.resetToDefaults() PlayingQueue.resetToDefaults()
intent?.parcelableExtra<PlayerData>(IntentData.playerData)?.let { playerData ->
// get the intent arguments // get the intent arguments
videoId = intent?.getStringExtra(IntentData.videoId)!! videoId = playerData.videoId
playlistId = intent.getStringExtra(IntentData.playlistId) playlistId = playerData.playlistId
val position = intent.getLongExtra(IntentData.position, 0L)
val keepQueue = intent.getBooleanExtra(IntentData.keepQueue, false)
// play the audio in the background // play the audio in the background
loadAudio(videoId, position, keepQueue) loadAudio(playerData)
PlayingQueue.setOnQueueTapListener { streamItem -> PlayingQueue.setOnQueueTapListener { streamItem ->
streamItem.url?.toID()?.let { playNextVideo(it) } streamItem.url?.toID()?.let { playNextVideo(it) }
} }
if (PlayerHelper.watchPositionsAudio) updateWatchPosition() if (PlayerHelper.watchPositionsAudio) {
} catch (e: Exception) { updateWatchPosition()
Log.e(TAG(), e.toString()) }
onDestroy()
} }
return super.onStartCommand(intent, flags, startId) return super.onStartCommand(intent, flags, startId)
} }
@ -158,15 +155,10 @@ class OnlinePlayerService : LifecycleService() {
/** /**
* Gets the video data and prepares the [player]. * Gets the video data and prepares the [player].
* @param videoId The id of the video to play
* @param seekToPosition The position of the video to seek to
* @param keepQueue Whether to keep the queue or clear it instead
*/ */
private fun loadAudio( private fun loadAudio(playerData: PlayerData) {
videoId: String, val (videoId, _, _, keepQueue, timestamp) = playerData
seekToPosition: Long = 0,
keepQueue: Boolean = false,
) {
lifecycleScope.launch(Dispatchers.IO) { lifecycleScope.launch(Dispatchers.IO) {
streams = runCatching { streams = runCatching {
RetrofitInstance.api.getStreams(videoId) RetrofitInstance.api.getStreams(videoId)
@ -183,7 +175,7 @@ class OnlinePlayerService : LifecycleService() {
} }
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
playAudio(seekToPosition) playAudio(timestamp)
} }
} }
} }
@ -292,7 +284,7 @@ class OnlinePlayerService : LifecycleService() {
this.videoId = nextVideo this.videoId = nextVideo
this.streams = null this.streams = null
this.segments = emptyList() this.segments = emptyList()
loadAudio(videoId, keepQueue = true) loadAudio(PlayerData(videoId, keepQueue = true))
} }
/** /**

View File

@ -28,7 +28,6 @@ import com.github.libretube.constants.IntentData
import com.github.libretube.constants.PreferenceKeys import com.github.libretube.constants.PreferenceKeys
import com.github.libretube.databinding.ActivityMainBinding import com.github.libretube.databinding.ActivityMainBinding
import com.github.libretube.extensions.toID import com.github.libretube.extensions.toID
import com.github.libretube.helpers.BackgroundHelper
import com.github.libretube.helpers.NavBarHelper import com.github.libretube.helpers.NavBarHelper
import com.github.libretube.helpers.NavigationHelper import com.github.libretube.helpers.NavigationHelper
import com.github.libretube.helpers.NetworkHelper import com.github.libretube.helpers.NetworkHelper
@ -440,7 +439,7 @@ class MainActivity : BaseActivity() {
NavigationHelper.navigateVideo( NavigationHelper.navigateVideo(
context = this, context = this,
videoId = it, videoId = it,
timeStamp = intent?.getLongExtra(IntentData.timeStamp, 0L), timestamp = intent.getLongExtra(IntentData.timeStamp, 0L),
) )
} }

View File

@ -138,7 +138,7 @@ class AudioPlayerFragment : Fragment(), AudioPlayerOptions {
NavigationHelper.navigateVideo( NavigationHelper.navigateVideo(
context = requireContext(), context = requireContext(),
videoId = PlayingQueue.getCurrent()?.url?.toID(), videoId = PlayingQueue.getCurrent()?.url?.toID(),
timeStamp = playerService?.player?.currentPosition?.div(1000), timestamp = playerService?.player?.currentPosition?.div(1000) ?: 0,
keepQueue = true, keepQueue = true,
forceVideo = true, forceVideo = true,
) )

View File

@ -70,6 +70,7 @@ 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
import com.github.libretube.extensions.hideKeyboard import com.github.libretube.extensions.hideKeyboard
import com.github.libretube.extensions.parcelable
import com.github.libretube.extensions.setMetadata import com.github.libretube.extensions.setMetadata
import com.github.libretube.extensions.toID import com.github.libretube.extensions.toID
import com.github.libretube.extensions.toastFromMainDispatcher import com.github.libretube.extensions.toastFromMainDispatcher
@ -86,6 +87,7 @@ import com.github.libretube.helpers.ProxyHelper
import com.github.libretube.obj.PlayerNotificationData import com.github.libretube.obj.PlayerNotificationData
import com.github.libretube.obj.ShareData import com.github.libretube.obj.ShareData
import com.github.libretube.obj.VideoResolution import com.github.libretube.obj.VideoResolution
import com.github.libretube.parcelable.PlayerData
import com.github.libretube.services.DownloadService import com.github.libretube.services.DownloadService
import com.github.libretube.ui.activities.MainActivity import com.github.libretube.ui.activities.MainActivity
import com.github.libretube.ui.adapters.ChaptersAdapter import com.github.libretube.ui.adapters.ChaptersAdapter
@ -137,11 +139,12 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
/** /**
* Video information passed by the intent * Video information passed by the intent
*/ */
private var videoId: String? = null private lateinit var playerData: PlayerData
private lateinit var videoId: String
private var playlistId: String? = null private var playlistId: String? = null
private var channelId: String? = null private var channelId: String? = null
private var keepQueue: Boolean = false private var keepQueue: Boolean = false
private var timeStamp: Long? = null private var timeStamp: Long = 0
/** /**
* Video information fetched at runtime * Video information fetched at runtime
@ -234,13 +237,12 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
arguments?.let { val playerData = requireArguments().parcelable<PlayerData>(IntentData.playerData)!!
videoId = it.getString(IntentData.videoId)!!.toID() videoId = playerData.videoId
playlistId = it.getString(IntentData.playlistId) playlistId = playerData.playlistId
channelId = it.getString(IntentData.channelId) channelId = playerData.channelId
keepQueue = it.getBoolean(IntentData.keepQueue, false) keepQueue = playerData.keepQueue
timeStamp = it.getLong(IntentData.timeStamp, 0L) timeStamp = playerData.timestamp
}
// broadcast receiver for PiP actions // broadcast receiver for PiP actions
context?.registerReceiver( context?.registerReceiver(
@ -390,11 +392,10 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
} }
binding.commentsToggle.setOnClickListener { binding.commentsToggle.setOnClickListener {
videoId ?: return@setOnClickListener
// set the max height to not cover the currently playing video // set the max height to not cover the currently playing video
commentsViewModel.handleLink = this::handleLink commentsViewModel.handleLink = this::handleLink
commentsViewModel.maxHeight = binding.root.height - binding.player.height commentsViewModel.maxHeight = binding.root.height - binding.player.height
commentsViewModel.videoId = videoId commentsViewModel.videoId = playerData.videoId
CommentsSheet().show(childFragmentManager) CommentsSheet().show(childFragmentManager)
} }
@ -434,7 +435,7 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
if (!this::streams.isInitialized) return@setOnClickListener if (!this::streams.isInitialized) return@setOnClickListener
val shareDialog = val shareDialog =
ShareDialog( ShareDialog(
videoId!!, videoId,
ShareObjectType.VIDEO, ShareObjectType.VIDEO,
ShareData( ShareData(
currentVideo = streams.title, currentVideo = streams.title,
@ -487,7 +488,7 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
BackgroundHelper.stopBackgroundPlay(requireContext()) BackgroundHelper.stopBackgroundPlay(requireContext())
BackgroundHelper.playOnBackground( BackgroundHelper.playOnBackground(
requireContext(), requireContext(),
videoId!!, videoId,
exoPlayer.currentPosition, exoPlayer.currentPosition,
playlistId, playlistId,
channelId, channelId,
@ -648,7 +649,7 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
C.TIME_UNSET C.TIME_UNSET
) )
) return ) return
val watchPosition = WatchPosition(videoId!!, exoPlayer.currentPosition) val watchPosition = WatchPosition(videoId, exoPlayer.currentPosition)
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
Database.watchPositionDao().insert(watchPosition) Database.watchPositionDao().insert(watchPosition)
} }
@ -685,7 +686,7 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
lifecycleScope.launch(Dispatchers.IO) { lifecycleScope.launch(Dispatchers.IO) {
streams = try { streams = try {
RetrofitInstance.api.getStreams(videoId!!) RetrofitInstance.api.getStreams(videoId)
} catch (e: IOException) { } catch (e: IOException) {
context?.toastFromMainDispatcher(R.string.unknown_error, Toast.LENGTH_LONG) context?.toastFromMainDispatcher(R.string.unknown_error, Toast.LENGTH_LONG)
return@launch return@launch
@ -700,18 +701,18 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
if (PlayingQueue.isEmpty()) { if (PlayingQueue.isEmpty()) {
lifecycleScope.launch(Dispatchers.IO) { lifecycleScope.launch(Dispatchers.IO) {
if (playlistId != null) { if (playlistId != null) {
PlayingQueue.insertPlaylist(playlistId!!, streams.toStreamItem(videoId!!)) PlayingQueue.insertPlaylist(playlistId!!, streams.toStreamItem(videoId))
} else if (channelId != null) { } else if (channelId != null) {
PlayingQueue.insertChannel(channelId!!, streams.toStreamItem(videoId!!)) PlayingQueue.insertChannel(channelId!!, streams.toStreamItem(videoId))
} else { } else {
PlayingQueue.updateCurrent(streams.toStreamItem(videoId!!)) PlayingQueue.updateCurrent(streams.toStreamItem(videoId))
if (PlayerHelper.autoInsertRelatedVideos) { if (PlayerHelper.autoInsertRelatedVideos) {
PlayingQueue.add(*streams.relatedStreams.toTypedArray()) PlayingQueue.add(*streams.relatedStreams.toTypedArray())
} }
} }
} }
} else { } else {
PlayingQueue.updateCurrent(streams.toStreamItem(videoId!!)) PlayingQueue.updateCurrent(streams.toStreamItem(videoId))
} }
if (PreferenceHelper.getBoolean(PreferenceKeys.AUTO_FULLSCREEN_SHORTS, false)) { if (PreferenceHelper.getBoolean(PreferenceKeys.AUTO_FULLSCREEN_SHORTS, false)) {
@ -761,7 +762,7 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
// add the video to the watch history // add the video to the watch history
if (PlayerHelper.watchHistoryEnabled) { if (PlayerHelper.watchHistoryEnabled) {
DatabaseHelper.addToWatchHistory(videoId!!, streams) DatabaseHelper.addToWatchHistory(videoId, streams)
} }
} }
} }
@ -777,7 +778,7 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
if (categories.isEmpty()) return@runCatching if (categories.isEmpty()) return@runCatching
segments = segments =
RetrofitInstance.api.getSegments( RetrofitInstance.api.getSegments(
videoId!!, videoId,
JsonHelper.json.encodeToString(categories), JsonHelper.json.encodeToString(categories),
).segments ).segments
if (segments.isEmpty()) return@runCatching if (segments.isEmpty()) return@runCatching
@ -816,7 +817,7 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
// browse the watch positions // browse the watch positions
val position = try { val position = try {
runBlocking { runBlocking {
Database.watchPositionDao().findById(videoId!!)?.position Database.watchPositionDao().findById(videoId)?.position
} }
} catch (e: Exception) { } catch (e: Exception) {
return return
@ -832,11 +833,11 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
*/ */
private fun trySeekToTimeStamp() { private fun trySeekToTimeStamp() {
// support for time stamped links // support for time stamped links
timeStamp?.let { if (timeStamp != 0L) {
if (it != 0L) exoPlayer.seekTo(it * 1000) exoPlayer.seekTo(timeStamp * 1000)
} }
// delete the time stamp because it already got consumed // delete the time stamp because it already got consumed
timeStamp = null timeStamp = 0
} }
// used for autoplay and skipping to next video // used for autoplay and skipping to next video
@ -1001,7 +1002,7 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
if (streams.duration <= 0) { if (streams.duration <= 0) {
Toast.makeText(context, R.string.cannotDownload, Toast.LENGTH_SHORT).show() Toast.makeText(context, R.string.cannotDownload, Toast.LENGTH_SHORT).show()
} else if (!DownloadService.IS_DOWNLOAD_RUNNING) { } else if (!DownloadService.IS_DOWNLOAD_RUNNING) {
val newFragment = DownloadDialog(videoId!!) val newFragment = DownloadDialog(videoId)
newFragment.show(childFragmentManager, DownloadDialog::class.java.name) newFragment.show(childFragmentManager, DownloadDialog::class.java.name)
} else { } else {
Toast.makeText(context, R.string.dlisinprogress, Toast.LENGTH_SHORT) Toast.makeText(context, R.string.dlisinprogress, Toast.LENGTH_SHORT)
@ -1034,7 +1035,7 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
) )
binding.relPlayerSave.setOnClickListener { binding.relPlayerSave.setOnClickListener {
AddToPlaylistDialog(videoId!!).show( AddToPlaylistDialog(videoId).show(
childFragmentManager, childFragmentManager,
AddToPlaylistDialog::class.java.name, AddToPlaylistDialog::class.java.name,
) )
@ -1379,7 +1380,7 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
streams.uploader, streams.uploader,
streams.thumbnailUrl, streams.thumbnailUrl,
) )
nowPlayingNotification.updatePlayerNotification(videoId!!, playerNotificationData) nowPlayingNotification.updatePlayerNotification(videoId, playerNotificationData)
} }
/** /**
@ -1458,7 +1459,7 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
override fun onStatsClicked() { override fun onStatsClicked() {
if (!this::streams.isInitialized) return if (!this::streams.isInitialized) return
StatsDialog(exoPlayer, videoId ?: return) StatsDialog(exoPlayer, videoId)
.show(childFragmentManager, null) .show(childFragmentManager, null)
} }