diff --git a/app/src/main/java/com/futo/platformplayer/Settings.kt b/app/src/main/java/com/futo/platformplayer/Settings.kt index c95947ea..c421c9a3 100644 --- a/app/src/main/java/com/futo/platformplayer/Settings.kt +++ b/app/src/main/java/com/futo/platformplayer/Settings.kt @@ -356,7 +356,7 @@ class Settings : FragmentedStorageFileJson() { var playback = PlaybackSettings(); @Serializable class PlaybackSettings { - @FormField(R.string.primary_language, FieldForm.DROPDOWN, -1, -1) + @FormField(R.string.primary_language, FieldForm.DROPDOWN, -1, -2) @DropdownFieldOptionsId(R.array.audio_languages) var primaryLanguage: Int = 0; @@ -380,6 +380,8 @@ class Settings : FragmentedStorageFileJson() { else -> null } } + @FormField(R.string.prefer_original_audio, FieldForm.TOGGLE, R.string.prefer_original_audio_description, -1) + var preferOriginalAudio: Boolean = true; //= context.resources.getStringArray(R.array.audio_languages)[primaryLanguage]; diff --git a/app/src/main/java/com/futo/platformplayer/UISlideOverlays.kt b/app/src/main/java/com/futo/platformplayer/UISlideOverlays.kt index 67497b1e..a1aa71b7 100644 --- a/app/src/main/java/com/futo/platformplayer/UISlideOverlays.kt +++ b/app/src/main/java/com/futo/platformplayer/UISlideOverlays.kt @@ -402,7 +402,7 @@ class UISlideOverlays { UIDialogs.toast(container.context, "Variant video HLS playlist download started") slideUpMenuOverlay.hide() } else if (source is IHLSManifestAudioSource) { - StateDownloads.instance.download(video, null, HLSVariantAudioUrlSource("variant", 0, "application/vnd.apple.mpegurl", "", "", null, false, sourceUrl), null) + StateDownloads.instance.download(video, null, HLSVariantAudioUrlSource("variant", 0, "application/vnd.apple.mpegurl", "", "", null, false, false, sourceUrl), null) UIDialogs.toast(container.context, "Variant audio HLS playlist download started") slideUpMenuOverlay.hide() } else { diff --git a/app/src/main/java/com/futo/platformplayer/api/media/models/streams/sources/AudioUrlSource.kt b/app/src/main/java/com/futo/platformplayer/api/media/models/streams/sources/AudioUrlSource.kt index 67548b89..a4d2cb55 100644 --- a/app/src/main/java/com/futo/platformplayer/api/media/models/streams/sources/AudioUrlSource.kt +++ b/app/src/main/java/com/futo/platformplayer/api/media/models/streams/sources/AudioUrlSource.kt @@ -13,7 +13,8 @@ class AudioUrlSource( override val codec: String = "", override val language: String = Language.UNKNOWN, override val duration: Long? = null, - override var priority: Boolean = false + override var priority: Boolean = false, + override var original: Boolean = false ) : IAudioUrlSource, IStreamMetaDataSource{ override var streamMetaData: StreamMetaData? = null; @@ -36,7 +37,9 @@ class AudioUrlSource( source.container, source.codec, source.language, - source.duration + source.duration, + source.priority, + source.original ); ret.streamMetaData = streamData; diff --git a/app/src/main/java/com/futo/platformplayer/api/media/models/streams/sources/HLSVariantUrlSource.kt b/app/src/main/java/com/futo/platformplayer/api/media/models/streams/sources/HLSVariantUrlSource.kt index 36df5fb2..854cf9b8 100644 --- a/app/src/main/java/com/futo/platformplayer/api/media/models/streams/sources/HLSVariantUrlSource.kt +++ b/app/src/main/java/com/futo/platformplayer/api/media/models/streams/sources/HLSVariantUrlSource.kt @@ -27,6 +27,7 @@ class HLSVariantAudioUrlSource( override val language: String, override val duration: Long?, override val priority: Boolean, + override val original: Boolean, val url: String ) : IAudioUrlSource { override fun getAudioUrl(): String { diff --git a/app/src/main/java/com/futo/platformplayer/api/media/models/streams/sources/IAudioSource.kt b/app/src/main/java/com/futo/platformplayer/api/media/models/streams/sources/IAudioSource.kt index eca17e47..f2c95b08 100644 --- a/app/src/main/java/com/futo/platformplayer/api/media/models/streams/sources/IAudioSource.kt +++ b/app/src/main/java/com/futo/platformplayer/api/media/models/streams/sources/IAudioSource.kt @@ -8,4 +8,5 @@ interface IAudioSource { val language : String; val duration : Long?; val priority: Boolean; + val original: Boolean; } \ No newline at end of file diff --git a/app/src/main/java/com/futo/platformplayer/api/media/models/streams/sources/LocalAudioSource.kt b/app/src/main/java/com/futo/platformplayer/api/media/models/streams/sources/LocalAudioSource.kt index 254b9731..1f616307 100644 --- a/app/src/main/java/com/futo/platformplayer/api/media/models/streams/sources/LocalAudioSource.kt +++ b/app/src/main/java/com/futo/platformplayer/api/media/models/streams/sources/LocalAudioSource.kt @@ -15,6 +15,7 @@ class LocalAudioSource : IAudioSource, IStreamMetaDataSource { override val duration: Long? = null; override var priority: Boolean = false; + override val original: Boolean = false; val filePath : String; val fileSize: Long; diff --git a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSAudioUrlSource.kt b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSAudioUrlSource.kt index 09de1f35..0edc4f73 100644 --- a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSAudioUrlSource.kt +++ b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSAudioUrlSource.kt @@ -21,6 +21,8 @@ open class JSAudioUrlSource : IAudioUrlSource, JSSource { override var priority: Boolean = false; + override var original: Boolean = false; + constructor(plugin: JSClient, obj: V8ValueObject) : super(TYPE_AUDIOURL, plugin, obj) { val contextName = "AudioUrlSource"; val config = plugin.config; @@ -35,6 +37,7 @@ open class JSAudioUrlSource : IAudioUrlSource, JSSource { name = _obj.getOrDefault(config, "name", contextName, "${container} ${bitrate}") ?: "${container} ${bitrate}"; priority = if(_obj.has("priority")) obj.getOrThrow(config, "priority", contextName) else false; + original = if(_obj.has("original")) obj.getOrThrow(config, "original", contextName) else false; } override fun getAudioUrl() : String { diff --git a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSDashManifestRawAudioSource.kt b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSDashManifestRawAudioSource.kt index 93ed6a01..ae35207b 100644 --- a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSDashManifestRawAudioSource.kt +++ b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSDashManifestRawAudioSource.kt @@ -23,6 +23,7 @@ class JSDashManifestRawAudioSource : JSSource, IAudioSource, IJSDashManifestRawS override val bitrate: Int; override val duration: Long; override val priority: Boolean; + override var original: Boolean = false; override val language: String; @@ -45,6 +46,7 @@ class JSDashManifestRawAudioSource : JSSource, IAudioSource, IJSDashManifestRawS duration = _obj.getOrDefault(config, "duration", contextName, 0) ?: 0; priority = _obj.getOrDefault(config, "priority", contextName, false) ?: false; language = _obj.getOrDefault(config, "language", contextName, Language.UNKNOWN) ?: Language.UNKNOWN; + original = if(_obj.has("original")) obj.getOrThrow(config, "original", contextName) else false; hasGenerate = _obj.has("generate"); } diff --git a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSHLSManifestAudioSource.kt b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSHLSManifestAudioSource.kt index 41948802..9e328df3 100644 --- a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSHLSManifestAudioSource.kt +++ b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSHLSManifestAudioSource.kt @@ -21,6 +21,7 @@ class JSHLSManifestAudioSource : IHLSManifestAudioSource, JSSource { override val language: String; override var priority: Boolean = false; + override var original: Boolean = false; constructor(plugin: JSClient, obj: V8ValueObject) : super(TYPE_HLS, plugin, obj) { val contextName = "HLSAudioSource"; @@ -32,6 +33,7 @@ class JSHLSManifestAudioSource : IHLSManifestAudioSource, JSSource { language = _obj.getOrThrow(config, "language", contextName); priority = obj.getOrNull(config, "priority", contextName) ?: false; + original = if(_obj.has("original")) obj.getOrThrow(config, "original", contextName) else false; } diff --git a/app/src/main/java/com/futo/platformplayer/helpers/VideoHelper.kt b/app/src/main/java/com/futo/platformplayer/helpers/VideoHelper.kt index 87e8f051..522647de 100644 --- a/app/src/main/java/com/futo/platformplayer/helpers/VideoHelper.kt +++ b/app/src/main/java/com/futo/platformplayer/helpers/VideoHelper.kt @@ -9,6 +9,7 @@ import androidx.media3.datasource.ResolvingDataSource import androidx.media3.exoplayer.dash.DashMediaSource import androidx.media3.exoplayer.dash.manifest.DashManifestParser import androidx.media3.exoplayer.source.MediaSource +import com.futo.platformplayer.Settings import com.futo.platformplayer.api.media.models.streams.IVideoSourceDescriptor import com.futo.platformplayer.api.media.models.streams.VideoUnMuxedSourceDescriptor import com.futo.platformplayer.api.media.models.streams.sources.IAudioSource @@ -85,12 +86,17 @@ class VideoHelper { return selectBestAudioSource((desc as VideoUnMuxedSourceDescriptor).audioSources.toList(), prefContainers, prefLanguage, targetBitrate); } - fun selectBestAudioSource(altSources : Iterable, prefContainers : Array, preferredLanguage: String? = null, targetBitrate: Long? = null) : IAudioSource? { + fun selectBestAudioSource(sources : Iterable, prefContainers : Array, preferredLanguage: String? = null, targetBitrate: Long? = null) : IAudioSource? { + val hasPriority = sources.any { it.priority }; + var altSources = if(hasPriority) sources.filter { it.priority } else sources; + val hasOriginal = altSources.any { it.original }; + if(hasOriginal && Settings.instance.playback.preferOriginalAudio) + altSources = altSources.filter { it.original }; val languageToFilter = if(preferredLanguage != null && altSources.any { it.language == preferredLanguage }) { preferredLanguage } else { if(altSources.any { it.language == Language.ENGLISH }) - Language.ENGLISH + Language.ENGLISH; else Language.UNKNOWN; } diff --git a/app/src/main/java/com/futo/platformplayer/parsers/HLS.kt b/app/src/main/java/com/futo/platformplayer/parsers/HLS.kt index 9d1a3faa..7d57a151 100644 --- a/app/src/main/java/com/futo/platformplayer/parsers/HLS.kt +++ b/app/src/main/java/com/futo/platformplayer/parsers/HLS.kt @@ -119,7 +119,7 @@ class HLS { return if (source is IHLSManifestSource) { listOf() } else if (source is IHLSManifestAudioSource) { - listOf(HLSVariantAudioUrlSource("variant", 0, "application/vnd.apple.mpegurl", "", "", null, false, url)) + listOf(HLSVariantAudioUrlSource("variant", 0, "application/vnd.apple.mpegurl", "", "", null, false, false, url)) } else { throw NotImplementedError() } @@ -340,7 +340,7 @@ class HLS { val suffix = listOf(it.language, it.groupID).mapNotNull { x -> x?.ifEmpty { null } }.joinToString(", ") return@mapNotNull when (it.type) { - "AUDIO" -> HLSVariantAudioUrlSource(it.name?.ifEmpty { "Audio (${suffix})" } ?: "Audio (${suffix})", 0, "application/vnd.apple.mpegurl", "", it.language ?: "", null, false, it.uri) + "AUDIO" -> HLSVariantAudioUrlSource(it.name?.ifEmpty { "Audio (${suffix})" } ?: "Audio (${suffix})", 0, "application/vnd.apple.mpegurl", "", it.language ?: "", null, false, false, it.uri) else -> null } } diff --git a/app/src/main/java/com/futo/platformplayer/states/StatePlaylists.kt b/app/src/main/java/com/futo/platformplayer/states/StatePlaylists.kt index 4a41760a..e2054c90 100644 --- a/app/src/main/java/com/futo/platformplayer/states/StatePlaylists.kt +++ b/app/src/main/java/com/futo/platformplayer/states/StatePlaylists.kt @@ -184,7 +184,7 @@ class StatePlaylists { wasNew = true; _watchlistStore.saveAsync(video); if(orderPosition == -1) - _watchlistOrderStore.set(*(listOf(video.url) + _watchlistOrderStore.values) .toTypedArray()); + _watchlistOrderStore.set(*(listOf(video.url) + _watchlistOrderStore.values).toTypedArray()); else { val existing = _watchlistOrderStore.getAllValues().toMutableList(); existing.add(orderPosition, video.url); @@ -230,17 +230,20 @@ class StatePlaylists { } } + public fun getWatchLaterSyncPacket(orderOnly: Boolean = false): SyncWatchLaterPackage{ + return SyncWatchLaterPackage( + if (orderOnly) listOf() else getWatchLater(), + if (orderOnly) mapOf() else _watchLaterAdds.all(), + if (orderOnly) mapOf() else _watchLaterRemovals.all(), + getWatchLaterLastReorderTime().toEpochSecond(), + _watchlistOrderStore.values.toList() + ) + } private fun broadcastWatchLater(orderOnly: Boolean = false) { StateApp.instance.scopeOrNull?.launch(Dispatchers.IO) { try { StateSync.instance.broadcastJsonData( - GJSyncOpcodes.syncWatchLater, SyncWatchLaterPackage( - if (orderOnly) listOf() else getWatchLater(), - if (orderOnly) mapOf() else _watchLaterAdds.all(), - if (orderOnly) mapOf() else _watchLaterRemovals.all(), - getWatchLaterLastReorderTime().toEpochSecond(), - _watchlistOrderStore.values.toList() - ) + GJSyncOpcodes.syncWatchLater, getWatchLaterSyncPacket(orderOnly) ); } catch (e: Throwable) { Logger.w(TAG, "Failed to broadcast watch later", e) diff --git a/app/src/main/java/com/futo/platformplayer/sync/internal/SyncSession.kt b/app/src/main/java/com/futo/platformplayer/sync/internal/SyncSession.kt index 8b5621e0..e4273d63 100644 --- a/app/src/main/java/com/futo/platformplayer/sync/internal/SyncSession.kt +++ b/app/src/main/java/com/futo/platformplayer/sync/internal/SyncSession.kt @@ -232,6 +232,8 @@ class SyncSession : IAuthorizable { sendData(GJSyncOpcodes.syncSubscriptionGroups, StateSubscriptionGroups.instance.getSyncSubscriptionGroupsPackageString()); sendData(GJSyncOpcodes.syncPlaylists, StatePlaylists.instance.getSyncPlaylistsPackageString()) + sendData(GJSyncOpcodes.syncWatchLater, Json.encodeToString(StatePlaylists.instance.getWatchLaterSyncPacket(false))); + val recentHistory = StateHistory.instance.getRecentHistory(syncSessionData.lastHistory); if(recentHistory.size > 0) sendJsonData(GJSyncOpcodes.syncHistory, recentHistory); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d4df1905..8f5605c0 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -447,6 +447,8 @@ Preferred Preview Quality Default quality while previewing a video in a feed Primary Language + Prefer Original Audio + Use original audio instead of preferred language when it is known Default Comment Section Hide Recommendations Fully hide the recommendations tab.