diff --git a/app/src/main/java/com/futo/platformplayer/Settings.kt b/app/src/main/java/com/futo/platformplayer/Settings.kt index da414d8f..360a281c 100644 --- a/app/src/main/java/com/futo/platformplayer/Settings.kt +++ b/app/src/main/java/com/futo/platformplayer/Settings.kt @@ -603,6 +603,11 @@ class Settings : FragmentedStorageFileJson() { else -> 2.0 } } + + + @AdvancedField + @FormField(R.string.shorts_pregenerate, FieldForm.TOGGLE, R.string.shorts_pregenerate_description, 28) + var shortsPregenerate: Boolean = false; } @FormField(R.string.comments, "group", R.string.comments_description, 6) 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 7b6388cd..f376ee6e 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 @@ -17,6 +17,7 @@ import com.futo.platformplayer.getOrNull import com.futo.platformplayer.getOrThrow import com.futo.platformplayer.invokeV8 import com.futo.platformplayer.invokeV8Async +import com.futo.platformplayer.logging.Logger import com.futo.platformplayer.others.Language import com.futo.platformplayer.states.StateDeveloper import kotlinx.coroutines.CompletableDeferred @@ -57,12 +58,24 @@ class JSDashManifestRawAudioSource : JSSource, IAudioSource, IJSDashManifestRawS hasGenerate = _obj.has("generate"); } + private var _pregenerate: V8Deferred? = null; + fun pregenerateAsync(scope: CoroutineScope): V8Deferred? { + _pregenerate = generateAsync(scope); + return _pregenerate; + } + override fun generateAsync(scope: CoroutineScope): V8Deferred { if(!hasGenerate) return V8Deferred(CompletableDeferred(manifest)); if(_obj.isClosed) throw IllegalStateException("Source object already closed"); + val pregenerated = _pregenerate; + if(pregenerated != null) { + Logger.w("JSDashManifestRawAudioSource", "Returning pre-generated audio"); + return pregenerated; + } + val plugin = _plugin.getUnderlyingPlugin(); var result: V8Deferred? = null; diff --git a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSDashManifestRawSource.kt b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSDashManifestRawSource.kt index aebaab23..9345b174 100644 --- a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSDashManifestRawSource.kt +++ b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSDashManifestRawSource.kt @@ -18,6 +18,7 @@ import com.futo.platformplayer.getOrNull import com.futo.platformplayer.getOrThrow import com.futo.platformplayer.invokeV8 import com.futo.platformplayer.invokeV8Async +import com.futo.platformplayer.logging.Logger import com.futo.platformplayer.states.StateDeveloper import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.CoroutineScope @@ -65,11 +66,22 @@ open class JSDashManifestRawSource: JSSource, IVideoSource, IJSDashManifestRawSo hasGenerate = _obj.has("generate"); } + private var _pregenerate: V8Deferred? = null; + fun pregenerateAsync(scope: CoroutineScope): V8Deferred? { + _pregenerate = generateAsync(scope); + return _pregenerate; + } + override fun generateAsync(scope: CoroutineScope): V8Deferred { if(!hasGenerate) return V8Deferred(CompletableDeferred(manifest)); if(_obj.isClosed) throw IllegalStateException("Source object already closed"); + val pregenerated = _pregenerate; + if(pregenerated != null) { + Logger.w("JSDashManifestRawSource", "Returning pre-generated video"); + return pregenerated; + } val plugin = _plugin.getUnderlyingPlugin(); diff --git a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/ShortView.kt b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/ShortView.kt index 2ec3d993..7078fd5d 100644 --- a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/ShortView.kt +++ b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/ShortView.kt @@ -35,6 +35,8 @@ import com.futo.platformplayer.api.media.models.subtitles.ISubtitleSource import com.futo.platformplayer.api.media.models.video.IPlatformVideo import com.futo.platformplayer.api.media.models.video.IPlatformVideoDetails import com.futo.platformplayer.api.media.platforms.js.SourcePluginConfig +import com.futo.platformplayer.api.media.platforms.js.models.sources.JSDashManifestRawAudioSource +import com.futo.platformplayer.api.media.platforms.js.models.sources.JSDashManifestRawSource import com.futo.platformplayer.constructs.Event0 import com.futo.platformplayer.constructs.Event1 import com.futo.platformplayer.constructs.Event3 @@ -66,6 +68,8 @@ import com.futo.platformplayer.views.pills.OnLikeDislikeUpdatedArgs import com.futo.platformplayer.views.platform.PlatformIndicator import com.futo.platformplayer.views.video.FutoShortPlayer import com.futo.platformplayer.views.video.FutoVideoPlayerBase +import com.futo.platformplayer.views.video.FutoVideoPlayerBase.Companion.PREFERED_AUDIO_CONTAINERS +import com.futo.platformplayer.views.video.FutoVideoPlayerBase.Companion.PREFERED_VIDEO_CONTAINERS import com.futo.polycentric.core.ApiMethods import com.futo.polycentric.core.ContentType import com.futo.polycentric.core.Models @@ -742,6 +746,23 @@ class ShortView : FrameLayout { videoDetails = result video = result + if(Settings.instance.playback.shortsPregenerate) + fragment.lifecycleScope.launch(Dispatchers.IO) { + if(result != null) { + val prefVid = VideoHelper.selectBestVideoSource(result.video, Settings.instance.playback.getCurrentPreferredQualityPixelCount(), PREFERED_VIDEO_CONTAINERS); + val prefAud = VideoHelper.selectBestAudioSource(result.video, PREFERED_AUDIO_CONTAINERS, Settings.instance.playback.getPrimaryLanguage(context)); + + if(prefVid != null && prefVid is JSDashManifestRawSource) { + Logger.i(TAG, "Shorts pregenerating video (${result.name})"); + prefVid.pregenerateAsync(fragment.lifecycleScope); + } + if(prefAud != null && prefAud is JSDashManifestRawAudioSource) { + Logger.i(TAG, "Shorts pregenerating audio (${result.name})"); + prefAud.pregenerateAsync(fragment.lifecycleScope); + } + } + } + bottomSheet.video = result setLoading(false) diff --git a/app/src/main/java/com/futo/platformplayer/views/video/FutoShortPlayer.kt b/app/src/main/java/com/futo/platformplayer/views/video/FutoShortPlayer.kt index cb5f1240..0b74781d 100644 --- a/app/src/main/java/com/futo/platformplayer/views/video/FutoShortPlayer.kt +++ b/app/src/main/java/com/futo/platformplayer/views/video/FutoShortPlayer.kt @@ -6,6 +6,7 @@ import android.graphics.drawable.Drawable import android.util.AttributeSet import android.view.LayoutInflater import android.view.animation.LinearInterpolator +import androidx.annotation.Dimension import androidx.annotation.OptIn import androidx.media3.common.PlaybackParameters import androidx.media3.common.Player @@ -65,6 +66,8 @@ class FutoShortPlayer(context: Context, attrs: AttributeSet? = null) : videoView = findViewById(R.id.short_player_view) progressBar = findViewById(R.id.short_player_progress_bar) + videoView.subtitleView?.setFixedTextSize(Dimension.SP, 18F); + if (!isInEditMode) { player = StatePlayer.instance.getShortPlayerOrCreate(context) player.player.repeatMode = Player.REPEAT_MODE_ONE diff --git a/app/src/main/res/drawable/ic_thumb_down_s_filled.xml b/app/src/main/res/drawable/ic_thumb_down_s_filled.xml index 96f55911..6dbc120c 100644 --- a/app/src/main/res/drawable/ic_thumb_down_s_filled.xml +++ b/app/src/main/res/drawable/ic_thumb_down_s_filled.xml @@ -5,6 +5,6 @@ android:viewportHeight="960" android:tint="?attr/colorControlNormal"> diff --git a/app/src/main/res/drawable/ic_thumb_up_s_filled.xml b/app/src/main/res/drawable/ic_thumb_up_s_filled.xml index a029aca4..ba1ac372 100644 --- a/app/src/main/res/drawable/ic_thumb_up_s_filled.xml +++ b/app/src/main/res/drawable/ic_thumb_up_s_filled.xml @@ -2,9 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="960" - android:viewportHeight="960" - android:tint="?attr/colorControlNormal"> + android:viewportHeight="960"> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5ddeef06..6a7a30a3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -435,6 +435,8 @@ Allow full-screen portrait when watching horizontal videos Delete from WatchLater when watched After you leave a video that you mostly watched, it will be removed from watch later. + Pre-generate shorts sources + Generates short sources (when applicable) one video ahead Seek duration Minimum Playback Speed Minimum Available Speed