diff --git a/app/src/main/assets/scripts/source.js b/app/src/main/assets/scripts/source.js index f254370e..7e619b3e 100644 --- a/app/src/main/assets/scripts/source.js +++ b/app/src/main/assets/scripts/source.js @@ -357,6 +357,15 @@ class AudioUrlSource { this.requestModifier = obj.requestModifier; } } +class AudioUrlWidevineSource extends AudioUrlSource { + constructor(obj) { + super(obj); + this.plugin_type = "AudioUrlWidevineSource"; + + this.bearerToken = obj.bearerToken; + this.licenseUri = obj.licenseUri; + } +} class AudioUrlRangeSource extends AudioUrlSource { constructor(obj) { super(obj); diff --git a/app/src/main/java/com/futo/platformplayer/Extensions_Polycentric.kt b/app/src/main/java/com/futo/platformplayer/Extensions_Polycentric.kt index 9d2553f4..74f1372e 100644 --- a/app/src/main/java/com/futo/platformplayer/Extensions_Polycentric.kt +++ b/app/src/main/java/com/futo/platformplayer/Extensions_Polycentric.kt @@ -50,12 +50,9 @@ fun Protocol.Claim.resolveChannelUrls(): List { suspend fun ProcessHandle.fullyBackfillServersAnnounceExceptions() { val systemState = SystemState.fromStorageTypeSystemState(Store.instance.getSystemState(system)) - if (!systemState.servers.contains(PolycentricCache.STAGING_SERVER)) { - removeServer(PolycentricCache.STAGING_SERVER) - } - if (!systemState.servers.contains(PolycentricCache.SERVER)) { - removeServer(PolycentricCache.SERVER) + Logger.w("Backfill", "Polycentric prod server not added, adding it.") + addServer(PolycentricCache.SERVER) } val exceptions = fullyBackfillServers() diff --git a/app/src/main/java/com/futo/platformplayer/activities/PolycentricCreateProfileActivity.kt b/app/src/main/java/com/futo/platformplayer/activities/PolycentricCreateProfileActivity.kt index 078c692d..f0fa8397 100644 --- a/app/src/main/java/com/futo/platformplayer/activities/PolycentricCreateProfileActivity.kt +++ b/app/src/main/java/com/futo/platformplayer/activities/PolycentricCreateProfileActivity.kt @@ -12,6 +12,7 @@ import com.futo.platformplayer.R import com.futo.platformplayer.UIDialogs import com.futo.platformplayer.fullyBackfillServersAnnounceExceptions import com.futo.platformplayer.logging.Logger +import com.futo.platformplayer.polycentric.PolycentricCache import com.futo.platformplayer.polycentric.PolycentricStorage import com.futo.platformplayer.setNavigationBarColorAndIcons import com.futo.platformplayer.states.StateApp @@ -77,7 +78,7 @@ class PolycentricCreateProfileActivity : AppCompatActivity() { Logger.e(TAG, "Failed to save process secret to secret storage.", e) } - processHandle.addServer("https://srv1-stg.polycentric.io"); + processHandle.addServer(PolycentricCache.SERVER); processHandle.setUsername(username); StatePolycentric.instance.setProcessHandle(processHandle); } catch (e: Throwable) { diff --git a/app/src/main/java/com/futo/platformplayer/api/media/models/streams/sources/IAudioUrlWidevineSource.kt b/app/src/main/java/com/futo/platformplayer/api/media/models/streams/sources/IAudioUrlWidevineSource.kt new file mode 100644 index 00000000..311c5ed0 --- /dev/null +++ b/app/src/main/java/com/futo/platformplayer/api/media/models/streams/sources/IAudioUrlWidevineSource.kt @@ -0,0 +1,6 @@ +package com.futo.platformplayer.api.media.models.streams.sources + +interface IAudioUrlWidevineSource : IAudioUrlSource { + val bearerToken: String + val licenseUri: String +} diff --git a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSAudioUrlWidevineSource.kt b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSAudioUrlWidevineSource.kt new file mode 100644 index 00000000..dcacdb72 --- /dev/null +++ b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSAudioUrlWidevineSource.kt @@ -0,0 +1,24 @@ +package com.futo.platformplayer.api.media.platforms.js.models.sources + +import com.caoccao.javet.values.reference.V8ValueObject +import com.futo.platformplayer.api.media.models.streams.sources.IAudioUrlWidevineSource +import com.futo.platformplayer.api.media.platforms.js.JSClient +import com.futo.platformplayer.getOrThrow + +class JSAudioUrlWidevineSource : JSAudioUrlSource, IAudioUrlWidevineSource { + override val bearerToken: String + override val licenseUri: String + + @Suppress("ConvertSecondaryConstructorToPrimary") + constructor(plugin: JSClient, obj: V8ValueObject) : super(plugin, obj) { + val contextName = "JSAudioUrlWidevineSource" + val config = plugin.config + bearerToken = _obj.getOrThrow(config, "bearerToken", contextName) + licenseUri = _obj.getOrThrow(config, "licenseUri", contextName) + } + + override fun toString(): String { + val url = getAudioUrl() + return "(name=$name, container=$container, bitrate=$bitrate, codec=$codec, url=$url, language=$language, duration=$duration, bearerToken=$bearerToken, licenseUri=$licenseUri)" + } +} diff --git a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSSource.kt b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSSource.kt index 9074e28e..862a53a5 100644 --- a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSSource.kt +++ b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSSource.kt @@ -66,6 +66,7 @@ abstract class JSSource { const val TYPE_VIDEO_WITH_METADATA = "VideoUrlRangeSource"; const val TYPE_DASH = "DashSource"; const val TYPE_HLS = "HLSSource"; + const val TYPE_AUDIOURL_WIDEVINE = "AudioUrlWidevineSource" fun fromV8VideoNullable(plugin: JSClient, obj: V8Value?) : IVideoSource? = obj.orNull { fromV8Video(plugin, it as V8ValueObject) }; fun fromV8Video(plugin: JSClient, obj: V8ValueObject) : IVideoSource { @@ -88,6 +89,7 @@ abstract class JSSource { return when(type) { TYPE_HLS -> JSHLSManifestAudioSource.fromV8HLS(plugin, obj); TYPE_AUDIOURL -> JSAudioUrlSource(plugin, obj); + TYPE_AUDIOURL_WIDEVINE -> JSAudioUrlWidevineSource(plugin, obj); TYPE_AUDIO_WITH_METADATA -> JSAudioUrlRangeSource(plugin, obj); else -> throw NotImplementedError("Unknown type ${type}"); } diff --git a/app/src/main/java/com/futo/platformplayer/polycentric/PolycentricCache.kt b/app/src/main/java/com/futo/platformplayer/polycentric/PolycentricCache.kt index 931d3865..abe4ca8e 100644 --- a/app/src/main/java/com/futo/platformplayer/polycentric/PolycentricCache.kt +++ b/app/src/main/java/com/futo/platformplayer/polycentric/PolycentricCache.kt @@ -316,7 +316,6 @@ class PolycentricCache { .build(); private const val TAG = "PolycentricCache" - const val STAGING_SERVER = "https://srv1-stg.polycentric.io" const val SERVER = "https://srv1-prod.polycentric.io" private var _instance: PolycentricCache? = null; private val CACHE_EXPIRATION_SECONDS = 60 * 5; diff --git a/app/src/main/java/com/futo/platformplayer/views/video/FutoVideoPlayerBase.kt b/app/src/main/java/com/futo/platformplayer/views/video/FutoVideoPlayerBase.kt index 3179e282..519ab6ec 100644 --- a/app/src/main/java/com/futo/platformplayer/views/video/FutoVideoPlayerBase.kt +++ b/app/src/main/java/com/futo/platformplayer/views/video/FutoVideoPlayerBase.kt @@ -16,6 +16,7 @@ import androidx.media3.datasource.DefaultDataSource import androidx.media3.datasource.DefaultHttpDataSource import androidx.media3.exoplayer.ExoPlayer import androidx.media3.exoplayer.dash.DashMediaSource +import androidx.media3.exoplayer.drm.DefaultDrmSessionManagerProvider import androidx.media3.exoplayer.hls.HlsMediaSource import androidx.media3.exoplayer.source.MediaSource import androidx.media3.exoplayer.source.MergingMediaSource @@ -27,6 +28,7 @@ import com.futo.platformplayer.api.media.models.chapters.IChapter import com.futo.platformplayer.api.media.models.streams.VideoMuxedSourceDescriptor import com.futo.platformplayer.api.media.models.streams.sources.IAudioSource import com.futo.platformplayer.api.media.models.streams.sources.IAudioUrlSource +import com.futo.platformplayer.api.media.models.streams.sources.IAudioUrlWidevineSource import com.futo.platformplayer.api.media.models.streams.sources.IDashManifestSource import com.futo.platformplayer.api.media.models.streams.sources.IHLSManifestAudioSource import com.futo.platformplayer.api.media.models.streams.sources.IHLSManifestSource @@ -389,6 +391,7 @@ abstract class FutoVideoPlayerBase : RelativeLayout { is LocalAudioSource -> swapAudioSourceLocal(audioSource); is JSAudioUrlRangeSource -> swapAudioSourceUrlRange(audioSource); is JSHLSManifestAudioSource -> swapAudioSourceHLS(audioSource); + is IAudioUrlWidevineSource -> swapAudioSourceUrlWidevine(audioSource) is IAudioUrlSource -> swapAudioSourceUrl(audioSource); null -> _lastAudioMediaSource = null; else -> throw IllegalArgumentException("Unsupported video source [${audioSource.javaClass.simpleName}]"); @@ -508,6 +511,31 @@ abstract class FutoVideoPlayerBase : RelativeLayout { .createMediaSource(MediaItem.fromUri(audioSource.url)); } + @OptIn(UnstableApi::class) + private fun swapAudioSourceUrlWidevine(audioSource: IAudioUrlWidevineSource) { + Logger.i(TAG, "Loading AudioSource [UrlWidevine]") + val dataSource = if (audioSource is JSSource && audioSource.hasRequestModifier) + audioSource.getHttpDataSourceFactory() + else + DefaultHttpDataSource.Factory().setUserAgent(DEFAULT_USER_AGENT) + + val httpRequestHeaders = mapOf("Authorization" to "Bearer " + audioSource.bearerToken) + val provider = DefaultDrmSessionManagerProvider() + provider.setDrmHttpDataSourceFactory(dataSource) + _lastAudioMediaSource = ProgressiveMediaSource.Factory(dataSource) + .setDrmSessionManagerProvider(provider) + .createMediaSource( + MediaItem.Builder() + .setUri(audioSource.getAudioUrl()).setDrmConfiguration( + MediaItem.DrmConfiguration.Builder(C.WIDEVINE_UUID) + .setLicenseUri(audioSource.licenseUri) + .setMultiSession(true) + .setLicenseRequestHeaders(httpRequestHeaders) + .build() + ).build() + ) + } + //Prefered source selection fun getPreferredVideoSource(video: IPlatformVideoDetails, targetPixels: Int = -1): IVideoSource? {