Merge pull request #1863 from Bnyro/master

Option to change the audio track
This commit is contained in:
Bnyro 2022-11-16 14:15:57 +01:00 committed by GitHub
commit d1f2e3ed27
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 79 additions and 22 deletions

View File

@ -17,5 +17,7 @@ data class PipedStream(
var indexEnd: Int? = null, var indexEnd: Int? = null,
var width: Int? = null, var width: Int? = null,
var height: Int? = null, var height: Int? = null,
var fps: Int? = null var fps: Int? = null,
val audioTrackName: String? = null,
val audioTrackId: String? = null
) )

View File

@ -38,6 +38,8 @@ import com.github.libretube.R
import com.github.libretube.api.CronetHelper import com.github.libretube.api.CronetHelper
import com.github.libretube.api.RetrofitInstance import com.github.libretube.api.RetrofitInstance
import com.github.libretube.api.obj.ChapterSegment import com.github.libretube.api.obj.ChapterSegment
import com.github.libretube.api.obj.PipedStream
import com.github.libretube.api.obj.Segment
import com.github.libretube.api.obj.SegmentData import com.github.libretube.api.obj.SegmentData
import com.github.libretube.api.obj.StreamItem import com.github.libretube.api.obj.StreamItem
import com.github.libretube.api.obj.Streams import com.github.libretube.api.obj.Streams
@ -107,6 +109,7 @@ import kotlinx.coroutines.launch
import org.chromium.net.CronetEngine import org.chromium.net.CronetEngine
import retrofit2.HttpException import retrofit2.HttpException
import java.io.IOException import java.io.IOException
import java.util.*
import java.util.concurrent.Executors import java.util.concurrent.Executors
import kotlin.math.abs import kotlin.math.abs
@ -166,6 +169,9 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
private lateinit var shareData: ShareData private lateinit var shareData: ShareData
private var selectedAudioSourceUrl: String? = null
private var selectedVideoSourceUrl: String? = null
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
arguments?.let { arguments?.let {
@ -536,7 +542,7 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
if (!::segmentData.isInitialized || segmentData.segments.isEmpty()) return if (!::segmentData.isInitialized || segmentData.segments.isEmpty()) return
val currentPosition = exoPlayer.currentPosition val currentPosition = exoPlayer.currentPosition
segmentData.segments.forEach { segment: com.github.libretube.api.obj.Segment -> segmentData.segments.forEach { segment: Segment ->
val segmentStart = (segment.segment[0] * 1000f).toLong() val segmentStart = (segment.segment[0] * 1000f).toLong()
val segmentEnd = (segment.segment[1] * 1000f).toLong() val segmentEnd = (segment.segment[1] * 1000f).toLong()
@ -1067,7 +1073,7 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
} }
private fun setMediaSource( private fun setMediaSource(
videoUri: Uri, videoUrl: String,
audioUrl: String audioUrl: String
) { ) {
val checkIntervalSize = when (PlayerHelper.progressiveLoadingIntervalSize) { val checkIntervalSize = when (PlayerHelper.progressiveLoadingIntervalSize) {
@ -1079,7 +1085,7 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
DefaultHttpDataSource.Factory() DefaultHttpDataSource.Factory()
val videoItem: MediaItem = MediaItem.Builder() val videoItem: MediaItem = MediaItem.Builder()
.setUri(videoUri) .setUri(videoUrl.toUri())
.setSubtitleConfigurations(subtitles) .setSubtitleConfigurations(subtitles)
.build() .build()
@ -1106,16 +1112,16 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
exoPlayer.setMediaItem(mediaItem) exoPlayer.setMediaItem(mediaItem)
} }
private fun getAvailableResolutions(): Pair<Array<String>, Array<Uri>> { private fun getAvailableResolutions(): Pair<Array<String>, Array<String>> {
if (!this::streams.isInitialized) return Pair(arrayOf(), arrayOf()) if (!this::streams.isInitialized) return Pair(arrayOf(), arrayOf())
var videosNameArray: Array<String> = arrayOf() var videosNameArray: Array<String> = arrayOf()
var videosUrlArray: Array<Uri> = arrayOf() var videosUrlArray: Array<String> = arrayOf()
// append hls to list if available // append hls to list if available
if (streams.hls != null) { if (streams.hls != null) {
videosNameArray += getString(R.string.hls) videosNameArray += getString(R.string.hls)
videosUrlArray += streams.hls!!.toUri() videosUrlArray += streams.hls!!
} }
val videoStreams = try { val videoStreams = try {
@ -1138,10 +1144,10 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
val preferredMimeType = "video/${PlayerHelper.videoFormatPreference}" val preferredMimeType = "video/${PlayerHelper.videoFormatPreference}"
if (vid.url != null && vid.mimeType == preferredMimeType) { // preferred format if (vid.url != null && vid.mimeType == preferredMimeType) { // preferred format
videosNameArray += vid.quality.toString() videosNameArray += vid.quality.toString()
videosUrlArray += vid.url!!.toUri() videosUrlArray += vid.url!!
} else if (vid.quality.equals("LBRY") && vid.format.equals("MP4")) { // LBRY MP4 format } else if (vid.quality.equals("LBRY") && vid.format.equals("MP4")) { // LBRY MP4 format
videosNameArray += "LBRY MP4" videosNameArray += "LBRY MP4"
videosUrlArray += vid.url!!.toUri() videosUrlArray += vid.url!!
} }
} }
return Pair(videosNameArray, videosUrlArray) return Pair(videosNameArray, videosUrlArray)
@ -1189,17 +1195,16 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
private fun setStreamSource( private fun setStreamSource(
streams: Streams, streams: Streams,
videosNameArray: Array<String>, videosNameArray: Array<String>,
videosUrlArray: Array<Uri> videosUrlArray: Array<String>
) { ) {
val defaultResolution = PlayerHelper.getDefaultResolution(requireContext()) val defaultResolution = PlayerHelper.getDefaultResolution(requireContext())
if (defaultResolution != "") { if (defaultResolution != "") {
videosNameArray.forEachIndexed { index, pipedStream -> videosNameArray.forEachIndexed { index, pipedStream ->
// search for quality preference in the available stream sources // search for quality preference in the available stream sources
if (pipedStream.contains(defaultResolution)) { if (pipedStream.contains(defaultResolution)) {
val videoUri = videosUrlArray[index] selectedVideoSourceUrl = videosUrlArray[index]
val audioUrl = selectedAudioSourceUrl = selectedAudioSourceUrl ?: getAudioSource(streams.audioStreams)
PlayerHelper.getAudioSource(requireContext(), streams.audioStreams!!) setMediaSource(selectedAudioSourceUrl!!, selectedVideoSourceUrl!!)
setMediaSource(videoUri, audioUrl)
return return
} }
} }
@ -1219,6 +1224,15 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
} }
} }
private fun getAudioSource(audioStreams: List<PipedStream>?): String {
val appLanguage = Locale.getDefault().language.lowercase().substring(0, 2)
val filteredStreams = audioStreams.orEmpty().filter { it.audioTrackId?.contains(appLanguage) ?: false }
return PlayerHelper.getAudioSource(
requireContext(),
filteredStreams.ifEmpty { audioStreams!! }
)
}
private fun createExoPlayer() { private fun createExoPlayer() {
val cronetEngine: CronetEngine = CronetHelper.getCronetEngine() val cronetEngine: CronetEngine = CronetHelper.getCronetEngine()
val cronetDataSourceFactory: CronetDataSource.Factory = val cronetDataSourceFactory: CronetDataSource.Factory =
@ -1395,18 +1409,36 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
videosNameArray[which] == "LBRY HLS" videosNameArray[which] == "LBRY HLS"
) { ) {
// set the progressive media source // set the progressive media source
setHLSMediaSource(videosUrlArray[which]) setHLSMediaSource(videosUrlArray[which].toUri())
} else { } else {
val videoUri = videosUrlArray[which] selectedVideoSourceUrl = videosUrlArray[which]
val audioUrl = selectedAudioSourceUrl = selectedAudioSourceUrl ?: getAudioSource(streams.audioStreams)
PlayerHelper.getAudioSource(requireContext(), streams.audioStreams!!) setMediaSource(selectedVideoSourceUrl!!, selectedAudioSourceUrl!!)
setMediaSource(videoUri, audioUrl)
} }
exoPlayer.seekTo(lastPosition) exoPlayer.seekTo(lastPosition)
} }
.show(childFragmentManager) .show(childFragmentManager)
} }
private fun getAudioStreamGroups(audioStreams: List<PipedStream>?): Map<String?, List<PipedStream>> {
return audioStreams.orEmpty()
.groupBy { it.audioTrackName }
}
override fun onAudioStreamClicked() {
val audioGroups = getAudioStreamGroups(streams.audioStreams)
val audioLanguages = audioGroups.map { it.key ?: getString(R.string.default_audio_track) }
BaseBottomSheet()
.setSimpleItems(audioLanguages) { index ->
val audioStreams = audioGroups.values.elementAt(index)
selectedAudioSourceUrl = PlayerHelper.getAudioSource(requireContext(), audioStreams)
selectedVideoSourceUrl = selectedVideoSourceUrl ?: streams.videoStreams!!.first().url!!
setMediaSource(selectedAudioSourceUrl!!, selectedVideoSourceUrl!!)
}
.show(childFragmentManager)
}
override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean) { override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean) {
super.onPictureInPictureModeChanged(isInPictureInPictureMode) super.onPictureInPictureModeChanged(isInPictureInPictureMode)
if (isInPictureInPictureMode) { if (isInPictureInPictureMode) {

View File

@ -4,4 +4,6 @@ interface OnlinePlayerOptions {
fun onCaptionsClicked() fun onCaptionsClicked()
fun onQualityClicked() fun onQualityClicked()
fun onAudioStreamClicked()
} }

View File

@ -195,6 +195,14 @@ internal class CustomExoPlayerView(
playerOptionsInterface?.onQualityClicked() playerOptionsInterface?.onQualityClicked()
} }
) )
items.add(
BottomSheetItem(
context.getString(R.string.audio_track),
R.drawable.ic_audio
) {
playerOptionsInterface?.onAudioStreamClicked()
}
)
items.add( items.add(
BottomSheetItem( BottomSheetItem(
context.getString(R.string.captions), context.getString(R.string.captions),

View File

@ -3,6 +3,7 @@ package com.github.libretube.util
import android.content.Context import android.content.Context
import android.content.pm.ActivityInfo import android.content.pm.ActivityInfo
import android.view.accessibility.CaptioningManager import android.view.accessibility.CaptioningManager
import com.github.libretube.api.obj.PipedStream
import com.github.libretube.constants.PreferenceKeys import com.github.libretube.constants.PreferenceKeys
import com.google.android.exoplayer2.ui.CaptionStyleCompat import com.google.android.exoplayer2.ui.CaptionStyleCompat
import com.google.android.exoplayer2.video.VideoSize import com.google.android.exoplayer2.video.VideoSize
@ -12,7 +13,7 @@ object PlayerHelper {
// get the audio source following the users preferences // get the audio source following the users preferences
fun getAudioSource( fun getAudioSource(
context: Context, context: Context,
audios: List<com.github.libretube.api.obj.PipedStream> audios: List<PipedStream>
): String { ): String {
val audioFormat = PreferenceHelper.getString(PreferenceKeys.PLAYER_AUDIO_FORMAT, "all") val audioFormat = PreferenceHelper.getString(PreferenceKeys.PLAYER_AUDIO_FORMAT, "all")
val audioQuality = if ( val audioQuality = if (
@ -39,7 +40,7 @@ object PlayerHelper {
} }
// get the best bit rate from audio streams // get the best bit rate from audio streams
private fun getMostBitRate(audios: List<com.github.libretube.api.obj.PipedStream>): String { private fun getMostBitRate(audios: List<PipedStream>): String {
var bitrate = 0 var bitrate = 0
var audioUrl = "" var audioUrl = ""
audios.forEach { audios.forEach {
@ -52,7 +53,7 @@ object PlayerHelper {
} }
// get the best bit rate from audio streams // get the best bit rate from audio streams
private fun getLeastBitRate(audios: List<com.github.libretube.api.obj.PipedStream>): String { private fun getLeastBitRate(audios: List<PipedStream>): String {
var bitrate = 1000000000 var bitrate = 1000000000
var audioUrl = "" var audioUrl = ""
audios.forEach { audios.forEach {

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?attr/colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M12,3v9.28c-0.47,-0.17 -0.97,-0.28 -1.5,-0.28C8.01,12 6,14.01 6,16.5S8.01,21 10.5,21c2.31,0 4.2,-1.75 4.45,-4H15V6h4V3h-7z" />
</vector>

View File

@ -370,6 +370,8 @@
<string name="layout">Layout</string> <string name="layout">Layout</string>
<string name="alternative_player_layout">Alternative player layout</string> <string name="alternative_player_layout">Alternative player layout</string>
<string name="alternative_player_layout_summary">Show the related videos as a row above the comments instead of below.</string> <string name="alternative_player_layout_summary">Show the related videos as a row above the comments instead of below.</string>
<string name="audio_track">Audio track</string>
<string name="default_audio_track">Default</string>
<!-- Notification channel strings --> <!-- Notification channel strings -->
<string name="download_channel_name">Download Service</string> <string name="download_channel_name">Download Service</string>