Merge pull request #354 from Bnyro/structure

Player Fragment Structure + BackBuffer Cache
This commit is contained in:
Bnyro 2022-06-03 18:45:57 +02:00 committed by GitHub
commit 962cb891a8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 455 additions and 398 deletions

View File

@ -1,8 +1,12 @@
package com.github.libretube.dialogs package com.github.libretube.dialogs
import android.Manifest
import android.app.Dialog import android.app.Dialog
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.os.Environment
import android.util.Log import android.util.Log
import android.util.TypedValue import android.util.TypedValue
import android.view.View import android.view.View
@ -13,14 +17,18 @@ import android.widget.RadioButton
import android.widget.RadioGroup import android.widget.RadioGroup
import android.widget.Spinner import android.widget.Spinner
import android.widget.TextView import android.widget.TextView
import androidx.core.app.ActivityCompat
import androidx.core.text.HtmlCompat import androidx.core.text.HtmlCompat
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import com.github.libretube.DownloadService import com.github.libretube.DownloadService
import com.github.libretube.MainActivity
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.obj.Streams
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
class DownloadDialog : DialogFragment() { class DownloadDialog : DialogFragment() {
private val TAG = "DownloadDialog" private val TAG = "DownloadDialog"
var streams: Streams = Streams()
var vidName = arrayListOf<String>() var vidName = arrayListOf<String>()
var vidUrl = arrayListOf<String>() var vidUrl = arrayListOf<String>()
var audioName = arrayListOf<String>() var audioName = arrayListOf<String>()
@ -32,12 +40,62 @@ class DownloadDialog : DialogFragment() {
private lateinit var videoId: String private lateinit var videoId: String
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return activity?.let { return activity?.let {
vidName = arguments?.getStringArrayList("videoName") as ArrayList<String> streams = arguments?.getParcelable("streams")!!
vidUrl = arguments?.getStringArrayList("videoUrl") as ArrayList<String> videoId = arguments?.getString("video_id")!!
audioName = arguments?.getStringArrayList("audioName") as ArrayList<String>
audioUrl = arguments?.getStringArrayList("audioUrl") as ArrayList<String> val mainActivity = activity as MainActivity
duration = arguments?.getInt("duration")!! Log.e(TAG, "download button clicked!")
videoId = arguments?.getString("videoId")!! if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
Log.d("myz", "" + Build.VERSION.SDK_INT)
if (!Environment.isExternalStorageManager()) {
ActivityCompat.requestPermissions(
mainActivity,
arrayOf(
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.MANAGE_EXTERNAL_STORAGE
),
1
) // permission request code is just an int
}
} else {
if (ActivityCompat.checkSelfPermission(
requireContext(),
Manifest.permission.READ_EXTERNAL_STORAGE
) != PackageManager.PERMISSION_GRANTED ||
ActivityCompat.checkSelfPermission(
requireContext(),
Manifest.permission.WRITE_EXTERNAL_STORAGE
) != PackageManager.PERMISSION_GRANTED
) {
ActivityCompat.requestPermissions(
mainActivity,
arrayOf(
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
),
1
)
}
}
var vidName = arrayListOf<String>()
vidName.add("No video")
var vidUrl = arrayListOf<String>()
vidUrl.add("")
for (vid in streams?.videoStreams!!) {
val name = vid.quality + " " + vid.format
vidName.add(name)
vidUrl.add(vid.url!!)
}
var audioName = arrayListOf<String>()
audioName.add("No audio")
var audioUrl = arrayListOf<String>()
audioUrl.add("")
for (audio in streams?.audioStreams!!) {
val name = audio.quality + " " + audio.format
audioName.add(name)
audioUrl.add(audio.url!!)
}
val builder = MaterialAlertDialogBuilder(it) val builder = MaterialAlertDialogBuilder(it)
// Get the layout inflater // Get the layout inflater
val inflater = requireActivity().layoutInflater val inflater = requireActivity().layoutInflater
@ -115,12 +173,4 @@ class DownloadDialog : DialogFragment() {
builder.create() builder.create()
} ?: throw IllegalStateException("Activity cannot be null") } ?: throw IllegalStateException("Activity cannot be null")
} }
override fun onDestroy() {
vidName.clear()
vidUrl.clear()
audioUrl.clear()
audioName.clear()
super.onDestroy()
}
} }

