mirror of
https://gitlab.futo.org/videostreaming/grayjay.git
synced 2025-04-29 22:24:29 +02:00
Sync watch later on initial connection, Original audio boolean support, priority audio support, setting to prefer original audio
This commit is contained in:
parent
9165a9f7cb
commit
54d58df4b6
@ -356,7 +356,7 @@ class Settings : FragmentedStorageFileJson() {
|
|||||||
var playback = PlaybackSettings();
|
var playback = PlaybackSettings();
|
||||||
@Serializable
|
@Serializable
|
||||||
class PlaybackSettings {
|
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)
|
@DropdownFieldOptionsId(R.array.audio_languages)
|
||||||
var primaryLanguage: Int = 0;
|
var primaryLanguage: Int = 0;
|
||||||
|
|
||||||
@ -380,6 +380,8 @@ class Settings : FragmentedStorageFileJson() {
|
|||||||
else -> null
|
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];
|
//= context.resources.getStringArray(R.array.audio_languages)[primaryLanguage];
|
||||||
|
|
||||||
|
@ -402,7 +402,7 @@ class UISlideOverlays {
|
|||||||
UIDialogs.toast(container.context, "Variant video HLS playlist download started")
|
UIDialogs.toast(container.context, "Variant video HLS playlist download started")
|
||||||
slideUpMenuOverlay.hide()
|
slideUpMenuOverlay.hide()
|
||||||
} else if (source is IHLSManifestAudioSource) {
|
} 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")
|
UIDialogs.toast(container.context, "Variant audio HLS playlist download started")
|
||||||
slideUpMenuOverlay.hide()
|
slideUpMenuOverlay.hide()
|
||||||
} else {
|
} else {
|
||||||
|
@ -13,7 +13,8 @@ class AudioUrlSource(
|
|||||||
override val codec: String = "",
|
override val codec: String = "",
|
||||||
override val language: String = Language.UNKNOWN,
|
override val language: String = Language.UNKNOWN,
|
||||||
override val duration: Long? = null,
|
override val duration: Long? = null,
|
||||||
override var priority: Boolean = false
|
override var priority: Boolean = false,
|
||||||
|
override var original: Boolean = false
|
||||||
) : IAudioUrlSource, IStreamMetaDataSource{
|
) : IAudioUrlSource, IStreamMetaDataSource{
|
||||||
override var streamMetaData: StreamMetaData? = null;
|
override var streamMetaData: StreamMetaData? = null;
|
||||||
|
|
||||||
@ -36,7 +37,9 @@ class AudioUrlSource(
|
|||||||
source.container,
|
source.container,
|
||||||
source.codec,
|
source.codec,
|
||||||
source.language,
|
source.language,
|
||||||
source.duration
|
source.duration,
|
||||||
|
source.priority,
|
||||||
|
source.original
|
||||||
);
|
);
|
||||||
ret.streamMetaData = streamData;
|
ret.streamMetaData = streamData;
|
||||||
|
|
||||||
|
@ -27,6 +27,7 @@ class HLSVariantAudioUrlSource(
|
|||||||
override val language: String,
|
override val language: String,
|
||||||
override val duration: Long?,
|
override val duration: Long?,
|
||||||
override val priority: Boolean,
|
override val priority: Boolean,
|
||||||
|
override val original: Boolean,
|
||||||
val url: String
|
val url: String
|
||||||
) : IAudioUrlSource {
|
) : IAudioUrlSource {
|
||||||
override fun getAudioUrl(): String {
|
override fun getAudioUrl(): String {
|
||||||
|
@ -8,4 +8,5 @@ interface IAudioSource {
|
|||||||
val language : String;
|
val language : String;
|
||||||
val duration : Long?;
|
val duration : Long?;
|
||||||
val priority: Boolean;
|
val priority: Boolean;
|
||||||
|
val original: Boolean;
|
||||||
}
|
}
|
@ -15,6 +15,7 @@ class LocalAudioSource : IAudioSource, IStreamMetaDataSource {
|
|||||||
override val duration: Long? = null;
|
override val duration: Long? = null;
|
||||||
|
|
||||||
override var priority: Boolean = false;
|
override var priority: Boolean = false;
|
||||||
|
override val original: Boolean = false;
|
||||||
|
|
||||||
val filePath : String;
|
val filePath : String;
|
||||||
val fileSize: Long;
|
val fileSize: Long;
|
||||||
|
@ -21,6 +21,8 @@ open class JSAudioUrlSource : IAudioUrlSource, JSSource {
|
|||||||
|
|
||||||
override var priority: Boolean = false;
|
override var priority: Boolean = false;
|
||||||
|
|
||||||
|
override var original: Boolean = false;
|
||||||
|
|
||||||
constructor(plugin: JSClient, obj: V8ValueObject) : super(TYPE_AUDIOURL, plugin, obj) {
|
constructor(plugin: JSClient, obj: V8ValueObject) : super(TYPE_AUDIOURL, plugin, obj) {
|
||||||
val contextName = "AudioUrlSource";
|
val contextName = "AudioUrlSource";
|
||||||
val config = plugin.config;
|
val config = plugin.config;
|
||||||
@ -35,6 +37,7 @@ open class JSAudioUrlSource : IAudioUrlSource, JSSource {
|
|||||||
name = _obj.getOrDefault(config, "name", contextName, "${container} ${bitrate}") ?: "${container} ${bitrate}";
|
name = _obj.getOrDefault(config, "name", contextName, "${container} ${bitrate}") ?: "${container} ${bitrate}";
|
||||||
|
|
||||||
priority = if(_obj.has("priority")) obj.getOrThrow(config, "priority", contextName) else false;
|
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 {
|
override fun getAudioUrl() : String {
|
||||||
|
@ -23,6 +23,7 @@ class JSDashManifestRawAudioSource : JSSource, IAudioSource, IJSDashManifestRawS
|
|||||||
override val bitrate: Int;
|
override val bitrate: Int;
|
||||||
override val duration: Long;
|
override val duration: Long;
|
||||||
override val priority: Boolean;
|
override val priority: Boolean;
|
||||||
|
override var original: Boolean = false;
|
||||||
|
|
||||||
override val language: String;
|
override val language: String;
|
||||||
|
|
||||||
@ -45,6 +46,7 @@ class JSDashManifestRawAudioSource : JSSource, IAudioSource, IJSDashManifestRawS
|
|||||||
duration = _obj.getOrDefault(config, "duration", contextName, 0) ?: 0;
|
duration = _obj.getOrDefault(config, "duration", contextName, 0) ?: 0;
|
||||||
priority = _obj.getOrDefault(config, "priority", contextName, false) ?: false;
|
priority = _obj.getOrDefault(config, "priority", contextName, false) ?: false;
|
||||||
language = _obj.getOrDefault(config, "language", contextName, Language.UNKNOWN) ?: Language.UNKNOWN;
|
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");
|
hasGenerate = _obj.has("generate");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,6 +21,7 @@ class JSHLSManifestAudioSource : IHLSManifestAudioSource, JSSource {
|
|||||||
override val language: String;
|
override val language: String;
|
||||||
|
|
||||||
override var priority: Boolean = false;
|
override var priority: Boolean = false;
|
||||||
|
override var original: Boolean = false;
|
||||||
|
|
||||||
constructor(plugin: JSClient, obj: V8ValueObject) : super(TYPE_HLS, plugin, obj) {
|
constructor(plugin: JSClient, obj: V8ValueObject) : super(TYPE_HLS, plugin, obj) {
|
||||||
val contextName = "HLSAudioSource";
|
val contextName = "HLSAudioSource";
|
||||||
@ -32,6 +33,7 @@ class JSHLSManifestAudioSource : IHLSManifestAudioSource, JSSource {
|
|||||||
language = _obj.getOrThrow(config, "language", contextName);
|
language = _obj.getOrThrow(config, "language", contextName);
|
||||||
|
|
||||||
priority = obj.getOrNull(config, "priority", contextName) ?: false;
|
priority = obj.getOrNull(config, "priority", contextName) ?: false;
|
||||||
|
original = if(_obj.has("original")) obj.getOrThrow(config, "original", contextName) else false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ import androidx.media3.datasource.ResolvingDataSource
|
|||||||
import androidx.media3.exoplayer.dash.DashMediaSource
|
import androidx.media3.exoplayer.dash.DashMediaSource
|
||||||
import androidx.media3.exoplayer.dash.manifest.DashManifestParser
|
import androidx.media3.exoplayer.dash.manifest.DashManifestParser
|
||||||
import androidx.media3.exoplayer.source.MediaSource
|
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.IVideoSourceDescriptor
|
||||||
import com.futo.platformplayer.api.media.models.streams.VideoUnMuxedSourceDescriptor
|
import com.futo.platformplayer.api.media.models.streams.VideoUnMuxedSourceDescriptor
|
||||||
import com.futo.platformplayer.api.media.models.streams.sources.IAudioSource
|
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);
|
return selectBestAudioSource((desc as VideoUnMuxedSourceDescriptor).audioSources.toList(), prefContainers, prefLanguage, targetBitrate);
|
||||||
}
|
}
|
||||||
fun selectBestAudioSource(altSources : Iterable<IAudioSource>, prefContainers : Array<String>, preferredLanguage: String? = null, targetBitrate: Long? = null) : IAudioSource? {
|
fun selectBestAudioSource(sources : Iterable<IAudioSource>, prefContainers : Array<String>, 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 }) {
|
val languageToFilter = if(preferredLanguage != null && altSources.any { it.language == preferredLanguage }) {
|
||||||
preferredLanguage
|
preferredLanguage
|
||||||
} else {
|
} else {
|
||||||
if(altSources.any { it.language == Language.ENGLISH })
|
if(altSources.any { it.language == Language.ENGLISH })
|
||||||
Language.ENGLISH
|
Language.ENGLISH;
|
||||||
else
|
else
|
||||||
Language.UNKNOWN;
|
Language.UNKNOWN;
|
||||||
}
|
}
|
||||||
|
@ -119,7 +119,7 @@ class HLS {
|
|||||||
return if (source is IHLSManifestSource) {
|
return if (source is IHLSManifestSource) {
|
||||||
listOf()
|
listOf()
|
||||||
} else if (source is IHLSManifestAudioSource) {
|
} 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 {
|
} else {
|
||||||
throw NotImplementedError()
|
throw NotImplementedError()
|
||||||
}
|
}
|
||||||
@ -340,7 +340,7 @@ class HLS {
|
|||||||
|
|
||||||
val suffix = listOf(it.language, it.groupID).mapNotNull { x -> x?.ifEmpty { null } }.joinToString(", ")
|
val suffix = listOf(it.language, it.groupID).mapNotNull { x -> x?.ifEmpty { null } }.joinToString(", ")
|
||||||
return@mapNotNull when (it.type) {
|
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
|
else -> null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -184,7 +184,7 @@ class StatePlaylists {
|
|||||||
wasNew = true;
|
wasNew = true;
|
||||||
_watchlistStore.saveAsync(video);
|
_watchlistStore.saveAsync(video);
|
||||||
if(orderPosition == -1)
|
if(orderPosition == -1)
|
||||||
_watchlistOrderStore.set(*(listOf(video.url) + _watchlistOrderStore.values) .toTypedArray());
|
_watchlistOrderStore.set(*(listOf(video.url) + _watchlistOrderStore.values).toTypedArray());
|
||||||
else {
|
else {
|
||||||
val existing = _watchlistOrderStore.getAllValues().toMutableList();
|
val existing = _watchlistOrderStore.getAllValues().toMutableList();
|
||||||
existing.add(orderPosition, video.url);
|
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) {
|
private fun broadcastWatchLater(orderOnly: Boolean = false) {
|
||||||
StateApp.instance.scopeOrNull?.launch(Dispatchers.IO) {
|
StateApp.instance.scopeOrNull?.launch(Dispatchers.IO) {
|
||||||
try {
|
try {
|
||||||
StateSync.instance.broadcastJsonData(
|
StateSync.instance.broadcastJsonData(
|
||||||
GJSyncOpcodes.syncWatchLater, SyncWatchLaterPackage(
|
GJSyncOpcodes.syncWatchLater, getWatchLaterSyncPacket(orderOnly)
|
||||||
if (orderOnly) listOf() else getWatchLater(),
|
|
||||||
if (orderOnly) mapOf() else _watchLaterAdds.all(),
|
|
||||||
if (orderOnly) mapOf() else _watchLaterRemovals.all(),
|
|
||||||
getWatchLaterLastReorderTime().toEpochSecond(),
|
|
||||||
_watchlistOrderStore.values.toList()
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
Logger.w(TAG, "Failed to broadcast watch later", e)
|
Logger.w(TAG, "Failed to broadcast watch later", e)
|
||||||
|
@ -232,6 +232,8 @@ class SyncSession : IAuthorizable {
|
|||||||
sendData(GJSyncOpcodes.syncSubscriptionGroups, StateSubscriptionGroups.instance.getSyncSubscriptionGroupsPackageString());
|
sendData(GJSyncOpcodes.syncSubscriptionGroups, StateSubscriptionGroups.instance.getSyncSubscriptionGroupsPackageString());
|
||||||
sendData(GJSyncOpcodes.syncPlaylists, StatePlaylists.instance.getSyncPlaylistsPackageString())
|
sendData(GJSyncOpcodes.syncPlaylists, StatePlaylists.instance.getSyncPlaylistsPackageString())
|
||||||
|
|
||||||
|
sendData(GJSyncOpcodes.syncWatchLater, Json.encodeToString(StatePlaylists.instance.getWatchLaterSyncPacket(false)));
|
||||||
|
|
||||||
val recentHistory = StateHistory.instance.getRecentHistory(syncSessionData.lastHistory);
|
val recentHistory = StateHistory.instance.getRecentHistory(syncSessionData.lastHistory);
|
||||||
if(recentHistory.size > 0)
|
if(recentHistory.size > 0)
|
||||||
sendJsonData(GJSyncOpcodes.syncHistory, recentHistory);
|
sendJsonData(GJSyncOpcodes.syncHistory, recentHistory);
|
||||||
|
@ -447,6 +447,8 @@
|
|||||||
<string name="preferred_preview_quality">Preferred Preview Quality</string>
|
<string name="preferred_preview_quality">Preferred Preview Quality</string>
|
||||||
<string name="preferred_preview_quality_description">Default quality while previewing a video in a feed</string>
|
<string name="preferred_preview_quality_description">Default quality while previewing a video in a feed</string>
|
||||||
<string name="primary_language">Primary Language</string>
|
<string name="primary_language">Primary Language</string>
|
||||||
|
<string name="prefer_original_audio">Prefer Original Audio</string>
|
||||||
|
<string name="prefer_original_audio_description">Use original audio instead of preferred language when it is known</string>
|
||||||
<string name="default_comment_section">Default Comment Section</string>
|
<string name="default_comment_section">Default Comment Section</string>
|
||||||
<string name="hide_recommendations">Hide Recommendations</string>
|
<string name="hide_recommendations">Hide Recommendations</string>
|
||||||
<string name="hide_recommendations_description">Fully hide the recommendations tab.</string>
|
<string name="hide_recommendations_description">Fully hide the recommendations tab.</string>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user