From cbbbf65428dcf9ab9ec8ab022b44e1400a479614 Mon Sep 17 00:00:00 2001 From: Bnyro Date: Wed, 16 Nov 2022 16:54:18 +0100 Subject: [PATCH 01/33] Improved user feedback on subscription import --- .../com/github/libretube/util/ImportHelper.kt | 79 +++++++++++-------- app/src/main/res/values/strings.xml | 1 + 2 files changed, 45 insertions(+), 35 deletions(-) diff --git a/app/src/main/java/com/github/libretube/util/ImportHelper.kt b/app/src/main/java/com/github/libretube/util/ImportHelper.kt index b02c05e65..dab77b37d 100644 --- a/app/src/main/java/com/github/libretube/util/ImportHelper.kt +++ b/app/src/main/java/com/github/libretube/util/ImportHelper.kt @@ -9,6 +9,7 @@ import com.github.libretube.R import com.github.libretube.api.RetrofitInstance import com.github.libretube.api.SubscriptionHelper import com.github.libretube.extensions.TAG +import com.github.libretube.extensions.toastFromMainThread import com.github.libretube.obj.NewPipeSubscription import com.github.libretube.obj.NewPipeSubscriptions import kotlinx.coroutines.CoroutineScope @@ -17,56 +18,64 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import java.io.FileOutputStream -class ImportHelper(private val activity: Activity) { +class ImportHelper( + private val activity: Activity +) { /** * Import subscriptions by a file uri */ fun importSubscriptions(uri: Uri?) { if (uri == null) return try { - val channels = when (activity.contentResolver.getType(uri)) { - "application/json" -> { - // NewPipe subscriptions format - val mapper = ObjectMapper() - val json = activity.contentResolver.openInputStream(uri)?.use { - it.bufferedReader().use { reader -> reader.readText() } - }.orEmpty() - - val subscriptions = mapper.readValue(json, NewPipeSubscriptions::class.java) - subscriptions.subscriptions.orEmpty().map { - it.url!!.replace("https://www.youtube.com/channel/", "") - } - } - "text/csv", "text/comma-separated-values" -> { - // import subscriptions from Google/YouTube Takeout - activity.contentResolver.openInputStream(uri)?.use { - it.bufferedReader().useLines { lines -> - lines.map { line -> line.substringBefore(",") } - .filter { channelId -> channelId.length == 24 } - .toList() - } - }.orEmpty() - } - else -> throw IllegalArgumentException("Unsupported file type") - } - + val applicationContext = activity.applicationContext + val channels = getChannelsFromUri(uri) CoroutineScope(Dispatchers.IO).launch { SubscriptionHelper.importSubscriptions(channels) + }.invokeOnCompletion { + applicationContext.toastFromMainThread(R.string.importsuccess) } - - Toast.makeText(activity, R.string.importsuccess, Toast.LENGTH_SHORT).show() + } catch (e: IllegalArgumentException) { + Log.e(TAG(), e.toString()) + Toast.makeText(activity, R.string.unsupported_file_format, Toast.LENGTH_SHORT).show() } catch (e: Exception) { Log.e(TAG(), e.toString()) - Toast.makeText( - activity, - R.string.error, - Toast.LENGTH_SHORT - ).show() + Toast.makeText(activity, R.string.server_error, Toast.LENGTH_SHORT).show() } } /** - * write the text to the document + * Get a list of channel IDs from a file [Uri] + */ + private fun getChannelsFromUri(uri: Uri): List { + return when (activity.contentResolver.getType(uri)) { + "application/json" -> { + // NewPipe subscriptions format + val mapper = ObjectMapper() + val json = activity.contentResolver.openInputStream(uri)?.use { + it.bufferedReader().use { reader -> reader.readText() } + }.orEmpty() + + val subscriptions = mapper.readValue(json, NewPipeSubscriptions::class.java) + subscriptions.subscriptions.orEmpty().map { + it.url!!.replace("https://www.youtube.com/channel/", "") + } + } + "text/csv", "text/comma-separated-values" -> { + // import subscriptions from Google/YouTube Takeout + activity.contentResolver.openInputStream(uri)?.use { + it.bufferedReader().useLines { lines -> + lines.map { line -> line.substringBefore(",") } + .filter { channelId -> channelId.length == 24 } + .toList() + } + }.orEmpty() + } + else -> throw IllegalArgumentException("Unsupported file type") + } + } + + /** + * Write the text to the document */ fun exportSubscriptions(uri: Uri?) { if (uri == null) return diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 04d59f718..81231ac6b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -372,6 +372,7 @@ Show the related videos as a row above the comments instead of below. Audio track Default + Unsupported file format! Download Service From da6a614a89dcb88ddc6e4c7c0c3e0e5c351f983b Mon Sep 17 00:00:00 2001 From: Kavin <20838718+FireMasterK@users.noreply.github.com> Date: Wed, 16 Nov 2022 14:17:27 +0000 Subject: [PATCH 02/33] Implement proper support for dash. --- app/build.gradle | 1 + .../github/libretube/api/obj/PipedStream.kt | 2 +- .../libretube/ui/fragments/PlayerFragment.kt | 84 ++------- .../com/github/libretube/util/DashHelper.kt | 176 ++++++++++++++++++ gradle/libs.versions.toml | 1 + 5 files changed, 199 insertions(+), 65 deletions(-) create mode 100644 app/src/main/java/com/github/libretube/util/DashHelper.kt diff --git a/app/build.gradle b/app/build.gradle index abad6cd8e..f65a4f934 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -103,6 +103,7 @@ dependencies { implementation libs.exoplayer implementation(libs.exoplayer.extension.cronet) { exclude group: 'com.google.android.gms' } implementation libs.exoplayer.extension.mediasession + implementation libs.exoplayer.dash /* Retrofit and Jackson */ implementation libs.square.retrofit diff --git a/app/src/main/java/com/github/libretube/api/obj/PipedStream.kt b/app/src/main/java/com/github/libretube/api/obj/PipedStream.kt index 58de468e8..daa1b6f78 100644 --- a/app/src/main/java/com/github/libretube/api/obj/PipedStream.kt +++ b/app/src/main/java/com/github/libretube/api/obj/PipedStream.kt @@ -19,5 +19,5 @@ data class PipedStream( var height: Int? = null, var fps: Int? = null, val audioTrackName: String? = null, - val audioTrackId: String? = null + val audioTrackId: String? = null, ) diff --git a/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt b/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt index f9d6700d5..b28e7d646 100644 --- a/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt +++ b/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt @@ -18,6 +18,7 @@ import android.os.Looper import android.os.PowerManager import android.text.Html import android.text.format.DateUtils +import android.util.Base64 import android.util.Log import android.view.LayoutInflater import android.view.MotionEvent @@ -75,6 +76,7 @@ import com.github.libretube.ui.models.PlayerViewModel import com.github.libretube.ui.sheets.BaseBottomSheet import com.github.libretube.ui.sheets.PlayingQueueSheet import com.github.libretube.util.BackgroundHelper +import com.github.libretube.util.DashHelper import com.github.libretube.util.ImageHelper import com.github.libretube.util.NowPlayingNotification import com.github.libretube.util.PlayerHelper @@ -102,6 +104,7 @@ import com.google.android.exoplayer2.ui.StyledPlayerView import com.google.android.exoplayer2.upstream.DataSource import com.google.android.exoplayer2.upstream.DefaultDataSource import com.google.android.exoplayer2.upstream.DefaultHttpDataSource +import com.google.android.exoplayer2.util.MimeTypes import com.google.android.material.dialog.MaterialAlertDialogBuilder import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -771,7 +774,7 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions { binding.apply { playerViewsInfo.text = context?.getString(R.string.views, response.views.formatShort()) + - if (!isLive) TextUtils.SEPARATOR + response.uploadDate else "" + if (!isLive) TextUtils.SEPARATOR + response.uploadDate else "" textLike.text = response.likes.formatShort() textDislike.text = response.dislikes.formatShort() @@ -941,7 +944,10 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions { } // update the subscribed state - binding.playerSubscribe.setupSubscriptionButton(streams.uploaderUrl?.toID(), streams.uploader) + binding.playerSubscribe.setupSubscriptionButton( + streams.uploaderUrl?.toID(), + streams.uploader + ) if (token != "") { binding.relPlayerSave.setOnClickListener { @@ -1072,41 +1078,10 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions { return chapterIndex } - private fun setMediaSource( - videoUrl: String, - audioUrl: String - ) { - val checkIntervalSize = when (PlayerHelper.progressiveLoadingIntervalSize) { - "default" -> ProgressiveMediaSource.DEFAULT_LOADING_CHECK_INTERVAL_BYTES - else -> PlayerHelper.progressiveLoadingIntervalSize.toInt() * 1024 - } - - val dataSourceFactory: DataSource.Factory = - DefaultHttpDataSource.Factory() - - val videoItem: MediaItem = MediaItem.Builder() - .setUri(videoUrl.toUri()) - .setSubtitleConfigurations(subtitles) - .build() - - val videoSource: MediaSource = - ProgressiveMediaSource.Factory(dataSourceFactory) - .setContinueLoadingCheckIntervalBytes(checkIntervalSize) - .createMediaSource(videoItem) - - val audioSource: MediaSource = - ProgressiveMediaSource.Factory(dataSourceFactory) - .setContinueLoadingCheckIntervalBytes(checkIntervalSize) - .createMediaSource(fromUri(audioUrl)) - - val mergeSource: MediaSource = - MergingMediaSource(videoSource, audioSource) - exoPlayer.setMediaSource(mergeSource) - } - - private fun setHLSMediaSource(uri: Uri) { + private fun setMediaSource(uri: Uri, mimeType: String) { val mediaItem: MediaItem = MediaItem.Builder() .setUri(uri) + .setMimeType(mimeType) .setSubtitleConfigurations(subtitles) .build() exoPlayer.setMediaItem(mediaItem) @@ -1187,41 +1162,24 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions { // set media source and resolution in the beginning setStreamSource( streams, - videosNameArray, - videosUrlArray ) } private fun setStreamSource( - streams: Streams, - videosNameArray: Array, - videosUrlArray: Array + streams: Streams ) { val defaultResolution = PlayerHelper.getDefaultResolution(requireContext()) if (defaultResolution != "") { - videosNameArray.forEachIndexed { index, pipedStream -> - // search for quality preference in the available stream sources - if (pipedStream.contains(defaultResolution)) { - selectedVideoSourceUrl = videosUrlArray[index] - selectedAudioSourceUrl = selectedAudioSourceUrl ?: getAudioSource(streams.audioStreams) - setMediaSource(selectedAudioSourceUrl!!, selectedVideoSourceUrl!!) - return - } - } + // TODO: Fix this, we need to set it from the player! } - // if default resolution isn't set or available, use hls if available - if (streams.hls != null) { - setHLSMediaSource(Uri.parse(streams.hls)) - return - } + val manifest = DashHelper.createManifest(streams) - // if nothing found, use the first list entry - if (videosUrlArray.isNotEmpty()) { - val videoUri = videosUrlArray[0] - val audioUrl = PlayerHelper.getAudioSource(requireContext(), streams.audioStreams!!) - setMediaSource(videoUri, audioUrl) - } + // encode to base64 + val encoded = Base64.encodeToString(manifest.toByteArray(), Base64.DEFAULT) + val mediaItem = "data:application/dash+xml;charset=utf-8;base64,${encoded}" + + this.setMediaSource(mediaItem.toUri(), MimeTypes.APPLICATION_MPD) } private fun getAudioSource(audioStreams: List?): String { @@ -1409,11 +1367,9 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions { videosNameArray[which] == "LBRY HLS" ) { // set the progressive media source - setHLSMediaSource(videosUrlArray[which].toUri()) + setMediaSource(videosUrlArray[which], MimeTypes.APPLICATION_M3U8) } else { - selectedVideoSourceUrl = videosUrlArray[which] - selectedAudioSourceUrl = selectedAudioSourceUrl ?: getAudioSource(streams.audioStreams) - setMediaSource(selectedVideoSourceUrl!!, selectedAudioSourceUrl!!) + // TODO: Fix this } exoPlayer.seekTo(lastPosition) } diff --git a/app/src/main/java/com/github/libretube/util/DashHelper.kt b/app/src/main/java/com/github/libretube/util/DashHelper.kt new file mode 100644 index 000000000..6f2a439b1 --- /dev/null +++ b/app/src/main/java/com/github/libretube/util/DashHelper.kt @@ -0,0 +1,176 @@ +package com.github.libretube.util + +import com.github.libretube.api.obj.PipedStream +import com.github.libretube.api.obj.Streams +import org.w3c.dom.Document +import org.w3c.dom.Element +import java.io.StringWriter +import javax.xml.parsers.DocumentBuilder +import javax.xml.parsers.DocumentBuilderFactory +import javax.xml.transform.TransformerFactory +import javax.xml.transform.dom.DOMSource +import javax.xml.transform.stream.StreamResult + +// Based off of https://github.com/TeamPiped/Piped/blob/master/src/utils/DashUtils.js + +class DashHelper { + companion object { + + private val builderFactory: DocumentBuilderFactory = DocumentBuilderFactory.newInstance() + private val transformerFactory: TransformerFactory = TransformerFactory.newInstance() + + private data class AdapSetInfo( + val mimeType: String, + val audioTrackId: String? = null, + val formats: MutableList = mutableListOf() + ) + + fun createManifest(streams: Streams): String { + val builder: DocumentBuilder = builderFactory.newDocumentBuilder() + + val doc = builder.newDocument() + val mpd = doc.createElement("MPD") + mpd.setAttribute("xmlns", "urn:mpeg:dash:schema:mpd:2011") + mpd.setAttribute("profiles", "urn:mpeg:dash:profile:full:2011") + mpd.setAttribute("minBufferTime", "PT1.5S") + mpd.setAttribute("type", "static") + mpd.setAttribute("mediaPresentationDuration", "PT${streams.duration}S") + + val period = doc.createElement("Period") + + val adapSetInfos = ArrayList() + + for (stream in streams.videoStreams!!) { + + // ignore dual format streams + if (!stream.videoOnly!!) + continue + + val adapSetInfo = adapSetInfos.find { it.mimeType == stream.mimeType } + if (adapSetInfo != null) { + adapSetInfo.formats.add(stream) + continue + } + adapSetInfos.add( + AdapSetInfo( + stream.mimeType!!, + null, + mutableListOf(stream) + ) + ) + } + + for (stream in streams.audioStreams!!) { + val adapSetInfo = + adapSetInfos.find { it.mimeType == stream.mimeType && it.audioTrackId == stream.audioTrackId } + if (adapSetInfo != null) { + adapSetInfo.formats.add(stream) + continue + } + adapSetInfos.add( + AdapSetInfo( + stream.mimeType!!, + null, + mutableListOf(stream) + ) + ) + } + + for (adapSet in adapSetInfos) { + val adapSetElement = doc.createElement("AdaptationSet") + adapSetElement.setAttribute("mimeType", adapSet.mimeType) + adapSetElement.setAttribute("startWithSAP", "1") + adapSetElement.setAttribute("subsegmentAlignment", "true") + if (adapSet.audioTrackId != null) { + adapSetElement.setAttribute("lang", adapSet.audioTrackId.substring(0, 2)) + } + + val isVideo = adapSet.mimeType.contains("video") + + if (isVideo) { + adapSetElement.setAttribute("scanType", "progressive") + } + + for (stream in adapSet.formats) { + val rep = let { + if (isVideo) { + createVideoRepresentation(doc, stream) + } else { + createAudioRepresentation(doc, stream) + } + } + adapSetElement.appendChild(rep) + } + + period.appendChild(adapSetElement) + } + + mpd.appendChild(period) + + doc.appendChild(mpd) + + val domSource = DOMSource(doc) + val writer = StringWriter() + + val transformer = transformerFactory.newTransformer() + transformer.transform(domSource, StreamResult(writer)) + + return writer.toString() + } + + private fun createAudioRepresentation(doc: Document, stream: PipedStream): Element { + val representation = doc.createElement("Representation") + representation.setAttribute("bandwidth", stream.bitrate.toString()) + representation.setAttribute("codecs", stream.codec!!) + representation.setAttribute("mimeType", stream.mimeType!!) + + val audioChannelConfiguration = doc.createElement("AudioChannelConfiguration") + audioChannelConfiguration.setAttribute( + "schemeIdUri", + "urn:mpeg:dash:23003:3:audio_channel_configuration:2011" + ) + audioChannelConfiguration.setAttribute("value", "2") + + val baseUrl = doc.createElement("BaseURL") + baseUrl.appendChild(doc.createTextNode(stream.url!!)) + + val segmentBase = doc.createElement("SegmentBase") + segmentBase.setAttribute("indexRange", "${stream.indexStart}-${stream.indexEnd}") + + val initialization = doc.createElement("Initialization") + initialization.setAttribute("range", "${stream.initStart}-${stream.initEnd}") + segmentBase.appendChild(initialization) + + representation.appendChild(audioChannelConfiguration) + representation.appendChild(baseUrl) + representation.appendChild(segmentBase) + + return representation + } + + private fun createVideoRepresentation(doc: Document, stream: PipedStream): Element { + val representation = doc.createElement("Representation") + representation.setAttribute("codecs", stream.codec!!) + representation.setAttribute("bandwidth", stream.bitrate.toString()) + representation.setAttribute("width", stream.width.toString()) + representation.setAttribute("height", stream.height.toString()) + representation.setAttribute("maxPlayoutRate", "1") + representation.setAttribute("frameRate", stream.fps.toString()) + + val baseUrl = doc.createElement("BaseURL") + baseUrl.appendChild(doc.createTextNode(stream.url!!)) + + val segmentBase = doc.createElement("SegmentBase") + segmentBase.setAttribute("indexRange", "${stream.indexStart}-${stream.indexEnd}") + + val initialization = doc.createElement("Initialization") + initialization.setAttribute("range", "${stream.initStart}-${stream.initEnd}") + segmentBase.appendChild(initialization) + + representation.appendChild(baseUrl) + representation.appendChild(segmentBase) + + return representation + } + } +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6fd6db684..c527e6aea 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -41,6 +41,7 @@ square-retrofit-converterJackson = { group = "com.squareup.retrofit2", name = "c jacksonAnnotations = { group = "com.fasterxml.jackson.core", name = "jackson-annotations", version.ref = "jacksonAnnotations" } desugaring = { group = "com.android.tools", name = "desugar_jdk_libs", version.ref = "desugaring" } exoplayer-extension-cronet = { group = "com.google.android.exoplayer", name = "extension-cronet", version.ref = "exoplayer" } +exoplayer-dash = { group = "com.google.android.exoplayer", name = "exoplayer-dash", version.ref = "exoplayer" } cronet-embedded = { group = "org.chromium.net", name = "cronet-embedded", version.ref = "cronetEmbedded" } cronet-okhttp = { group = "com.google.net.cronet", name = "cronet-okhttp", version.ref = "cronetOkHttp" } coil = { group = "io.coil-kt", name = "coil", version.ref="coil" } From 21850e88216aab4d0931a6b6ae783c00c7cedf80 Mon Sep 17 00:00:00 2001 From: Kavin <20838718+FireMasterK@users.noreply.github.com> Date: Wed, 16 Nov 2022 14:22:22 +0000 Subject: [PATCH 03/33] Remove broken code. --- .../com/github/libretube/ui/fragments/PlayerFragment.kt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt b/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt index b28e7d646..70ca93480 100644 --- a/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt +++ b/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt @@ -1367,7 +1367,7 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions { videosNameArray[which] == "LBRY HLS" ) { // set the progressive media source - setMediaSource(videosUrlArray[which], MimeTypes.APPLICATION_M3U8) + setMediaSource(videosUrlArray[which].toUri(), MimeTypes.APPLICATION_M3U8) } else { // TODO: Fix this } @@ -1388,9 +1388,7 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions { BaseBottomSheet() .setSimpleItems(audioLanguages) { index -> val audioStreams = audioGroups.values.elementAt(index) - selectedAudioSourceUrl = PlayerHelper.getAudioSource(requireContext(), audioStreams) - selectedVideoSourceUrl = selectedVideoSourceUrl ?: streams.videoStreams!!.first().url!! - setMediaSource(selectedAudioSourceUrl!!, selectedVideoSourceUrl!!) + // TODO: Fix this } .show(childFragmentManager) } From a89d131413d058f9f050d715b57bc78ced3d2be2 Mon Sep 17 00:00:00 2001 From: Kavin <20838718+FireMasterK@users.noreply.github.com> Date: Wed, 16 Nov 2022 14:26:25 +0000 Subject: [PATCH 04/33] Run ktlint. --- .../java/com/github/libretube/api/obj/PipedStream.kt | 2 +- .../github/libretube/ui/fragments/PlayerFragment.kt | 12 +++--------- .../java/com/github/libretube/util/DashHelper.kt | 4 ++-- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/com/github/libretube/api/obj/PipedStream.kt b/app/src/main/java/com/github/libretube/api/obj/PipedStream.kt index daa1b6f78..58de468e8 100644 --- a/app/src/main/java/com/github/libretube/api/obj/PipedStream.kt +++ b/app/src/main/java/com/github/libretube/api/obj/PipedStream.kt @@ -19,5 +19,5 @@ data class PipedStream( var height: Int? = null, var fps: Int? = null, val audioTrackName: String? = null, - val audioTrackId: String? = null, + val audioTrackId: String? = null ) diff --git a/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt b/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt index 70ca93480..92b018eea 100644 --- a/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt +++ b/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt @@ -88,22 +88,16 @@ import com.google.android.exoplayer2.DefaultLoadControl import com.google.android.exoplayer2.ExoPlayer import com.google.android.exoplayer2.MediaItem import com.google.android.exoplayer2.MediaItem.SubtitleConfiguration -import com.google.android.exoplayer2.MediaItem.fromUri import com.google.android.exoplayer2.PlaybackException import com.google.android.exoplayer2.Player import com.google.android.exoplayer2.audio.AudioAttributes import com.google.android.exoplayer2.ext.cronet.CronetDataSource import com.google.android.exoplayer2.source.DefaultMediaSourceFactory -import com.google.android.exoplayer2.source.MediaSource -import com.google.android.exoplayer2.source.MergingMediaSource -import com.google.android.exoplayer2.source.ProgressiveMediaSource import com.google.android.exoplayer2.text.Cue.TEXT_SIZE_TYPE_ABSOLUTE import com.google.android.exoplayer2.trackselection.DefaultTrackSelector import com.google.android.exoplayer2.ui.CaptionStyleCompat import com.google.android.exoplayer2.ui.StyledPlayerView -import com.google.android.exoplayer2.upstream.DataSource import com.google.android.exoplayer2.upstream.DefaultDataSource -import com.google.android.exoplayer2.upstream.DefaultHttpDataSource import com.google.android.exoplayer2.util.MimeTypes import com.google.android.material.dialog.MaterialAlertDialogBuilder import kotlinx.coroutines.CoroutineScope @@ -774,7 +768,7 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions { binding.apply { playerViewsInfo.text = context?.getString(R.string.views, response.views.formatShort()) + - if (!isLive) TextUtils.SEPARATOR + response.uploadDate else "" + if (!isLive) TextUtils.SEPARATOR + response.uploadDate else "" textLike.text = response.likes.formatShort() textDislike.text = response.dislikes.formatShort() @@ -1161,7 +1155,7 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions { // set media source and resolution in the beginning setStreamSource( - streams, + streams ) } @@ -1177,7 +1171,7 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions { // encode to base64 val encoded = Base64.encodeToString(manifest.toByteArray(), Base64.DEFAULT) - val mediaItem = "data:application/dash+xml;charset=utf-8;base64,${encoded}" + val mediaItem = "data:application/dash+xml;charset=utf-8;base64,$encoded" this.setMediaSource(mediaItem.toUri(), MimeTypes.APPLICATION_MPD) } diff --git a/app/src/main/java/com/github/libretube/util/DashHelper.kt b/app/src/main/java/com/github/libretube/util/DashHelper.kt index 6f2a439b1..33684d742 100644 --- a/app/src/main/java/com/github/libretube/util/DashHelper.kt +++ b/app/src/main/java/com/github/libretube/util/DashHelper.kt @@ -41,10 +41,10 @@ class DashHelper { val adapSetInfos = ArrayList() for (stream in streams.videoStreams!!) { - // ignore dual format streams - if (!stream.videoOnly!!) + if (!stream.videoOnly!!) { continue + } val adapSetInfo = adapSetInfos.find { it.mimeType == stream.mimeType } if (adapSetInfo != null) { From c3f3b004f5d2b04be94ac2c51b7396ab86cc6738 Mon Sep 17 00:00:00 2001 From: Kavin <20838718+FireMasterK@users.noreply.github.com> Date: Wed, 16 Nov 2022 15:56:23 +0000 Subject: [PATCH 05/33] Remove companion object --- .../com/github/libretube/util/DashHelper.kt | 290 +++++++++--------- 1 file changed, 144 insertions(+), 146 deletions(-) diff --git a/app/src/main/java/com/github/libretube/util/DashHelper.kt b/app/src/main/java/com/github/libretube/util/DashHelper.kt index 33684d742..e543ff6ef 100644 --- a/app/src/main/java/com/github/libretube/util/DashHelper.kt +++ b/app/src/main/java/com/github/libretube/util/DashHelper.kt @@ -13,164 +13,162 @@ import javax.xml.transform.stream.StreamResult // Based off of https://github.com/TeamPiped/Piped/blob/master/src/utils/DashUtils.js -class DashHelper { - companion object { +object DashHelper { - private val builderFactory: DocumentBuilderFactory = DocumentBuilderFactory.newInstance() - private val transformerFactory: TransformerFactory = TransformerFactory.newInstance() + private val builderFactory: DocumentBuilderFactory = DocumentBuilderFactory.newInstance() + private val transformerFactory: TransformerFactory = TransformerFactory.newInstance() - private data class AdapSetInfo( - val mimeType: String, - val audioTrackId: String? = null, - val formats: MutableList = mutableListOf() - ) + private data class AdapSetInfo( + val mimeType: String, + val audioTrackId: String? = null, + val formats: MutableList = mutableListOf() + ) - fun createManifest(streams: Streams): String { - val builder: DocumentBuilder = builderFactory.newDocumentBuilder() + fun createManifest(streams: Streams): String { + val builder: DocumentBuilder = builderFactory.newDocumentBuilder() - val doc = builder.newDocument() - val mpd = doc.createElement("MPD") - mpd.setAttribute("xmlns", "urn:mpeg:dash:schema:mpd:2011") - mpd.setAttribute("profiles", "urn:mpeg:dash:profile:full:2011") - mpd.setAttribute("minBufferTime", "PT1.5S") - mpd.setAttribute("type", "static") - mpd.setAttribute("mediaPresentationDuration", "PT${streams.duration}S") + val doc = builder.newDocument() + val mpd = doc.createElement("MPD") + mpd.setAttribute("xmlns", "urn:mpeg:dash:schema:mpd:2011") + mpd.setAttribute("profiles", "urn:mpeg:dash:profile:full:2011") + mpd.setAttribute("minBufferTime", "PT1.5S") + mpd.setAttribute("type", "static") + mpd.setAttribute("mediaPresentationDuration", "PT${streams.duration}S") - val period = doc.createElement("Period") + val period = doc.createElement("Period") - val adapSetInfos = ArrayList() + val adapSetInfos = ArrayList() - for (stream in streams.videoStreams!!) { - // ignore dual format streams - if (!stream.videoOnly!!) { - continue - } + for (stream in streams.videoStreams!!) { + // ignore dual format streams + if (!stream.videoOnly!!) { + continue + } - val adapSetInfo = adapSetInfos.find { it.mimeType == stream.mimeType } - if (adapSetInfo != null) { - adapSetInfo.formats.add(stream) - continue - } - adapSetInfos.add( - AdapSetInfo( - stream.mimeType!!, - null, - mutableListOf(stream) - ) + val adapSetInfo = adapSetInfos.find { it.mimeType == stream.mimeType } + if (adapSetInfo != null) { + adapSetInfo.formats.add(stream) + continue + } + adapSetInfos.add( + AdapSetInfo( + stream.mimeType!!, + null, + mutableListOf(stream) ) - } - - for (stream in streams.audioStreams!!) { - val adapSetInfo = - adapSetInfos.find { it.mimeType == stream.mimeType && it.audioTrackId == stream.audioTrackId } - if (adapSetInfo != null) { - adapSetInfo.formats.add(stream) - continue - } - adapSetInfos.add( - AdapSetInfo( - stream.mimeType!!, - null, - mutableListOf(stream) - ) - ) - } - - for (adapSet in adapSetInfos) { - val adapSetElement = doc.createElement("AdaptationSet") - adapSetElement.setAttribute("mimeType", adapSet.mimeType) - adapSetElement.setAttribute("startWithSAP", "1") - adapSetElement.setAttribute("subsegmentAlignment", "true") - if (adapSet.audioTrackId != null) { - adapSetElement.setAttribute("lang", adapSet.audioTrackId.substring(0, 2)) - } - - val isVideo = adapSet.mimeType.contains("video") - - if (isVideo) { - adapSetElement.setAttribute("scanType", "progressive") - } - - for (stream in adapSet.formats) { - val rep = let { - if (isVideo) { - createVideoRepresentation(doc, stream) - } else { - createAudioRepresentation(doc, stream) - } - } - adapSetElement.appendChild(rep) - } - - period.appendChild(adapSetElement) - } - - mpd.appendChild(period) - - doc.appendChild(mpd) - - val domSource = DOMSource(doc) - val writer = StringWriter() - - val transformer = transformerFactory.newTransformer() - transformer.transform(domSource, StreamResult(writer)) - - return writer.toString() - } - - private fun createAudioRepresentation(doc: Document, stream: PipedStream): Element { - val representation = doc.createElement("Representation") - representation.setAttribute("bandwidth", stream.bitrate.toString()) - representation.setAttribute("codecs", stream.codec!!) - representation.setAttribute("mimeType", stream.mimeType!!) - - val audioChannelConfiguration = doc.createElement("AudioChannelConfiguration") - audioChannelConfiguration.setAttribute( - "schemeIdUri", - "urn:mpeg:dash:23003:3:audio_channel_configuration:2011" ) - audioChannelConfiguration.setAttribute("value", "2") - - val baseUrl = doc.createElement("BaseURL") - baseUrl.appendChild(doc.createTextNode(stream.url!!)) - - val segmentBase = doc.createElement("SegmentBase") - segmentBase.setAttribute("indexRange", "${stream.indexStart}-${stream.indexEnd}") - - val initialization = doc.createElement("Initialization") - initialization.setAttribute("range", "${stream.initStart}-${stream.initEnd}") - segmentBase.appendChild(initialization) - - representation.appendChild(audioChannelConfiguration) - representation.appendChild(baseUrl) - representation.appendChild(segmentBase) - - return representation } - private fun createVideoRepresentation(doc: Document, stream: PipedStream): Element { - val representation = doc.createElement("Representation") - representation.setAttribute("codecs", stream.codec!!) - representation.setAttribute("bandwidth", stream.bitrate.toString()) - representation.setAttribute("width", stream.width.toString()) - representation.setAttribute("height", stream.height.toString()) - representation.setAttribute("maxPlayoutRate", "1") - representation.setAttribute("frameRate", stream.fps.toString()) - - val baseUrl = doc.createElement("BaseURL") - baseUrl.appendChild(doc.createTextNode(stream.url!!)) - - val segmentBase = doc.createElement("SegmentBase") - segmentBase.setAttribute("indexRange", "${stream.indexStart}-${stream.indexEnd}") - - val initialization = doc.createElement("Initialization") - initialization.setAttribute("range", "${stream.initStart}-${stream.initEnd}") - segmentBase.appendChild(initialization) - - representation.appendChild(baseUrl) - representation.appendChild(segmentBase) - - return representation + for (stream in streams.audioStreams!!) { + val adapSetInfo = + adapSetInfos.find { it.mimeType == stream.mimeType && it.audioTrackId == stream.audioTrackId } + if (adapSetInfo != null) { + adapSetInfo.formats.add(stream) + continue + } + adapSetInfos.add( + AdapSetInfo( + stream.mimeType!!, + null, + mutableListOf(stream) + ) + ) } + + for (adapSet in adapSetInfos) { + val adapSetElement = doc.createElement("AdaptationSet") + adapSetElement.setAttribute("mimeType", adapSet.mimeType) + adapSetElement.setAttribute("startWithSAP", "1") + adapSetElement.setAttribute("subsegmentAlignment", "true") + if (adapSet.audioTrackId != null) { + adapSetElement.setAttribute("lang", adapSet.audioTrackId.substring(0, 2)) + } + + val isVideo = adapSet.mimeType.contains("video") + + if (isVideo) { + adapSetElement.setAttribute("scanType", "progressive") + } + + for (stream in adapSet.formats) { + val rep = let { + if (isVideo) { + createVideoRepresentation(doc, stream) + } else { + createAudioRepresentation(doc, stream) + } + } + adapSetElement.appendChild(rep) + } + + period.appendChild(adapSetElement) + } + + mpd.appendChild(period) + + doc.appendChild(mpd) + + val domSource = DOMSource(doc) + val writer = StringWriter() + + val transformer = transformerFactory.newTransformer() + transformer.transform(domSource, StreamResult(writer)) + + return writer.toString() + } + + private fun createAudioRepresentation(doc: Document, stream: PipedStream): Element { + val representation = doc.createElement("Representation") + representation.setAttribute("bandwidth", stream.bitrate.toString()) + representation.setAttribute("codecs", stream.codec!!) + representation.setAttribute("mimeType", stream.mimeType!!) + + val audioChannelConfiguration = doc.createElement("AudioChannelConfiguration") + audioChannelConfiguration.setAttribute( + "schemeIdUri", + "urn:mpeg:dash:23003:3:audio_channel_configuration:2011" + ) + audioChannelConfiguration.setAttribute("value", "2") + + val baseUrl = doc.createElement("BaseURL") + baseUrl.appendChild(doc.createTextNode(stream.url!!)) + + val segmentBase = doc.createElement("SegmentBase") + segmentBase.setAttribute("indexRange", "${stream.indexStart}-${stream.indexEnd}") + + val initialization = doc.createElement("Initialization") + initialization.setAttribute("range", "${stream.initStart}-${stream.initEnd}") + segmentBase.appendChild(initialization) + + representation.appendChild(audioChannelConfiguration) + representation.appendChild(baseUrl) + representation.appendChild(segmentBase) + + return representation + } + + private fun createVideoRepresentation(doc: Document, stream: PipedStream): Element { + val representation = doc.createElement("Representation") + representation.setAttribute("codecs", stream.codec!!) + representation.setAttribute("bandwidth", stream.bitrate.toString()) + representation.setAttribute("width", stream.width.toString()) + representation.setAttribute("height", stream.height.toString()) + representation.setAttribute("maxPlayoutRate", "1") + representation.setAttribute("frameRate", stream.fps.toString()) + + val baseUrl = doc.createElement("BaseURL") + baseUrl.appendChild(doc.createTextNode(stream.url!!)) + + val segmentBase = doc.createElement("SegmentBase") + segmentBase.setAttribute("indexRange", "${stream.indexStart}-${stream.indexEnd}") + + val initialization = doc.createElement("Initialization") + initialization.setAttribute("range", "${stream.initStart}-${stream.initEnd}") + segmentBase.appendChild(initialization) + + representation.appendChild(baseUrl) + representation.appendChild(segmentBase) + + return representation } } From 365ff2eabb3afa1ed9269a9ffb285ac133872702 Mon Sep 17 00:00:00 2001 From: Bnyro Date: Wed, 16 Nov 2022 18:14:47 +0100 Subject: [PATCH 06/33] reimplement quality selection and pref --- .../github/libretube/obj/VideoResolution.kt | 7 ++ .../libretube/ui/fragments/PlayerFragment.kt | 84 +++++++++++-------- 2 files changed, 54 insertions(+), 37 deletions(-) create mode 100644 app/src/main/java/com/github/libretube/obj/VideoResolution.kt diff --git a/app/src/main/java/com/github/libretube/obj/VideoResolution.kt b/app/src/main/java/com/github/libretube/obj/VideoResolution.kt new file mode 100644 index 000000000..b234cfda2 --- /dev/null +++ b/app/src/main/java/com/github/libretube/obj/VideoResolution.kt @@ -0,0 +1,7 @@ +package com.github.libretube.obj + +data class VideoResolution( + val name: String, + val resolution: Int? = null, + val adaptiveSourceUrl: String? = null +) diff --git a/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt b/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt index 92b018eea..93e13620f 100644 --- a/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt +++ b/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt @@ -60,6 +60,7 @@ import com.github.libretube.extensions.query import com.github.libretube.extensions.toID import com.github.libretube.extensions.toStreamItem import com.github.libretube.obj.ShareData +import com.github.libretube.obj.VideoResolution import com.github.libretube.services.BackgroundMode import com.github.libretube.services.DownloadService import com.github.libretube.ui.activities.MainActivity @@ -166,9 +167,6 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions { private lateinit var shareData: ShareData - private var selectedAudioSourceUrl: String? = null - private var selectedVideoSourceUrl: String? = null - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) arguments?.let { @@ -1081,16 +1079,19 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions { exoPlayer.setMediaItem(mediaItem) } - private fun getAvailableResolutions(): Pair, Array> { - if (!this::streams.isInitialized) return Pair(arrayOf(), arrayOf()) + private fun getAvailableResolutions(): List { + if (!this::streams.isInitialized) return listOf() - var videosNameArray: Array = arrayOf() - var videosUrlArray: Array = arrayOf() + val resolutions = mutableListOf() // append hls to list if available if (streams.hls != null) { - videosNameArray += getString(R.string.hls) - videosUrlArray += streams.hls!! + resolutions.add( + VideoResolution( + name = getString(R.string.hls), + adaptiveSourceUrl = streams.hls!! + ) + ) } val videoStreams = try { @@ -1112,20 +1113,26 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions { // append quality to list if it has the preferred format (e.g. MPEG) val preferredMimeType = "video/${PlayerHelper.videoFormatPreference}" if (vid.url != null && vid.mimeType == preferredMimeType) { // preferred format - videosNameArray += vid.quality.toString() - videosUrlArray += vid.url!! + resolutions.add( + VideoResolution( + name = vid.quality!!, + resolution = vid.quality.toString().split("p").first().toInt() + ) + ) } else if (vid.quality.equals("LBRY") && vid.format.equals("MP4")) { // LBRY MP4 format - videosNameArray += "LBRY MP4" - videosUrlArray += vid.url!! + resolutions.add( + VideoResolution( + name = "LBRY MP4", + adaptiveSourceUrl = vid.url, + resolution = Int.MAX_VALUE + ) + ) } } - return Pair(videosNameArray, videosUrlArray) + return resolutions } private fun setResolutionAndSubtitles() { - // get the available resolutions - val (videosNameArray, videosUrlArray) = getAvailableResolutions() - // create a list of subtitles subtitles = mutableListOf() val subtitlesNamesList = mutableListOf(context?.getString(R.string.none)!!) @@ -1159,12 +1166,13 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions { ) } - private fun setStreamSource( - streams: Streams - ) { + private fun setStreamSource(streams: Streams) { val defaultResolution = PlayerHelper.getDefaultResolution(requireContext()) if (defaultResolution != "") { - // TODO: Fix this, we need to set it from the player! + val params = trackSelector.buildUponParameters() + .setMaxVideoSize(Int.MAX_VALUE, defaultResolution.toInt()) + .setMinVideoSize(Int.MAX_VALUE, defaultResolution.toInt()) + trackSelector.setParameters(params) } val manifest = DashHelper.createManifest(streams) @@ -1176,15 +1184,6 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions { this.setMediaSource(mediaItem.toUri(), MimeTypes.APPLICATION_MPD) } - private fun getAudioSource(audioStreams: List?): 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() { val cronetEngine: CronetEngine = CronetHelper.getCronetEngine() val cronetDataSourceFactory: CronetDataSource.Factory = @@ -1216,6 +1215,11 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions { // control for the track sources like subtitles and audio source trackSelector = DefaultTrackSelector(requireContext()) + val params = trackSelector.buildUponParameters().setPreferredAudioLanguage( + Locale.getDefault().language.lowercase().substring(0, 2) + ) + trackSelector.setParameters(params) + // limit hls to full hd if ( PreferenceHelper.getBoolean( @@ -1348,22 +1352,25 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions { override fun onQualityClicked() { // get the available resolutions - val (videosNameArray, videosUrlArray) = getAvailableResolutions() + val resolutions = getAvailableResolutions() // Dialog for quality selection val lastPosition = exoPlayer.currentPosition BaseBottomSheet() .setSimpleItems( - videosNameArray.toList() + resolutions.map { it.name } ) { which -> if ( - videosNameArray[which] == getString(R.string.hls) || - videosNameArray[which] == "LBRY HLS" + resolutions[which].adaptiveSourceUrl != null ) { // set the progressive media source - setMediaSource(videosUrlArray[which].toUri(), MimeTypes.APPLICATION_M3U8) + setMediaSource(resolutions[which].adaptiveSourceUrl!!.toUri(), MimeTypes.APPLICATION_M3U8) } else { - // TODO: Fix this + val resolution = resolutions[which].resolution!! + val params = trackSelector.buildUponParameters() + .setMaxVideoSize(Int.MAX_VALUE, resolution) + .setMinVideoSize(Int.MAX_VALUE, resolution) + trackSelector.setParameters(params) } exoPlayer.seekTo(lastPosition) } @@ -1382,7 +1389,10 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions { BaseBottomSheet() .setSimpleItems(audioLanguages) { index -> val audioStreams = audioGroups.values.elementAt(index) - // TODO: Fix this + val lang = audioStreams.first().audioTrackId!!.substring(0, 2) + val newParams = trackSelector.buildUponParameters() + .setPreferredAudioLanguage(lang) + trackSelector.setParameters(newParams) } .show(childFragmentManager) } From 0a200a6d260a6230e44bd47bb7224ac5b74b755a Mon Sep 17 00:00:00 2001 From: Bnyro Date: Wed, 16 Nov 2022 18:42:57 +0100 Subject: [PATCH 07/33] option to force hls --- .../libretube/constants/PreferenceKeys.kt | 1 + .../libretube/ui/fragments/PlayerFragment.kt | 57 +++++++++---------- app/src/main/res/values/array.xml | 2 +- app/src/main/res/values/strings.xml | 5 +- app/src/main/res/xml/audio_video_settings.xml | 10 ++++ 5 files changed, 44 insertions(+), 31 deletions(-) diff --git a/app/src/main/java/com/github/libretube/constants/PreferenceKeys.kt b/app/src/main/java/com/github/libretube/constants/PreferenceKeys.kt index 9f0f69781..a46100d37 100644 --- a/app/src/main/java/com/github/libretube/constants/PreferenceKeys.kt +++ b/app/src/main/java/com/github/libretube/constants/PreferenceKeys.kt @@ -81,6 +81,7 @@ object PreferenceKeys { const val LIMIT_HLS = "limit_hls" const val PROGRESSIVE_LOADING_INTERVAL_SIZE = "progressive_loading_interval" const val ALTERNATIVE_PLAYER_LAYOUT = "alternative_player_layout" + const val USE_HLS_OVER_DASH = "use_hls" /** * Background mode diff --git a/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt b/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt index 93e13620f..89cd89da5 100644 --- a/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt +++ b/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt @@ -1084,16 +1084,6 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions { val resolutions = mutableListOf() - // append hls to list if available - if (streams.hls != null) { - resolutions.add( - VideoResolution( - name = getString(R.string.hls), - adaptiveSourceUrl = streams.hls!! - ) - ) - } - val videoStreams = try { // attempt to sort the qualities, catch if there was an error ih parsing streams.videoStreams?.sortedBy { @@ -1129,6 +1119,15 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions { ) } } + + if (resolutions.isEmpty()) { + return listOf( + VideoResolution(getString(R.string.hls), adaptiveSourceUrl = streams.hls) + ) + } + + resolutions.add(0, VideoResolution(getString(R.string.auto_quality), Int.MAX_VALUE)) + return resolutions } @@ -1175,13 +1174,21 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions { trackSelector.setParameters(params) } - val manifest = DashHelper.createManifest(streams) + if (!PreferenceHelper.getBoolean(PreferenceKeys.USE_HLS_OVER_DASH, false) && + streams.videoStreams.orEmpty().isNotEmpty() + ) { + val manifest = DashHelper.createManifest(streams) - // encode to base64 - val encoded = Base64.encodeToString(manifest.toByteArray(), Base64.DEFAULT) - val mediaItem = "data:application/dash+xml;charset=utf-8;base64,$encoded" + // encode to base64 + val encoded = Base64.encodeToString(manifest.toByteArray(), Base64.DEFAULT) + val mediaItem = "data:application/dash+xml;charset=utf-8;base64,$encoded" - this.setMediaSource(mediaItem.toUri(), MimeTypes.APPLICATION_MPD) + this.setMediaSource(mediaItem.toUri(), MimeTypes.APPLICATION_MPD) + } else if (streams.hls != null) { + setMediaSource(streams.hls.toUri(), MimeTypes.APPLICATION_M3U8) + } else { + Toast.makeText(context, R.string.unknown_error, Toast.LENGTH_SHORT).show() + } } private fun createExoPlayer() { @@ -1355,24 +1362,16 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions { val resolutions = getAvailableResolutions() // Dialog for quality selection - val lastPosition = exoPlayer.currentPosition BaseBottomSheet() .setSimpleItems( resolutions.map { it.name } ) { which -> - if ( - resolutions[which].adaptiveSourceUrl != null - ) { - // set the progressive media source - setMediaSource(resolutions[which].adaptiveSourceUrl!!.toUri(), MimeTypes.APPLICATION_M3U8) - } else { - val resolution = resolutions[which].resolution!! - val params = trackSelector.buildUponParameters() - .setMaxVideoSize(Int.MAX_VALUE, resolution) - .setMinVideoSize(Int.MAX_VALUE, resolution) - trackSelector.setParameters(params) - } - exoPlayer.seekTo(lastPosition) + val resolution = resolutions[which].resolution!! + + val params = trackSelector.buildUponParameters() + .setMaxVideoSize(Int.MAX_VALUE, resolution) + .setMinVideoSize(Int.MAX_VALUE, resolution) + trackSelector.setParameters(params) } .show(childFragmentManager) } diff --git a/app/src/main/res/values/array.xml b/app/src/main/res/values/array.xml index f48a87cb5..d48ec1b73 100644 --- a/app/src/main/res/values/array.xml +++ b/app/src/main/res/values/array.xml @@ -188,7 +188,7 @@ - @string/hls + @string/auto_quality 2160p 1440p 1080p diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 81231ac6b..43190dc2a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -208,7 +208,7 @@ Authentication instance Use a different instance for authenticated calls. Choose an auth instance - Auto + HLS GitHub Audio and video Fullscreen orientation @@ -373,6 +373,9 @@ Audio track Default Unsupported file format! + Use HLS + Use HLS instead of DASH (will be slower, not recommended) + Auto Download Service diff --git a/app/src/main/res/xml/audio_video_settings.xml b/app/src/main/res/xml/audio_video_settings.xml index 9d140d99b..fdeb60a2c 100644 --- a/app/src/main/res/xml/audio_video_settings.xml +++ b/app/src/main/res/xml/audio_video_settings.xml @@ -46,6 +46,7 @@ + @@ -62,10 +64,18 @@ app:defaultValue="all" app:entries="@array/playerAudioFormat" app:entryValues="@array/playerAudioFormatValues" + app:isPreferenceVisible="false" app:key="player_audio_format" app:title="@string/playerAudioFormat" app:useSimpleSummaryProvider="true" /> + + Date: Wed, 16 Nov 2022 18:55:12 +0100 Subject: [PATCH 08/33] fix crash caused by default resolution --- .../java/com/github/libretube/ui/fragments/PlayerFragment.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt b/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt index 89cd89da5..2d85bfb7e 100644 --- a/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt +++ b/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt @@ -1166,7 +1166,7 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions { } private fun setStreamSource(streams: Streams) { - val defaultResolution = PlayerHelper.getDefaultResolution(requireContext()) + val defaultResolution = PlayerHelper.getDefaultResolution(requireContext()).replace("p", "") if (defaultResolution != "") { val params = trackSelector.buildUponParameters() .setMaxVideoSize(Int.MAX_VALUE, defaultResolution.toInt()) From c766fb953b81b1beda8e77db94d5e2261e4510f6 Mon Sep 17 00:00:00 2001 From: Bnyro Date: Wed, 16 Nov 2022 19:04:47 +0100 Subject: [PATCH 09/33] avoid duplicated resolutions --- .../libretube/ui/fragments/PlayerFragment.kt | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt b/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt index 2d85bfb7e..a9325f397 100644 --- a/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt +++ b/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt @@ -1087,12 +1087,7 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions { val videoStreams = try { // attempt to sort the qualities, catch if there was an error ih parsing streams.videoStreams?.sortedBy { - it.quality - .toString() - .split("p") - .first() - .replace("p", "") - .toLong() + it.quality?.toLong() ?: 0L }?.reversed() .orEmpty() } catch (_: Exception) { @@ -1102,14 +1097,20 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions { for (vid in videoStreams) { // append quality to list if it has the preferred format (e.g. MPEG) val preferredMimeType = "video/${PlayerHelper.videoFormatPreference}" - if (vid.url != null && vid.mimeType == preferredMimeType) { // preferred format + if (vid.url != null && vid.mimeType == preferredMimeType) { + // avoid duplicated resolutions + if (resolutions.any { + it.resolution == vid.quality.toString().split("p").first().toInt() + } + ) continue + resolutions.add( VideoResolution( name = vid.quality!!, resolution = vid.quality.toString().split("p").first().toInt() ) ) - } else if (vid.quality.equals("LBRY") && vid.format.equals("MP4")) { // LBRY MP4 format + } else if (vid.quality.equals("LBRY") && vid.format.equals("MP4")) { resolutions.add( VideoResolution( name = "LBRY MP4", From 0b5454605b539edd041033b9c83371c476e3ae22 Mon Sep 17 00:00:00 2001 From: Bnyro Date: Wed, 16 Nov 2022 19:11:07 +0100 Subject: [PATCH 10/33] remove the limit HLS pref as HLS now follows the default resolution --- .../github/libretube/constants/PreferenceKeys.kt | 1 - .../libretube/ui/fragments/PlayerFragment.kt | 16 +++------------- app/src/main/res/xml/audio_video_settings.xml | 6 ------ 3 files changed, 3 insertions(+), 20 deletions(-) diff --git a/app/src/main/java/com/github/libretube/constants/PreferenceKeys.kt b/app/src/main/java/com/github/libretube/constants/PreferenceKeys.kt index a46100d37..ac20cc559 100644 --- a/app/src/main/java/com/github/libretube/constants/PreferenceKeys.kt +++ b/app/src/main/java/com/github/libretube/constants/PreferenceKeys.kt @@ -78,7 +78,6 @@ object PreferenceKeys { const val PLAYER_RESIZE_MODE = "player_resize_mode" const val SB_SKIP_MANUALLY = "sb_skip_manually_key" const val SB_SHOW_MARKERS = "sb_show_markers" - const val LIMIT_HLS = "limit_hls" const val PROGRESSIVE_LOADING_INTERVAL_SIZE = "progressive_loading_interval" const val ALTERNATIVE_PLAYER_LAYOUT = "alternative_player_layout" const val USE_HLS_OVER_DASH = "use_hls" diff --git a/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt b/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt index a9325f397..ef913c0dc 100644 --- a/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt +++ b/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt @@ -1102,7 +1102,9 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions { if (resolutions.any { it.resolution == vid.quality.toString().split("p").first().toInt() } - ) continue + ) { + continue + } resolutions.add( VideoResolution( @@ -1228,18 +1230,6 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions { ) trackSelector.setParameters(params) - // limit hls to full hd - if ( - PreferenceHelper.getBoolean( - PreferenceKeys.LIMIT_HLS, - false - ) - ) { - val newParams = trackSelector.buildUponParameters() - .setMaxVideoSize(1920, 1080) - trackSelector.setParameters(newParams) - } - exoPlayer = ExoPlayer.Builder(requireContext()) .setMediaSourceFactory(DefaultMediaSourceFactory(dataSourceFactory)) .setLoadControl(loadControl) diff --git a/app/src/main/res/xml/audio_video_settings.xml b/app/src/main/res/xml/audio_video_settings.xml index fdeb60a2c..ee98f808f 100644 --- a/app/src/main/res/xml/audio_video_settings.xml +++ b/app/src/main/res/xml/audio_video_settings.xml @@ -76,12 +76,6 @@ android:title="@string/hls_instead_of_dash" app:key="use_hls" /> - - Date: Wed, 16 Nov 2022 18:28:29 +0000 Subject: [PATCH 11/33] Actually specify the right audioTrackId. --- app/src/main/java/com/github/libretube/util/DashHelper.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/github/libretube/util/DashHelper.kt b/app/src/main/java/com/github/libretube/util/DashHelper.kt index e543ff6ef..8acadf5a4 100644 --- a/app/src/main/java/com/github/libretube/util/DashHelper.kt +++ b/app/src/main/java/com/github/libretube/util/DashHelper.kt @@ -69,7 +69,7 @@ object DashHelper { adapSetInfos.add( AdapSetInfo( stream.mimeType!!, - null, + stream.audioTrackId, mutableListOf(stream) ) ) From e7bf856d828f3b005e0cf8978b6bd6904fe895e3 Mon Sep 17 00:00:00 2001 From: Kavin <20838718+FireMasterK@users.noreply.github.com> Date: Wed, 16 Nov 2022 21:55:58 +0000 Subject: [PATCH 12/33] Initialize nextpage as null. --- app/src/main/java/com/github/libretube/api/obj/CommentsPage.kt | 2 +- app/src/main/java/com/github/libretube/api/obj/SearchResult.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/github/libretube/api/obj/CommentsPage.kt b/app/src/main/java/com/github/libretube/api/obj/CommentsPage.kt index 469c64a0c..cc0b4ad66 100644 --- a/app/src/main/java/com/github/libretube/api/obj/CommentsPage.kt +++ b/app/src/main/java/com/github/libretube/api/obj/CommentsPage.kt @@ -6,5 +6,5 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties data class CommentsPage( val comments: MutableList = arrayListOf(), val disabled: Boolean? = null, - val nextpage: String? = "" + val nextpage: String? = null ) diff --git a/app/src/main/java/com/github/libretube/api/obj/SearchResult.kt b/app/src/main/java/com/github/libretube/api/obj/SearchResult.kt index 8ec7699f0..f923a6c7f 100644 --- a/app/src/main/java/com/github/libretube/api/obj/SearchResult.kt +++ b/app/src/main/java/com/github/libretube/api/obj/SearchResult.kt @@ -5,7 +5,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties @JsonIgnoreProperties(ignoreUnknown = true) data class SearchResult( val items: MutableList? = arrayListOf(), - val nextpage: String? = "", + val nextpage: String? = null, val suggestion: String? = "", val corrected: Boolean? = null ) From e4752eacc5e626627cf9fa3778ce7a1d3bf15c72 Mon Sep 17 00:00:00 2001 From: Linerly Date: Wed, 16 Nov 2022 13:19:38 +0000 Subject: [PATCH 13/33] Translated using Weblate (Indonesian) Currently translated at 99.7% (377 of 378 strings) Translation: LibreTube/LibreTube Translate-URL: https://hosted.weblate.org/projects/libretube/libretube/id/ --- app/src/main/res/values-in/strings.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index eaac6c1fd..a7f39aff1 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -376,4 +376,5 @@ Tata letak Tata letak pemutar alternatif Tampilkan video terkait sebagai baris di atas komentar, bukan di bawah. + Trek audio \ No newline at end of file From 4ee373fefd96d5e6902e79e2eb1f0091411e6679 Mon Sep 17 00:00:00 2001 From: Nizami Date: Wed, 16 Nov 2022 13:19:34 +0000 Subject: [PATCH 14/33] Translated using Weblate (Azerbaijani) Currently translated at 99.4% (376 of 378 strings) Translation: LibreTube/LibreTube Translate-URL: https://hosted.weblate.org/projects/libretube/libretube/az/ --- app/src/main/res/values-az/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values-az/strings.xml b/app/src/main/res/values-az/strings.xml index 3cc9accf1..3ccfb4dc8 100644 --- a/app/src/main/res/values-az/strings.xml +++ b/app/src/main/res/values-az/strings.xml @@ -66,7 +66,7 @@ Əvvəlcə bəzi kanallara abunə ol. Bu yayımı endirmək mümkün deyil. Endirmə tamamlandı. - Başqa bir endirmə artıq davam edir, zəhmət olmasa, tamamlanana qədər gözləyin. + Başqa bir endirmə artıq davam edir, zəhmət olmasa, tamamlanana qədər gözlə. Endirmək alınmadı. VLC\'də Aç VLC\'də açmaq mümkün olmadı, O, quraşdırılmamış ola bilər. From 3aefa83d90b2ea48e7b757cc5c39d91da55556ca Mon Sep 17 00:00:00 2001 From: Eric Date: Wed, 16 Nov 2022 13:17:31 +0000 Subject: [PATCH 15/33] Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (378 of 378 strings) Translation: LibreTube/LibreTube Translate-URL: https://hosted.weblate.org/projects/libretube/libretube/zh_Hans/ --- app/src/main/res/values-zh-rCN/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 6b8064f68..9085bb210 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -376,4 +376,6 @@ 备选的播放器布局 将相关视频显示为评论上方而不是下方的一行。 备选的“时下流行”布局 + 音轨 + 默认 \ No newline at end of file From d66f133a4678527bdad27fffd99c332e06a333fb Mon Sep 17 00:00:00 2001 From: Linerly Date: Wed, 16 Nov 2022 13:21:59 +0000 Subject: [PATCH 16/33] Translated using Weblate (Indonesian) Currently translated at 100.0% (378 of 378 strings) Translation: LibreTube/LibreTube Translate-URL: https://hosted.weblate.org/projects/libretube/libretube/id/ --- app/src/main/res/values-in/strings.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index a7f39aff1..28792c26b 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -377,4 +377,5 @@ Tata letak pemutar alternatif Tampilkan video terkait sebagai baris di atas komentar, bukan di bawah. Trek audio + Bawaan \ No newline at end of file From ce2ee31224f25c7ab7e1467ab58c4a59b8b07dd8 Mon Sep 17 00:00:00 2001 From: Nizami Date: Wed, 16 Nov 2022 13:45:40 +0000 Subject: [PATCH 17/33] Translated using Weblate (Azerbaijani) Currently translated at 100.0% (378 of 378 strings) Translation: LibreTube/LibreTube Translate-URL: https://hosted.weblate.org/projects/libretube/libretube/az/ --- app/src/main/res/values-az/strings.xml | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/app/src/main/res/values-az/strings.xml b/app/src/main/res/values-az/strings.xml index 3ccfb4dc8..d6aab3e53 100644 --- a/app/src/main/res/values-az/strings.xml +++ b/app/src/main/res/values-az/strings.xml @@ -33,7 +33,7 @@ Oldu Tarixçə Axtarış Tarixçəsi - Tarixçəni təmizləyin + Tarixçəni təmizlə YT Musiqi Albomları YT Musiqi Mahnıları YT Musiqi Videoları @@ -66,18 +66,18 @@ Əvvəlcə bəzi kanallara abunə ol. Bu yayımı endirmək mümkün deyil. Endirmə tamamlandı. - Başqa bir endirmə artıq davam edir, zəhmət olmasa, tamamlanana qədər gözlə. + Başqa endirmə artıq davam edir, zəhmət olmasa, bitənə qədər gözlə. Endirmək alınmadı. VLC\'də Aç VLC\'də açmaq mümkün olmadı, O, quraşdırılmamış ola bilər. Abunəlikləri idxal et YouTube və ya NewPipe\'dan Mövzu - Serverdə problem var. Başqa bir nümunə cəhd edilsin\? + Serverdə problem var. Başqa nümunə cəhd edilsinmi\? Şəbəkə xətası. Nəsə xəta baş verdi. İstifadəçi adı və şifrə daxil etməlisiniz. - Bu, Piped hesabı üçündür. + Bu Piped hesabı üçündür. Video keyfiyyəti Şəbəkə sütunları Burada heç nə yoxdur. @@ -99,7 +99,7 @@ Tənzimləmələr Məkan Nümunə - Tənzimləmələr + Nizamlamalar Veb Sayt Vurğular Rahatladıcı qırmızı @@ -115,7 +115,7 @@ Nişan Piped YouTube - Arxa fonda oynat + Arxa planda oynat %1$s Versiyası Mövcuddur Onu endirmək üçün GitHub\'da buraxılışlara keçilsinmi\? Görünüş @@ -138,7 +138,7 @@ Ən son versiyanı işlədirsiniz. Canlı Müəlliflər - Bu şərhə cavab yoxdur. + Bu şərh cavabsızdır. Endirmə qovluğu Musiqi qovluğu Film qovluğu @@ -367,8 +367,8 @@ Abunəliyi ləğv etməzdən əvvəl təsdiqləmə dialoqunu göstər. Hamısını oynat Vaxt - Başlama vaxtı - Bitmə vaxtı + Başlatma vaxtı + Bitirmə vaxtı Bildiriş vaxtı Bildirişlərin göstərilməsinə icazə verilən vaxt aralığı. Alternativ trend tərtibatı @@ -376,4 +376,6 @@ Tərtibat Alternativ oynadıcı tərtibatı Əlaqədar videoları aşağıda deyil, şərhlərin üstündə cərgə kimi göstər. + Səs trek + Defolt \ No newline at end of file From 39391820f24e39f7ae01c7b43a1983277905507b Mon Sep 17 00:00:00 2001 From: Yaron Shahrabani Date: Wed, 16 Nov 2022 15:05:38 +0000 Subject: [PATCH 18/33] Translated using Weblate (Hebrew) Currently translated at 100.0% (378 of 378 strings) Translation: LibreTube/LibreTube Translate-URL: https://hosted.weblate.org/projects/libretube/libretube/he/ --- app/src/main/res/values-iw/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index 9cc446dcc..0c6e617cf 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -376,4 +376,6 @@ סדר פריסה פריסת נגן חלופית + רצועת שמע + ברירת מחדל \ No newline at end of file From d3892db0d6203defb8176123acfc39712df632b4 Mon Sep 17 00:00:00 2001 From: Linerly Date: Wed, 16 Nov 2022 17:29:21 +0000 Subject: [PATCH 19/33] Translated using Weblate (Indonesian) Currently translated at 100.0% (379 of 379 strings) Translation: LibreTube/LibreTube Translate-URL: https://hosted.weblate.org/projects/libretube/libretube/id/ --- app/src/main/res/values-in/strings.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index 28792c26b..c481dd735 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -378,4 +378,5 @@ Tampilkan video terkait sebagai baris di atas komentar, bukan di bawah. Trek audio Bawaan + Format berkas tidak didukung! \ No newline at end of file From 0c20831b03396194d4e56696862c8ead7228bffd Mon Sep 17 00:00:00 2001 From: Daviteusz Date: Wed, 16 Nov 2022 16:35:48 +0000 Subject: [PATCH 20/33] Translated using Weblate (Polish) Currently translated at 100.0% (379 of 379 strings) Translation: LibreTube/LibreTube Translate-URL: https://hosted.weblate.org/projects/libretube/libretube/pl/ --- app/src/main/res/values-pl/strings.xml | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 7e5e86a21..68508e320 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -6,8 +6,8 @@ Pobierz Jakość Szukaj - SUBSKRYBUJ - ODSUBSKRYBUJ + Subskrybuj + Odsubskrybuj Udostępnij Zapisz Nazwa użytkownika @@ -33,7 +33,7 @@ Serwer napotkał problem. Spróbuj użyć innej instancji. Błąd sieci. Coś poszło nie tak. - To jest dla konta Piped. + Dotyczy konta Piped. Rozdzielczość wideo Kolumny siatki Tu jeszcze nic nie ma. @@ -286,7 +286,7 @@ Skopiowano do schowka Otwórz Proszę włączyć Wi-Fi lub dane komórkowe, aby połączyć się z Internetem. - Widoczność etykiet paska nawigacji + Widoczność etykiet Zawsze Wybrana karta Nigdy @@ -340,7 +340,7 @@ Proszę wybrać co najmniej jedną pozycję Filmy na czasie wydają się być niedostępne dla bieżącego regionu. Proszę wybrać inny w ustawieniach. Pokaż więcej - Kod czasowy + Kod czasowy (sekundy) Skok wyświetleń Wstępne wczytywanie wideo @@ -364,11 +364,19 @@ Sklonowano playlistę Potwierdzenie odsubskrybowania Pokaż okno potwierdzenia przed odsubskrybowaniem. - Odsubskrybować %1$s\? + Czy na pewno chcesz odsubskrybować %1$s\? Odtwórz wszystko Czas Początek Koniec Ograniczenie powiadomień Ogranicz powiadomienia do zakresu czasu. + Alternatywny układ głównej karty + Układ + Zarządzanie + Pokaż powiązane filmiki w wierszu nad komentarzami. + Inny układ powiązanych filmów + Ścieżka dźwiękowa + Domyślna + Nieobsługowany typ pliku! \ No newline at end of file From 63bd7a7cf92f8e1cec693c57620a364edc86f27a Mon Sep 17 00:00:00 2001 From: Nizami Date: Wed, 16 Nov 2022 18:05:02 +0000 Subject: [PATCH 21/33] Translated using Weblate (Turkish) Currently translated at 99.7% (378 of 379 strings) Translation: LibreTube/LibreTube Translate-URL: https://hosted.weblate.org/projects/libretube/libretube/tr/ --- app/src/main/res/values-tr/strings.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 18b3d8083..437b3bee8 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -375,4 +375,8 @@ Düzen Alternatif oynatıcı düzeni İlgili videoları, yorumların altında değil, üstünde bir satır olarak göster. + Sıra + Müzik parçası + Varsayılan + Desteklenmeyen dosya formatı! \ No newline at end of file From 0522b4a67559b5491cebd4c1d268ace3a63afe63 Mon Sep 17 00:00:00 2001 From: Nizami Date: Wed, 16 Nov 2022 17:59:51 +0000 Subject: [PATCH 22/33] Translated using Weblate (Azerbaijani) Currently translated at 100.0% (379 of 379 strings) Translation: LibreTube/LibreTube Translate-URL: https://hosted.weblate.org/projects/libretube/libretube/az/ --- app/src/main/res/values-az/strings.xml | 51 +++++++++++++------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/app/src/main/res/values-az/strings.xml b/app/src/main/res/values-az/strings.xml index d6aab3e53..911e2bfe4 100644 --- a/app/src/main/res/values-az/strings.xml +++ b/app/src/main/res/values-az/strings.xml @@ -196,7 +196,7 @@ Ekran söndürüldükdə oynatmanı dayandır. Pleylisti klonla İlkin tənzimləmələri bərpa et - Bütün tənzimləmələr sıfırlansın və sistemdən çıxılsın\? + Bütün tənzimləmələr sıfırlanılsın və sistemdən çıxılsın\? Hesab Bərpa Et Hesabı sil @@ -204,15 +204,15 @@ Baxış Tarixçəsi Mövqe xatırladıcı Son oynatma mövqeyindən davam etdir - Təsdiqləyici nümunəni seçin + Təsdiqləyici nümunəni seç Təsdiqlənmiş dəvətlər üçün fərqli nümunədən istifadə edin. Təsdiqləyici nümunə GitHub Avtomatik Səs və video Tam ekran istiqaməti - Video aspekt nisbəti - Avtomatik fırlatma + Video görünüş nisbəti + Avtomatik fırlanma Portret Landşaft İctimaiyyət @@ -222,7 +222,7 @@ Twitter Discord Aç… - İnternetə qoşulmaq üçün Wi-Fi və ya mobil datanı yandırın. + Lütfən, İnternetə qoşulmaq üçün Wi-Fi və ya mobil datanı yandır. Bölmələr Oynatma sürəti Bu dəyişiklik tətbiqin yenidən başladılmasını tələb edir. İndi yenidən başlatmaq üçün \"Oldu\" düyməsini basın. @@ -240,7 +240,7 @@ Dataya qənaət rejimi Cihaz fırladıldıqda tam ekran oynatma. Saxlanılan axtarışlar - Yerli olaraq baxılmış videoları izlə + Baxılan videoları yerli olaraq saxla Sıfırla Sistem altyazı üsulu Altyazılar @@ -248,10 +248,10 @@ Saxlanılmış oynatma mövqeləri Yeni LibreTube versiyası indi quraşdırılsınmı\? Video önizləməsi - Oynatma göstəricisini sürükləyərkən fotoqrafiya göstərir. + Oynatma göstəricisini sürükləyərkən fotoqrafiya göstər. Dil və məkan Ümumi - Arxa fonda oynadılır… + Fonda oynadılır… Altyazılar APK Endirilir… Oynadıcı üçün səs formatı @@ -288,7 +288,7 @@ Ötürücü düymələr Növbəti və ya əvvəlki videoya ötürmək üçün düymələri göstər. Maksimum tarixçə ölçüsü - Limitsiz + Qeyri-məhdud Arxa plan rejimi Növbəyə əlavə et Müxtəlif @@ -303,7 +303,7 @@ Uyğunlaşdır Heç biri Cari - Nüsxələyin və bərpa edin + Nüsxələyin & bərpa edin Nüsxələ Şəkil içində Şəkil Ölçü dəyişdirmə rejimi @@ -322,26 +322,26 @@ Mobil data Wi-Fi Seqmenti ötür - Əl ilə keçirt + Əl ilə ötür Seqmentləri avtomatik olaraq ötürmə, həmişə əvvəlcə icazə istə. Yerli abunəlikər Tənzimləmələr Fərdi Nümunələr - Axını arxa fonda yüklə - Abunəlik axınının arxa planda yüklənilməsinin və avtomatik yenilənməsinin qarşısını al. + Axını fonda yüklə + Abunəlik axınının fonda yüklənilməsinin və avtomatik yenilənməsinin qarşısını al. Endirmə Xidməti Media yükləyərkən bildiriş göstərir. - Arxa Fon Rejimi + Fon Rejimi Bildiriş İşçisi - Yeni yayımlar mövcud olduqda bildiriş göstərir. - Səs oynadıcını idarə etmək üçün düymələri olan bildiriş göstərir. + Yeni yayımlar mövcuddursa bildiriş göstərir. + Səs oynadıcını idarə etmək üçün düymələrlə bildiriş göstərir. Növbətini oynat - Naviqasiya paneli - Ən azı bir element seç - Trend\'in cari region üçün əlçatan olmadığı görünür.Tənzimləmələrdə başqasını seç. + Naviqasiya çubuğu + Lütfən, ən az bir element seç + Trend\'in cari region üçün əlçatan olmadığı görünür.Lütfən, tənzimləmələrdə başqasını seç. HLS\'ni 1080p ilə məhdudlaşdır Defolt - Yüksələn yükləmə intervalı ölçüsü + Qabaqcıl yükləmə intervalı ölçüsü Daha aşağı dəyər ilkin video yükləməni sürətləndirə bilər. baxış Səs ucalığı @@ -349,15 +349,15 @@ Yanlış fayl adı! Pleylist adı (tərs) Pleylist sırası - Bu yaxınlarda yeniləndi - Bu yaxınlarda yeniləndi (tərs) + Təzəlikcə yeniləndi + Təzəlikcə yeniləndi (əks) Daha çox göstər Vaxt kodu (saniyə) Pleylistə əlavə edildi Oynatma növbəsi Növbə Göstəricilər - Zaman çubuğunda seqmentləri göstər. + Vaxt çubuğunda seqmentləri nişanla. Canlı yayımlar Alternativ video tərtibatı Defolt işıqlı @@ -372,10 +372,11 @@ Bildiriş vaxtı Bildirişlərin göstərilməsinə icazə verilən vaxt aralığı. Alternativ trend tərtibatı - Nizam + Sıra Tərtibat Alternativ oynadıcı tərtibatı Əlaqədar videoları aşağıda deyil, şərhlərin üstündə cərgə kimi göstər. - Səs trek + Səs treki Defolt + Dəstəklənməyən fayl formatı! \ No newline at end of file From bab0268259427d297ad4d1d6fb740262942d4f8b Mon Sep 17 00:00:00 2001 From: Gediminas Murauskas Date: Wed, 16 Nov 2022 17:45:59 +0000 Subject: [PATCH 23/33] Translated using Weblate (Lithuanian) Currently translated at 100.0% (379 of 379 strings) Translation: LibreTube/LibreTube Translate-URL: https://hosted.weblate.org/projects/libretube/libretube/lt/ --- app/src/main/res/values-lt/strings.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml index baa142f1a..3cc8bae8b 100644 --- a/app/src/main/res/values-lt/strings.xml +++ b/app/src/main/res/values-lt/strings.xml @@ -376,4 +376,7 @@ Rodyti susijusius vaizdo įrašus kaip eilutę virš komentarų, o ne kaip įprasta po jais. Alternatyvus tendencijų išdėstymas Alternatyvus grotuvo išdėstymas + Garso takelis + Numatytasis + Nepalaikomas failo formatas! \ No newline at end of file From 27a4331a2562e9ab06d40e8516516d87e45aa7d3 Mon Sep 17 00:00:00 2001 From: Mario Ruiz Date: Wed, 16 Nov 2022 19:11:16 +0000 Subject: [PATCH 24/33] Translated using Weblate (Spanish) Currently translated at 100.0% (382 of 382 strings) Translation: LibreTube/LibreTube Translate-URL: https://hosted.weblate.org/projects/libretube/libretube/es/ --- app/src/main/res/values-es/strings.xml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 457a0e35a..7f524a65d 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -207,7 +207,7 @@ Instancia de autenticación Utilice una instancia diferente para las llamadas autenticadas. Elija una instancia de autenticación - Automática + HLS GitHub Audio y vídeo Orientación de pantalla completa @@ -376,4 +376,10 @@ Diseño alternativo del reproductor Ordenar Diseño + Pista de audio + Por defecto + ¡Formato no soportado! + Automatico + Usar HLS en vez de DASH (podría ser lento, no recomendado) + Usar HLS \ No newline at end of file From 7a58d4c563cae42f02f5e51c580076dd412eafb9 Mon Sep 17 00:00:00 2001 From: TiA4f8R Date: Wed, 16 Nov 2022 20:28:32 +0000 Subject: [PATCH 25/33] Translated using Weblate (French) Currently translated at 99.2% (379 of 382 strings) Translation: LibreTube/LibreTube Translate-URL: https://hosted.weblate.org/projects/libretube/libretube/fr/ --- app/src/main/res/values-fr/strings.xml | 27 ++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 0d43c4a79..26de37413 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -3,7 +3,7 @@ Oui Recherche S\'abonner - Partage + Partager S\'inscrire Télécharger Enregistrer @@ -32,7 +32,7 @@ Région Se connecter/s\'inscrire Un autre téléchargement est en cours, veuillez attendre qu\'il soit terminé. - Vous devez d\'abord vous connecter ou vous inscrire à l\'aide du menu paramètre ! + Vous devez d\'abord vous connecter ou vous inscrire depuis les paramètres ! Importer les abonnements Le serveur a rencontré un problème. Peut-être essayez une autre instance \? Erreur réseau. @@ -80,10 +80,10 @@ Historique Historique de recherche Effacer l’historique - Chansons YT Musique - Vidéos YT Musique - Albums YT Musique - Listes de lecture YT Musique + Chansons YT Music + Vidéos YT Music + Albums YT Music + Listes de lecture YT Music Onglet par défaut SponsorBlock Utilise l\'API de https://sponsor.ajay.app/ @@ -201,11 +201,11 @@ Supprimer le compte Compte Restaurer - Regarder l’historique + Historique de visionnage Se souvenir de la position Reprendre de la dernière position de lecture Instance d\'authentification - Auto + HLS GitHub Utilisez une instance différente pour les appels authentifiés. Choisissez une instance d\'authentification @@ -371,4 +371,15 @@ Heure de fin Délai de notification Durée pendant laquelle les notifications sont autorisées à s\'afficher. + Mise en page alternative du lecteur + Mise en page alternative des tendances + Ordre + Mise en page + Afficher les vidéos connexes en une ligne au-dessus des commentaires, à la place d\'au-dessous. + Piste audio + Par défaut + Format de fichier non supporté ! + Utiliser HLS + Utiliser HLS à la place de DASH (sera plus lent, non recommandé) + Automatique \ No newline at end of file From 8097c19e3a5f8425a7b9ffd2687ade4ce3fd8ec6 Mon Sep 17 00:00:00 2001 From: Fjuro Date: Wed, 16 Nov 2022 21:28:31 +0000 Subject: [PATCH 26/33] Translated using Weblate (Czech) Currently translated at 100.0% (382 of 382 strings) Translation: LibreTube/LibreTube Translate-URL: https://hosted.weblate.org/projects/libretube/libretube/cs/ --- app/src/main/res/values-cs/strings.xml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 13b156afc..728ade755 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -170,7 +170,7 @@ Autentifikační instance Použít jinou instanci pro autentifikační volání. Vyberte autent. instanci - Auto + HLS GitHub Pro výplňové scény přidané jen jako přídavek nebo humor, které nejsou vyžadovány pro pochopení hlavního obsahu videa. Použití pouze u hudebních videí. Mělo by pokrývat části videa, ne části oficiálních skladeb. Ve výsledku by mělo video připomínat verzi ze Spotify nebo jakoukoli jinou mixovanou verzi jak nejlépe je to možné, nebo omezit mluvení a další rušivé prvky. @@ -376,4 +376,10 @@ Alternativní rozložení trendů Rozložení Alternativní rozložení přehrávače + Zvuková stopa + Výchozí + Nepodporovaný formát souboru! + Auto + Použít HLS + Použít HLS místo DASH (bude pomalejší, nedoporučuje se) \ No newline at end of file From 8e56c48f53dd9988a08c0b718a4f1b3433bbb300 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81cs=20Zolt=C3=A1n?= Date: Wed, 16 Nov 2022 21:55:29 +0000 Subject: [PATCH 27/33] Translated using Weblate (Hungarian) Currently translated at 100.0% (382 of 382 strings) Translation: LibreTube/LibreTube Translate-URL: https://hosted.weblate.org/projects/libretube/libretube/hu/ --- app/src/main/res/values-hu/strings.xml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 5f9a2428c..b13f2fe11 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -197,7 +197,7 @@ Automatikus lejátszás Minőség Működés - Automatikus + HLS GitHub Közösség Discord @@ -376,4 +376,10 @@ Alternatív lejátszó elrendezés Sorrend A kapcsolódó videók megjelenítése egy sorban a hozzászólások felett, nem pedig alattuk. + Alapértelmezett + Hangsáv + HLS használata + Automatikus + HLS használata DASH helyett (lassabb, nem ajánlott) + Nem támogatott fájlformátum! \ No newline at end of file From c08bd1a7fd35ebf4b413ebfbd0eb71e6123532d8 Mon Sep 17 00:00:00 2001 From: Nizami Date: Wed, 16 Nov 2022 18:41:43 +0000 Subject: [PATCH 28/33] Translated using Weblate (Turkish) Currently translated at 100.0% (382 of 382 strings) Translation: LibreTube/LibreTube Translate-URL: https://hosted.weblate.org/projects/libretube/libretube/tr/ --- app/src/main/res/values-tr/strings.xml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 437b3bee8..57b26d377 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -207,7 +207,7 @@ Doğrulanmış davetiyeler için farklı bir oluşum kullanın. Yetkilendirme örneği seçin Doğrulayıcı oluşum - Otomatik + HLS GitHub Tam ekran yönü Video en boy oranı @@ -379,4 +379,7 @@ Müzik parçası Varsayılan Desteklenmeyen dosya formatı! + Otomatik + HLS\'yi kullan + DASH yerine HLS kullan (daha yavaş olacaktır, önerilmez) \ No newline at end of file From b683aaf10c7c51f6b69283cd84fbf944051c570b Mon Sep 17 00:00:00 2001 From: metezd Date: Wed, 16 Nov 2022 19:24:40 +0000 Subject: [PATCH 29/33] Translated using Weblate (Turkish) Currently translated at 100.0% (382 of 382 strings) Translation: LibreTube/LibreTube Translate-URL: https://hosted.weblate.org/projects/libretube/libretube/tr/ --- app/src/main/res/values-tr/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 57b26d377..e9e443705 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -282,7 +282,7 @@ Ölçülen Hata Kopyalandı - Zaman koduyla paylaşın + Zaman koduyla paylaş İndirme başarılı Atlama düğmeleri Sonraki veya önceki videoya atlamak için düğmeleri göster. From fdd7777afd9cabdd853bb43531c7f87309f47458 Mon Sep 17 00:00:00 2001 From: Nizami Date: Wed, 16 Nov 2022 18:38:30 +0000 Subject: [PATCH 30/33] Translated using Weblate (Azerbaijani) Currently translated at 100.0% (382 of 382 strings) Translation: LibreTube/LibreTube Translate-URL: https://hosted.weblate.org/projects/libretube/libretube/az/ --- app/src/main/res/values-az/strings.xml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/values-az/strings.xml b/app/src/main/res/values-az/strings.xml index 911e2bfe4..a51ed2a8c 100644 --- a/app/src/main/res/values-az/strings.xml +++ b/app/src/main/res/values-az/strings.xml @@ -208,7 +208,7 @@ Təsdiqlənmiş dəvətlər üçün fərqli nümunədən istifadə edin. Təsdiqləyici nümunə GitHub - Avtomatik + HLS Səs və video Tam ekran istiqaməti Video görünüş nisbəti @@ -379,4 +379,7 @@ Səs treki Defolt Dəstəklənməyən fayl formatı! + Avtomatik + HLS istifadə et + DASH əvəzinə HLS istifadə et (daha yavaş olacaq, tövsiyə edilmir) \ No newline at end of file From aa05a42eafdd9bcac3a556dbf74404072a1ddd28 Mon Sep 17 00:00:00 2001 From: Ihor Hordiichuk Date: Wed, 16 Nov 2022 20:00:02 +0000 Subject: [PATCH 31/33] Translated using Weblate (Ukrainian) Currently translated at 100.0% (382 of 382 strings) Translation: LibreTube/LibreTube Translate-URL: https://hosted.weblate.org/projects/libretube/libretube/uk/ --- app/src/main/res/values-uk/strings.xml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 8a16b4965..c89f1ae33 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -199,7 +199,7 @@ Історія переглядів Пам\'ятати позицію Вибрати сервер - Авто + HLS GitHub URL-адреса API екземпляра Аудіо та відео @@ -376,4 +376,10 @@ Альтернативний вигляд трендів Вигляд Альтернативний вигляд програвача + Звукова доріжка + Типово + Непідтримуваний формат файлу! + Використовувати HLS + Використовувати HLS замість DASH (працюватиме повільніше, не рекомендується) + Авто \ No newline at end of file From 89597d90a1d68d64efb859887cd8f65da4c24fc9 Mon Sep 17 00:00:00 2001 From: Yaron Shahrabani Date: Wed, 16 Nov 2022 18:33:50 +0000 Subject: [PATCH 32/33] Translated using Weblate (Hebrew) Currently translated at 99.2% (379 of 382 strings) Translation: LibreTube/LibreTube Translate-URL: https://hosted.weblate.org/projects/libretube/libretube/he/ --- app/src/main/res/values-iw/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index 0c6e617cf..df601291a 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -378,4 +378,6 @@ פריסת נגן חלופית רצועת שמע ברירת מחדל + סוג הקובץ לא נתמך! + אוטומטי \ No newline at end of file From 8b225bf6e36e29f1debac13e7c0117064b71b9e0 Mon Sep 17 00:00:00 2001 From: Gediminas Murauskas Date: Wed, 16 Nov 2022 18:49:47 +0000 Subject: [PATCH 33/33] Translated using Weblate (Lithuanian) Currently translated at 100.0% (382 of 382 strings) Translation: LibreTube/LibreTube Translate-URL: https://hosted.weblate.org/projects/libretube/libretube/lt/ --- app/src/main/res/values-lt/strings.xml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml index 3cc8bae8b..a02e3288c 100644 --- a/app/src/main/res/values-lt/strings.xml +++ b/app/src/main/res/values-lt/strings.xml @@ -328,7 +328,7 @@ Didžiausias užkrauto vaizdo įrašo sekundžių skaičius atmintyje. Numatytieji nustatymai ir elgsena Prasukimo žingsnio dydis - Automatinė + HLS Viso ekrano padėtis Pasirinktinis Juodai balta tema @@ -379,4 +379,7 @@ Garso takelis Numatytasis Nepalaikomas failo formatas! + Automatinė + Naudoti HLS + Vietoj DASH naudoti HLS (veiks lėčiau, nerekomenduojama) \ No newline at end of file