View File

@ -1,19 +1,16 @@
package com.github.libretube.fragments package com.github.libretube.fragments
import android.Manifest
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.NotificationManager import android.app.NotificationManager
import android.content.Context import android.content.Context
import android.content.DialogInterface import android.content.DialogInterface
import android.content.Intent import android.content.Intent
import android.content.pm.ActivityInfo import android.content.pm.ActivityInfo
import android.content.pm.PackageManager
import android.graphics.Rect import android.graphics.Rect
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Build.VERSION.SDK_INT import android.os.Build.VERSION.SDK_INT
import android.os.Bundle import android.os.Bundle
import android.os.Environment
import android.support.v4.media.session.MediaSessionCompat import android.support.v4.media.session.MediaSessionCompat
import android.text.Html import android.text.Html
import android.text.TextUtils import android.text.TextUtils
@ -34,7 +31,6 @@ import android.widget.TextView
import android.widget.Toast import android.widget.Toast
import androidx.constraintlayout.motion.widget.MotionLayout import androidx.constraintlayout.motion.widget.MotionLayout
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.app.ActivityCompat
import androidx.core.net.toUri import androidx.core.net.toUri
import androidx.core.os.bundleOf import androidx.core.os.bundleOf
import androidx.core.view.isVisible import androidx.core.view.isVisible
@ -63,6 +59,7 @@ import com.github.libretube.obj.Subscribe
import com.github.libretube.util.CronetHelper import com.github.libretube.util.CronetHelper
import com.github.libretube.util.RetrofitInstance import com.github.libretube.util.RetrofitInstance
import com.google.android.exoplayer2.C import com.google.android.exoplayer2.C
import com.google.android.exoplayer2.DefaultLoadControl
import com.google.android.exoplayer2.ExoPlayer import com.google.android.exoplayer2.ExoPlayer
import com.google.android.exoplayer2.MediaItem import com.google.android.exoplayer2.MediaItem
import com.google.android.exoplayer2.MediaItem.SubtitleConfiguration import com.google.android.exoplayer2.MediaItem.SubtitleConfiguration
@ -97,8 +94,6 @@ class PlayerFragment : Fragment() {
private val TAG = "PlayerFragment" private val TAG = "PlayerFragment"
private var videoId: String? = null private var videoId: String? = null
private var param2: String? = null
private var lastProgress: Float = 0.toFloat()
private var sId: Int = 0 private var sId: Int = 0
private var eId: Int = 0 private var eId: Int = 0
private var paused = false private var paused = false
@ -116,7 +111,6 @@ class PlayerFragment : Fragment() {
private lateinit var exoPlayerView: StyledPlayerView private lateinit var exoPlayerView: StyledPlayerView
private lateinit var motionLayout: MotionLayout private lateinit var motionLayout: MotionLayout
private lateinit var exoPlayer: ExoPlayer private lateinit var exoPlayer: ExoPlayer
private lateinit var mediaSource: MediaSource
private lateinit var segmentData: Segments private lateinit var segmentData: Segments
private lateinit var relDownloadVideo: LinearLayout private lateinit var relDownloadVideo: LinearLayout
@ -154,6 +148,7 @@ class PlayerFragment : Fragment() {
val playerMotionLayout = view.findViewById<MotionLayout>(R.id.playerMotionLayout) val playerMotionLayout = view.findViewById<MotionLayout>(R.id.playerMotionLayout)
motionLayout = playerMotionLayout motionLayout = playerMotionLayout
exoPlayerView = view.findViewById(R.id.player) exoPlayerView = view.findViewById(R.id.player)
view.findViewById<TextView>(R.id.player_description).text = videoId view.findViewById<TextView>(R.id.player_description).text = videoId
playerMotionLayout.addTransitionListener(object : MotionLayout.TransitionListener { playerMotionLayout.addTransitionListener(object : MotionLayout.TransitionListener {
override fun onTransitionStarted( override fun onTransitionStarted(
@ -203,6 +198,7 @@ class PlayerFragment : Fragment() {
) { ) {
} }
}) })
playerMotionLayout.progress = 1.toFloat() playerMotionLayout.progress = 1.toFloat()
playerMotionLayout.transitionToStart() playerMotionLayout.transitionToStart()
fetchJson(view) fetchJson(view)
@ -328,14 +324,6 @@ class PlayerFragment : Fragment() {
GridLayoutManager(view.context, resources.getInteger(R.integer.grid_items)) GridLayoutManager(view.context, resources.getInteger(R.integer.grid_items))
} }
override fun onStop() {
try {
// exoPlayer.pause() // breaks background play
} catch (e: Exception) {
}
super.onStop()
}
override fun onDestroy() { override fun onDestroy() {
super.onDestroy() super.onDestroy()
try { try {
@ -423,17 +411,20 @@ class PlayerFragment : Fragment() {
} }
} }
} }
runOnUiThread {
createExoPlayer(view)
prepareExoPlayerView()
setResolutionAndSubtitles(view, response)
exoPlayer.prepare()
exoPlayer.play()
initializePlayerView(view, response) initializePlayerView(view, response)
} }
} }
}
run() run()
} }
private fun initializePlayerView(view: View, response: Streams) { private fun prepareExoPlayerView() {
isLoading = false
runOnUiThread {
createExoPlayer(view)
exoPlayerView.setShowSubtitleButton(true) exoPlayerView.setShowSubtitleButton(true)
exoPlayerView.setShowNextButton(false) exoPlayerView.setShowNextButton(false)
exoPlayerView.setShowPreviousButton(false) exoPlayerView.setShowPreviousButton(false)
@ -441,7 +432,146 @@ class PlayerFragment : Fragment() {
// exoPlayerView.controllerShowTimeoutMs = 1500 // exoPlayerView.controllerShowTimeoutMs = 1500
exoPlayerView.controllerHideOnTouch = true exoPlayerView.controllerHideOnTouch = true
exoPlayerView.player = exoPlayer exoPlayerView.player = exoPlayer
}
private fun initializePlayerView(view: View, response: Streams) {
view.findViewById<TextView>(R.id.player_views_info).text =
response.views.formatShort() + " views • " + response.uploadDate
view.findViewById<TextView>(R.id.textLike).text = response.likes.formatShort()
val channelImage = view.findViewById<ImageView>(R.id.player_channelImage)
Picasso.get().load(response.uploaderAvatar).into(channelImage)
view.findViewById<TextView>(R.id.player_channelName).text = response.uploader
view.findViewById<TextView>(R.id.title_textView).text = response.title
view.findViewById<TextView>(R.id.player_title).text = response.title
view.findViewById<TextView>(R.id.player_description).text = response.description
// Listener for play and pause icon change
exoPlayer.addListener(object : com.google.android.exoplayer2.Player.Listener {
override fun onIsPlayingChanged(isPlaying: Boolean) {
if (isPlaying && SponsorBlockSettings.sponsorBlockEnabled) {
exoPlayerView.postDelayed(
this@PlayerFragment::checkForSegments,
100
)
}
}
override fun onPlayerStateChanged(
playWhenReady: Boolean,
playbackState: Int
) {
exoPlayerView.keepScreenOn = !(
playbackState == Player.STATE_IDLE ||
playbackState == Player.STATE_ENDED ||
!playWhenReady
)
if (playWhenReady && playbackState == Player.STATE_READY) {
// media actually playing
view.findViewById<ImageView>(R.id.play_imageView)
.setImageResource(R.drawable.ic_pause)
} else if (playWhenReady) {
// might be idle (plays after prepare()),
// buffering (plays when data available)
// or ended (plays when seek away from end)
view.findViewById<ImageView>(R.id.play_imageView)
.setImageResource(R.drawable.ic_play)
} else {
// player paused in any state
view.findViewById<ImageView>(R.id.play_imageView)
.setImageResource(R.drawable.ic_play)
}
}
})
// share button
view.findViewById<LinearLayout>(R.id.relPlayer_share).setOnClickListener {
showShareDialog(requireContext(), videoId!!)
}
// check if livestream
if (response.duration!! > 0) {
// download clicked
relDownloadVideo.setOnClickListener {
if (!IS_DOWNLOAD_RUNNING) {
val newFragment = DownloadDialog()
var bundle = Bundle()
bundle.putString("video_id", videoId)
bundle.putParcelable("streams", response)
newFragment.arguments = bundle
newFragment.show(childFragmentManager, "Download")
} else {
Toast.makeText(context, R.string.dlisinprogress, Toast.LENGTH_SHORT)
.show()
}
}
} else {
Toast.makeText(context, R.string.cannotDownload, Toast.LENGTH_SHORT).show()
}
if (response.hls != null) {
view.findViewById<LinearLayout>(R.id.relPlayer_vlc).setOnClickListener {
exoPlayer.pause()
try {
val vlcRequestCode = 42
val uri: Uri = Uri.parse(response.hls)
val vlcIntent = Intent(Intent.ACTION_VIEW)
vlcIntent.setPackage("org.videolan.vlc")
vlcIntent.setDataAndTypeAndNormalize(uri, "video/*")
vlcIntent.putExtra("title", response.title)
vlcIntent.putExtra("from_start", false)
vlcIntent.putExtra("position", exoPlayer.currentPosition)
startActivityForResult(vlcIntent, vlcRequestCode)
} catch (e: Exception) {
Toast.makeText(context, R.string.vlcerror, Toast.LENGTH_SHORT)
.show()
}
}
}
relatedRecView.adapter = TrendingAdapter(
response.relatedStreams!!,
childFragmentManager
)
val description = response.description!!
view.findViewById<TextView>(R.id.player_description).text =
// detect whether the description is html formatted
if (description.contains("<") && description.contains(">")) {
if (SDK_INT >= Build.VERSION_CODES.N) {
Html.fromHtml(description, Html.FROM_HTML_MODE_COMPACT)
.trim()
} else {
Html.fromHtml(description).trim()
}
} else {
description
}
view.findViewById<RelativeLayout>(R.id.player_channel).setOnClickListener {
val activity = view.context as MainActivity
val bundle = bundleOf("channel_id" to response.uploaderUrl)
activity.navController.navigate(R.id.channel, bundle)
activity.findViewById<MotionLayout>(R.id.mainMotionLayout).transitionToEnd()
view.findViewById<MotionLayout>(R.id.playerMotionLayout).transitionToEnd()
}
val sharedPref = context?.getSharedPreferences("token", Context.MODE_PRIVATE)
if (sharedPref?.getString("token", "") != "") {
val channelId = response.uploaderUrl?.replace("/channel/", "")
val subButton = view.findViewById<MaterialButton>(R.id.player_subscribe)
isSubscribed(subButton, channelId!!)
view.findViewById<LinearLayout>(R.id.save).setOnClickListener {
val newFragment = AddtoPlaylistDialog()
var bundle = Bundle()
bundle.putString("videoId", videoId)
newFragment.arguments = bundle
newFragment.show(childFragmentManager, "AddToPlaylist")
}
}
}
private fun setResolutionAndSubtitles(view: View, response: Streams) {
var videosNameArray: Array<CharSequence> = arrayOf() var videosNameArray: Array<CharSequence> = arrayOf()
videosNameArray += "HLS" videosNameArray += "HLS"
for (vid in response.videoStreams!!) { for (vid in response.videoStreams!!) {
@ -462,11 +592,9 @@ class PlayerFragment : Fragment() {
val defres = sharedPreferences.getString("default_res", "")!! val defres = sharedPreferences.getString("default_res", "")!!
when { when {
defres != "" -> { defres != "" -> {
var foundRes = false
run lit@{ run lit@{
response.videoStreams.forEachIndexed { index, pipedStream -> response.videoStreams.forEachIndexed { index, pipedStream ->
if (pipedStream.quality!!.contains(defres)) { if (pipedStream.quality!!.contains(defres)) {
foundRes = true
val dataSourceFactory: DataSource.Factory = val dataSourceFactory: DataSource.Factory =
DefaultHttpDataSource.Factory() DefaultHttpDataSource.Factory()
val videoItem: MediaItem = MediaItem.Builder() val videoItem: MediaItem = MediaItem.Builder()
@ -555,14 +683,6 @@ class PlayerFragment : Fragment() {
} }
} }
// /exoPlayer.getMediaItemAt(5)
exoPlayer.prepare()
exoPlayer.play()
view.findViewById<TextView>(R.id.title_textView).text = response.title
view.findViewById<TextView>(R.id.player_title).text = response.title
view.findViewById<TextView>(R.id.player_description).text = response.description
view.findViewById<ImageButton>(R.id.quality_select).setOnClickListener { view.findViewById<ImageButton>(R.id.quality_select).setOnClickListener {
// Dialog for quality selection // Dialog for quality selection
val builder: MaterialAlertDialogBuilder? = activity?.let { val builder: MaterialAlertDialogBuilder? = activity?.let {
@ -635,190 +755,6 @@ class PlayerFragment : Fragment() {
val dialog = builder.create() val dialog = builder.create()
dialog.show() dialog.show()
} }
// Listener for play and pause icon change
exoPlayer.addListener(object : com.google.android.exoplayer2.Player.Listener {
override fun onIsPlayingChanged(isPlaying: Boolean) {
if (isPlaying && SponsorBlockSettings.sponsorBlockEnabled) {
exoPlayerView.postDelayed(
this@PlayerFragment::checkForSegments,
100
)
}
}
override fun onPlayerStateChanged(
playWhenReady: Boolean,
playbackState: Int
) {
exoPlayerView.keepScreenOn = !(
playbackState == Player.STATE_IDLE ||
playbackState == Player.STATE_ENDED ||
!playWhenReady
)
if (playWhenReady && playbackState == Player.STATE_READY) {
// media actually playing
view.findViewById<ImageView>(R.id.play_imageView)
.setImageResource(R.drawable.ic_pause)
} else if (playWhenReady) {
// might be idle (plays after prepare()),
// buffering (plays when data available)
// or ended (plays when seek away from end)
view.findViewById<ImageView>(R.id.play_imageView)
.setImageResource(R.drawable.ic_play)
} else {
// player paused in any state
view.findViewById<ImageView>(R.id.play_imageView)
.setImageResource(R.drawable.ic_play)
}
}
})
relatedRecView.adapter = TrendingAdapter(
response.relatedStreams!!,
childFragmentManager
)
val description = response.description!!
view.findViewById<TextView>(R.id.player_description).text =
// detect whether the description is html formatted
if (description.contains("<") && description.contains(">")) {
if (SDK_INT >= Build.VERSION_CODES.N) {
Html.fromHtml(description, Html.FROM_HTML_MODE_COMPACT)
.trim()
} else {
Html.fromHtml(description).trim()
}
} else {
description
}
view.findViewById<TextView>(R.id.player_views_info).text =
response.views.formatShort() + " views • " + response.uploadDate
view.findViewById<TextView>(R.id.textLike).text = response.likes.formatShort()
val channelImage = view.findViewById<ImageView>(R.id.player_channelImage)
Picasso.get().load(response.uploaderAvatar).into(channelImage)
view.findViewById<TextView>(R.id.player_channelName).text = response.uploader
view.findViewById<RelativeLayout>(R.id.player_channel).setOnClickListener {
val activity = view.context as MainActivity
val bundle = bundleOf("channel_id" to response.uploaderUrl)
activity.navController.navigate(R.id.channel, bundle)
activity.findViewById<MotionLayout>(R.id.mainMotionLayout).transitionToEnd()
view.findViewById<MotionLayout>(R.id.playerMotionLayout).transitionToEnd()
}
val sharedPref = context?.getSharedPreferences("token", Context.MODE_PRIVATE)
if (sharedPref?.getString("token", "") != "") {
val channelId = response.uploaderUrl?.replace("/channel/", "")
val subButton = view.findViewById<MaterialButton>(R.id.player_subscribe)
isSubscribed(subButton, channelId!!)
view.findViewById<LinearLayout>(R.id.save).setOnClickListener {
val newFragment = AddtoPlaylistDialog()
var bundle = Bundle()
bundle.putString("videoId", videoId)
newFragment.arguments = bundle
newFragment.show(childFragmentManager, "AddToPlaylist")
}
}
// share button
view.findViewById<LinearLayout>(R.id.relPlayer_share).setOnClickListener {
showShareDialog(requireContext(), videoId!!)
}
// check if livestream
if (response.duration!! > 0) {
// download clicked
relDownloadVideo.setOnClickListener {
if (!IS_DOWNLOAD_RUNNING) {
val mainActivity = activity as MainActivity
Log.e(TAG, "download button clicked!")
if (SDK_INT >= Build.VERSION_CODES.R) {
Log.d("myz", "" + SDK_INT)
if (!Environment.isExternalStorageManager()) {
ActivityCompat.requestPermissions(
mainActivity,
arrayOf(
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.MANAGE_EXTERNAL_STORAGE
),
1
) // permission request code is just an int
}
} else {
if (ActivityCompat.checkSelfPermission(
requireContext(),
Manifest.permission.READ_EXTERNAL_STORAGE
) != PackageManager.PERMISSION_GRANTED ||
ActivityCompat.checkSelfPermission(
requireContext(),
Manifest.permission.WRITE_EXTERNAL_STORAGE
) != PackageManager.PERMISSION_GRANTED
) {
ActivityCompat.requestPermissions(
mainActivity,
arrayOf(
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
),
1
)
}
}
var vidName = arrayListOf<String>()
vidName.add("No video")
var vidUrl = arrayListOf<String>()
vidUrl.add("")
for (vid in response.videoStreams) {
val name = vid.quality + " " + vid.format
vidName.add(name)
vidUrl.add(vid.url!!)
}
var audioName = arrayListOf<String>()
audioName.add("No audio")
var audioUrl = arrayListOf<String>()
audioUrl.add("")
for (audio in response.audioStreams!!) {
val name = audio.quality + " " + audio.format
audioName.add(name)
audioUrl.add(audio.url!!)
}
val newFragment = DownloadDialog()
var bundle = Bundle()
bundle.putStringArrayList("videoName", vidName)
bundle.putStringArrayList("videoUrl", vidUrl)
bundle.putStringArrayList("audioName", audioName)
bundle.putStringArrayList("audioUrl", audioUrl)
bundle.putString("videoId", videoId)
bundle.putInt("duration", response.duration)
newFragment.arguments = bundle
newFragment.show(childFragmentManager, "Download")
} else {
Toast.makeText(context, R.string.dlisinprogress, Toast.LENGTH_SHORT)
.show()
}
}
} else {
Toast.makeText(context, R.string.cannotDownload, Toast.LENGTH_SHORT).show()
}
if (response.hls != null) {
view.findViewById<LinearLayout>(R.id.relPlayer_vlc).setOnClickListener {
exoPlayer.pause()
try {
val vlcRequestCode = 42
val uri: Uri = Uri.parse(response.hls)
val vlcIntent = Intent(Intent.ACTION_VIEW)
vlcIntent.setPackage("org.videolan.vlc")
vlcIntent.setDataAndTypeAndNormalize(uri, "video/*")
vlcIntent.putExtra("title", response.title)
vlcIntent.putExtra("from_start", false)
vlcIntent.putExtra("position", exoPlayer.currentPosition)
startActivityForResult(vlcIntent, vlcRequestCode)
} catch (e: Exception) {
Toast.makeText(context, R.string.vlcerror, Toast.LENGTH_SHORT)
.show()
}
}
}
}
} }
private fun createExoPlayer(view: View) { private fun createExoPlayer(view: View) {
@ -831,13 +767,21 @@ class PlayerFragment : Fragment() {
cronetDataSourceFactory cronetDataSourceFactory
) )
// handles the audio focus
val audioAttributes = AudioAttributes.Builder() val audioAttributes = AudioAttributes.Builder()
.setUsage(C.USAGE_MEDIA) .setUsage(C.USAGE_MEDIA)
.setContentType(C.CONTENT_TYPE_MOVIE) .setContentType(C.CONTENT_TYPE_MOVIE)
.build() .build()
// handles the duration of media to retain in the buffer prior to the current playback position (for fast backward seeking)
val loadControl = DefaultLoadControl.Builder()
// cache the last three minutes
.setBackBuffer(1000 * 60 * 3, true)
.build()
exoPlayer = ExoPlayer.Builder(view.context) exoPlayer = ExoPlayer.Builder(view.context)
.setMediaSourceFactory(DefaultMediaSourceFactory(dataSourceFactory)) .setMediaSourceFactory(DefaultMediaSourceFactory(dataSourceFactory))
.setLoadControl(loadControl)
.setSeekBackIncrementMs(5000) .setSeekBackIncrementMs(5000)
.setSeekForwardIncrementMs(5000) .setSeekForwardIncrementMs(5000)
.build() .build()
@ -996,6 +940,7 @@ class PlayerFragment : Fragment() {
commentsRecView.adapter = commentsAdapter commentsRecView.adapter = commentsAdapter
nextPage = commentsResponse.nextpage nextPage = commentsResponse.nextpage
commentsLoaded = true commentsLoaded = true
isLoading = false
} }
} }

View File

@ -1,5 +1,7 @@
package com.github.libretube.obj package com.github.libretube.obj
import android.os.Parcel
import android.os.Parcelable
import com.fasterxml.jackson.annotation.JsonIgnoreProperties import com.fasterxml.jackson.annotation.JsonIgnoreProperties
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
@ -26,9 +28,69 @@ data class Streams(
val livestream: Boolean?, val livestream: Boolean?,
val proxyUrl: String?, val proxyUrl: String?,
val chapters: List<ChapterSegment>? val chapters: List<ChapterSegment>?
) { ) : Parcelable {
constructor(parcel: Parcel) : this(
parcel.readString(),
parcel.readString(),
parcel.readString(),
parcel.readString(),
parcel.readString(),
parcel.readString(),
parcel.readString(),
parcel.readString(),
parcel.readString(),
parcel.readString(),
parcel.readValue(Boolean::class.java.classLoader) as? Boolean,
parcel.readValue(Int::class.java.classLoader) as? Int,
parcel.readValue(Long::class.java.classLoader) as? Long,
parcel.readValue(Long::class.java.classLoader) as? Long,
parcel.readValue(Int::class.java.classLoader) as? Int,
TODO("audioStreams"),
TODO("videoStreams"),
TODO("relatedStreams"),
TODO("subtitles"),
parcel.readValue(Boolean::class.java.classLoader) as? Boolean,
parcel.readString(),
TODO("chapters")
) {
}
constructor() : this( constructor() : this(
"", "", "", "", "", "", "", "", "", "", null, -1, -1, -1, -1, emptyList(), emptyList(), "", "", "", "", "", "", "", "", "", "", null, -1, -1, -1, -1, emptyList(), emptyList(),
emptyList(), emptyList(), null, "", emptyList() emptyList(), emptyList(), null, "", emptyList()
) )
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeString(title)
parcel.writeString(description)
parcel.writeString(uploadDate)
parcel.writeString(uploader)
parcel.writeString(uploaderUrl)
parcel.writeString(uploaderAvatar)
parcel.writeString(thumbnailUrl)
parcel.writeString(hls)
parcel.writeString(dash)
parcel.writeString(lbryId)
parcel.writeValue(uploaderVerified)
parcel.writeValue(duration)
parcel.writeValue(views)
parcel.writeValue(likes)
parcel.writeValue(dislikes)
parcel.writeValue(livestream)
parcel.writeString(proxyUrl)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<Streams> {
override fun createFromParcel(parcel: Parcel): Streams {
return Streams(parcel)
}
override fun newArray(size: Int): Array<Streams?> {
return arrayOfNulls(size)
}
}
} }