diff --git a/app/build.gradle b/app/build.gradle
index b5c68c4f2e02de9773e305115e7d0074b83249b7..8214f0b6c5b0f52a38a4b5fbbdf38bc53a4f1bc6 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -172,13 +172,14 @@ dependencies {
     implementation("com.caoccao.javet:javet-android:2.2.1")
 
     //Exoplayer
-    implementation 'com.google.android.exoplayer:exoplayer-core:2.19.1'
-    implementation 'com.google.android.exoplayer:exoplayer-dash:2.19.1'
-    implementation 'com.google.android.exoplayer:exoplayer-ui:2.19.1'
-    implementation 'com.google.android.exoplayer:exoplayer-hls:2.19.1'
-    implementation 'com.google.android.exoplayer:exoplayer-rtsp:2.19.1'
-    implementation 'com.google.android.exoplayer:exoplayer-smoothstreaming:2.19.1'
-    implementation 'com.google.android.exoplayer:exoplayer-transformer:2.19.1'
+    implementation 'androidx.media3:media3-exoplayer:1.2.0'
+    implementation 'androidx.media3:media3-exoplayer-dash:1.2.0'
+    implementation 'androidx.media3:media3-ui:1.2.0'
+    implementation 'androidx.media3:media3-session:1.2.0'
+    implementation 'androidx.media3:media3-exoplayer-hls:1.2.0'
+    implementation 'androidx.media3:media3-exoplayer-rtsp:1.2.0'
+    implementation 'androidx.media3:media3-exoplayer-smoothstreaming:1.2.0'
+    implementation 'androidx.media3:media3-transformer:1.2.0'
     implementation 'androidx.navigation:navigation-fragment-ktx:2.7.5'
     implementation 'androidx.navigation:navigation-ui-ktx:2.7.5'
 
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 fb7f05dd46124ac60a1b1b6595903051bba07efc..9bf35ad204819d5056caa4a33860de75b51f8f55 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
@@ -1,7 +1,7 @@
-@file:Suppress("DEPRECATION")
-
 package com.futo.platformplayer.api.media.platforms.js.models.sources
 
+import androidx.media3.datasource.DefaultHttpDataSource
+import androidx.media3.datasource.HttpDataSource
 import com.caoccao.javet.values.V8Value
 import com.caoccao.javet.values.reference.V8ValueObject
 import com.futo.platformplayer.api.media.models.streams.sources.IAudioSource
@@ -11,8 +11,6 @@ import com.futo.platformplayer.engine.IV8PluginConfig
 import com.futo.platformplayer.engine.V8Plugin
 import com.futo.platformplayer.orNull
 import com.futo.platformplayer.views.video.datasources.JSHttpDataSource
-import com.google.android.exoplayer2.upstream.DefaultHttpDataSource
-import com.google.android.exoplayer2.upstream.HttpDataSource
 
 abstract class JSSource {
     protected val _config: IV8PluginConfig;
diff --git a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailView.kt b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailView.kt
index e7bc360fbd46a479358a5fa8916cfd38e1a2c5ce..4be113edd3c5fde9b4074354265ba2b09c0d3591 100644
--- a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailView.kt
+++ b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailView.kt
@@ -1,5 +1,3 @@
-@file:Suppress("DEPRECATION")
-
 package com.futo.platformplayer.fragment.mainactivity.main
 
 import android.app.PictureInPictureParams
@@ -14,7 +12,6 @@ import android.graphics.drawable.BitmapDrawable
 import android.graphics.drawable.Drawable
 import android.graphics.drawable.Icon
 import android.net.Uri
-import android.support.v4.media.session.PlaybackStateCompat
 import android.text.Spanned
 import android.util.AttributeSet
 import android.util.Log
@@ -32,6 +29,12 @@ import android.widget.LinearLayout
 import android.widget.TextView
 import androidx.constraintlayout.widget.ConstraintLayout
 import androidx.lifecycle.lifecycleScope
+import androidx.media3.common.C
+import androidx.media3.common.Format
+import androidx.media3.common.util.UnstableApi
+import androidx.media3.datasource.HttpDataSource
+import androidx.media3.ui.PlayerControlView
+import androidx.media3.ui.TimeBar
 import com.bumptech.glide.Glide
 import com.bumptech.glide.request.target.CustomTarget
 import com.bumptech.glide.request.transition.Transition
@@ -138,11 +141,6 @@ import com.futo.polycentric.core.ContentType
 import com.futo.polycentric.core.Models
 import com.futo.polycentric.core.Opinion
 import com.futo.polycentric.core.toURLInfoSystemLinkUrl
-import com.google.android.exoplayer2.C
-import com.google.android.exoplayer2.Format
-import com.google.android.exoplayer2.ui.PlayerControlView
-import com.google.android.exoplayer2.ui.TimeBar
-import com.google.android.exoplayer2.upstream.HttpDataSource.InvalidResponseCodeException
 import com.google.protobuf.ByteString
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -155,7 +153,6 @@ import java.time.OffsetDateTime
 import kotlin.math.abs
 import kotlin.math.roundToLong
 
-
 class VideoDetailView : ConstraintLayout {
     private val TAG = "VideoDetailView"
 
@@ -303,7 +300,7 @@ class VideoDetailView : ConstraintLayout {
         Pair(0, 10) //around live, try every 10 seconds
     );
 
-
+    @androidx.annotation.OptIn(UnstableApi::class)
     constructor(context: Context, attrs : AttributeSet? = null) : super(context, attrs) {
         inflate(context, R.layout.fragview_video_detail, this);
 
@@ -312,7 +309,7 @@ class VideoDetailView : ConstraintLayout {
         _cast = findViewById(R.id.videodetail_cast);
         _player = findViewById(R.id.videodetail_player);
         _playerProgress = findViewById(R.id.videodetail_progress);
-        _timeBar = _playerProgress.findViewById(com.google.android.exoplayer2.ui.R.id.exo_progress);
+        _timeBar = _playerProgress.findViewById(androidx.media3.ui.R.id.exo_progress);
         _title = findViewById(R.id.videodetail_title);
         _subTitle = findViewById(R.id.videodetail_meta);
         _platform = findViewById(R.id.videodetail_platform);
@@ -574,8 +571,8 @@ class VideoDetailView : ConstraintLayout {
         }
 
         _playerProgress.player = _player.exoPlayer?.player;
-        _playerProgress.setProgressUpdateListener { position, _ ->
-            StatePlayer.instance.updateMediaSessionPlaybackState(_player.exoPlayer?.getPlaybackStateCompat() ?: PlaybackStateCompat.STATE_NONE, position);
+        _playerProgress.setProgressUpdateListener { _, _ ->
+            StatePlayer.instance.updateMediaSessionPlaybackState();
         }
 
         StatePlayer.instance.onQueueChanged.subscribe(this) {
@@ -1497,6 +1494,7 @@ class VideoDetailView : ConstraintLayout {
     }
 
     //Events
+    @androidx.annotation.OptIn(UnstableApi::class)
     private fun onSourceChanged(videoSource: IVideoSource?, audioSource: IAudioSource?, resume: Boolean){
         Logger.i(TAG, "onSourceChanged(videoSource=$videoSource, audioSource=$audioSource, resume=$resume)")
 
@@ -1532,7 +1530,7 @@ class VideoDetailView : ConstraintLayout {
     private var _didTriggerDatasourceError = false;
     private fun onDataSourceError(exception: Throwable) {
         Logger.e(TAG, "onDataSourceError", exception);
-        if(exception.cause != null && exception.cause is InvalidResponseCodeException && (exception.cause!! as InvalidResponseCodeException).responseCode == 403) {
+        if(exception.cause != null && exception.cause is HttpDataSource.InvalidResponseCodeException && (exception.cause!! as HttpDataSource.InvalidResponseCodeException).responseCode == 403) {
             val currentVideo = video
             if(currentVideo == null || currentVideo !is IPluginSourced)
                 return;
@@ -1611,6 +1609,7 @@ class VideoDetailView : ConstraintLayout {
         val v = video ?: return;
         updateQualitySourcesOverlay(v, videoLocal, liveStreamVideoFormats, liveStreamAudioFormats);
     }
+    @androidx.annotation.OptIn(UnstableApi::class)
     private fun updateQualitySourcesOverlay(videoDetails: IPlatformVideoDetails?, videoLocal: VideoLocal? = null, liveStreamVideoFormats: List<Format>? = null, liveStreamAudioFormats: List<Format>? = null) {
         Logger.i(TAG, "updateQualitySourcesOverlay");
 
@@ -1730,7 +1729,7 @@ class VideoDetailView : ConstraintLayout {
                                 { handleSelectAudioTrack(it) });
                         }.toList().toTypedArray())
             else null,
-            if(video?.subtitles?.isNotEmpty() ?: false && video != null)
+            if(video?.subtitles?.isNotEmpty() == true)
                 SlideUpMenuGroup(this.context, context.getString(R.string.subtitles), "subtitles",
                     *video.subtitles
                         .map {
@@ -1792,7 +1791,7 @@ class VideoDetailView : ConstraintLayout {
             _cast.setIsPlaying(playing);
         } else {
             StatePlayer.instance.updateMediaSession( null);
-            StatePlayer.instance.updateMediaSessionPlaybackState(_player.exoPlayer?.getPlaybackStateCompat() ?: PlaybackStateCompat.STATE_NONE, _player.exoPlayer?.player?.currentPosition ?: 0);
+            StatePlayer.instance.updateMediaSessionPlaybackState();
         }
 
         if(playing) {
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 3c03472cf9bf930306b7e485a715fcef889f7f1e..7cf603ec2b044a5d97c85d93d6c1a560c791d246 100644
--- a/app/src/main/java/com/futo/platformplayer/helpers/VideoHelper.kt
+++ b/app/src/main/java/com/futo/platformplayer/helpers/VideoHelper.kt
@@ -1,8 +1,7 @@
-@file:Suppress("DEPRECATION")
-
 package com.futo.platformplayer.helpers
 
 import android.net.Uri
+import androidx.annotation.OptIn
 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
@@ -15,11 +14,12 @@ import com.futo.platformplayer.api.media.models.video.IPlatformVideoDetails
 import com.futo.platformplayer.api.media.platforms.js.models.sources.JSAudioUrlRangeSource
 import com.futo.platformplayer.api.media.platforms.js.models.sources.JSVideoUrlRangeSource
 import com.futo.platformplayer.logging.Logger
-import com.google.android.exoplayer2.MediaItem
-import com.google.android.exoplayer2.source.MediaSource
-import com.google.android.exoplayer2.source.dash.DashMediaSource
-import com.google.android.exoplayer2.source.dash.manifest.DashManifestParser
-import com.google.android.exoplayer2.upstream.ResolvingDataSource
+import androidx.media3.common.MediaItem
+import androidx.media3.common.util.UnstableApi
+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 kotlin.math.abs
 
 class VideoHelper {
@@ -121,7 +121,7 @@ class VideoHelper {
             return bestSource;
         }
 
-        @Suppress("DEPRECATION")
+        @OptIn(UnstableApi::class)
         fun convertItagSourceToChunkedDashSource(videoSource: JSVideoUrlRangeSource) : MediaSource {
             val urlToUse = videoSource.getVideoUrl();
             val manifestConfig = ProgressiveDashManifestCreator.fromVideoProgressiveStreamingUrl(urlToUse,
@@ -140,14 +140,13 @@ class VideoHelper {
             );
 
             val manifest = DashManifestParser().parse(Uri.parse(""), manifestConfig.byteInputStream());
-
             return DashMediaSource.Factory(ResolvingDataSource.Factory(videoSource.getHttpDataSourceFactory(), ResolvingDataSource.Resolver { dataSpec ->
                 Logger.v("PLAYBACK", "Video REQ Range [" + dataSpec.position + "-" + (dataSpec.position + dataSpec.length) + "](" + dataSpec.length + ")", null);
                 return@Resolver dataSpec;
             })).createMediaSource(manifest, MediaItem.Builder().setUri(Uri.parse(videoSource.getVideoUrl())).build())
         }
 
-        @Suppress("DEPRECATION")
+        @OptIn(UnstableApi::class)
         fun convertItagSourceToChunkedDashSource(audioSource: JSAudioUrlRangeSource) : MediaSource {
             val manifestConfig = ProgressiveDashManifestCreator.fromAudioProgressiveStreamingUrl(audioSource.getAudioUrl(),
                 audioSource.duration?.times(1000) ?: 0,
diff --git a/app/src/main/java/com/futo/platformplayer/receivers/InstallReceiver.kt b/app/src/main/java/com/futo/platformplayer/receivers/InstallReceiver.kt
index 09e4115012c30a6489b7962a489f037381ca6390..9112b6d5bd2281c50996580b88407a6afb6c2e65 100644
--- a/app/src/main/java/com/futo/platformplayer/receivers/InstallReceiver.kt
+++ b/app/src/main/java/com/futo/platformplayer/receivers/InstallReceiver.kt
@@ -17,7 +17,6 @@ class InstallReceiver : BroadcastReceiver() {
         val onReceiveResult = Event1<String?>();
     }
 
-    @Suppress("DEPRECATION")
     override fun onReceive(context: Context, intent: Intent) {
         val status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, -1);
         Logger.i(TAG, "Received status $status.");
diff --git a/app/src/main/java/com/futo/platformplayer/services/MediaPlaybackService.kt b/app/src/main/java/com/futo/platformplayer/services/MediaPlaybackService.kt
index ba73594e98fdf1272e9669b352960fbb76549af4..437fb960e582c054e6b60c56e12e91290e263c24 100644
--- a/app/src/main/java/com/futo/platformplayer/services/MediaPlaybackService.kt
+++ b/app/src/main/java/com/futo/platformplayer/services/MediaPlaybackService.kt
@@ -13,15 +13,14 @@ import android.graphics.drawable.Drawable
 import android.media.AudioFocusRequest
 import android.media.AudioManager
 import android.media.AudioManager.OnAudioFocusChangeListener
-import android.media.MediaMetadata
 import android.os.Build
 import android.os.IBinder
-import android.os.SystemClock
-import android.support.v4.media.MediaMetadataCompat
-import android.support.v4.media.session.MediaSessionCompat
-import android.support.v4.media.session.PlaybackStateCompat
 import android.util.Log
+import androidx.annotation.OptIn
 import androidx.core.app.NotificationCompat
+import androidx.media3.common.util.UnstableApi
+import androidx.media3.session.MediaSession
+import androidx.media3.session.MediaStyleNotificationHelper
 import com.bumptech.glide.Glide
 import com.bumptech.glide.request.target.CustomTarget
 import com.bumptech.glide.request.transition.Transition
@@ -51,7 +50,7 @@ class MediaPlaybackService : Service() {
     private var _audioManager: AudioManager? = null;
     private var _notificationManager: NotificationManager? = null;
     private var _notificationChannel: NotificationChannel? = null;
-    private var _mediaSession: MediaSessionCompat? = null;
+    private var _mediaSession: MediaSession? = null;
     private var _hasFocus: Boolean = false;
     private var _focusRequest: AudioFocusRequest? = null;
     private var _audioFocusLossTime_ms: Long? = null
@@ -82,6 +81,7 @@ class MediaPlaybackService : Service() {
 
         return START_STICKY;
     }
+    @OptIn(UnstableApi::class)
     fun setupNotificationRequirements() {
         _audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager;
         _notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager;
@@ -91,47 +91,54 @@ class MediaPlaybackService : Service() {
         };
         _notificationManager!!.createNotificationChannel(_notificationChannel!!);
 
-        _mediaSession = MediaSessionCompat(this, "PlayerState");
-        _mediaSession?.setPlaybackState(PlaybackStateCompat.Builder()
-            .setState(PlaybackStateCompat.STATE_PLAYING, 0, 1f)
-            .build());
-        _mediaSession?.setCallback(object: MediaSessionCompat.Callback() {
-            override fun onSeekTo(pos: Long) {
-                super.onSeekTo(pos)
-                Logger.i(TAG, "Media session callback onSeekTo(pos = $pos)");
-                MediaControlReceiver.onSeekToReceived.emit(pos);
-            }
+        _mediaSession = MediaSession.Builder(this, StatePlayer.instance.getPlayerOrCreate(this).player)
 
-            override fun onPlay() {
-                super.onPlay();
-                Logger.i(TAG, "Media session callback onPlay()");
-                MediaControlReceiver.onPlayReceived.emit();
-            }
+            .setCallback(object: MediaSession.Callback {
+                override fun onMediaButtonEvent(session: MediaSession, controllerInfo: MediaSession.ControllerInfo, intent: Intent): Boolean {
+                    //TODO: Reimplement
+                    return super.onMediaButtonEvent(session, controllerInfo, intent)
+                }
 
-            override fun onPause() {
-                super.onPause();
-                Logger.i(TAG, "Media session callback onPause()");
-                MediaControlReceiver.onPauseReceived.emit();
-            }
+                /*override fun onSeekTo(pos: Long) {
+                    super.onSeekTo(pos)
+                    Logger.i(TAG, "Media session callback onSeekTo(pos = $pos)");
+                    MediaControlReceiver.onSeekToReceived.emit(pos);
+                }
 
-            override fun onStop() {
-                super.onStop();
-                Logger.i(TAG, "Media session callback onStop()");
-                MediaControlReceiver.onCloseReceived.emit();
-            }
+                override fun onPlay() {
+                    super.onPlay();
+                    Logger.i(TAG, "Media session callback onPlay()");
+                    MediaControlReceiver.onPlayReceived.emit();
+                }
 
-            override fun onSkipToPrevious() {
-                super.onSkipToPrevious();
-                Logger.i(TAG, "Media session callback onSkipToPrevious()");
-                MediaControlReceiver.onPreviousReceived.emit();
-            }
+                override fun onPause() {
+                    super.onPause();
+                    Logger.i(TAG, "Media session callback onPause()");
+                    MediaControlReceiver.onPauseReceived.emit();
+                }
 
-            override fun onSkipToNext() {
-                super.onSkipToNext()
-                Logger.i(TAG, "Media session callback onSkipToNext()");
-                MediaControlReceiver.onNextReceived.emit();
-            }
-        });
+                override fun onStop() {
+                    super.onStop();
+                    Logger.i(TAG, "Media session callback onStop()");
+                    MediaControlReceiver.onCloseReceived.emit();
+                }
+
+                override fun onSkipToPrevious() {
+                    super.onSkipToPrevious();
+                    Logger.i(TAG, "Media session callback onSkipToPrevious()");
+                    MediaControlReceiver.onPreviousReceived.emit();
+                }
+
+                override fun onSkipToNext() {
+                    super.onSkipToNext()
+                    Logger.i(TAG, "Media session callback onSkipToNext()");
+                    MediaControlReceiver.onNextReceived.emit();
+                }*/
+            })
+            .build();
+        /*_mediaSession?.setPlaybackState(PlaybackStateCompat.Builder()
+            .setState(PlaybackStateCompat.STATE_PLAYING, 0, 1f)
+            .build());*/
     }
 
     override fun onCreate() {
@@ -186,15 +193,7 @@ class MediaPlaybackService : Service() {
         if(_notificationChannel == null || _mediaSession == null)
             setupNotificationRequirements();
 
-        _mediaSession?.setMetadata(
-            MediaMetadataCompat.Builder()
-                .putString(MediaMetadata.METADATA_KEY_ARTIST, video.author.name)
-                .putString(MediaMetadata.METADATA_KEY_TITLE, video.name)
-                .putLong(MediaMetadata.METADATA_KEY_DURATION, video.duration * 1000)
-                .build());
-
         val thumbnail = video.thumbnails.getHQThumbnail();
-
         _notif_last_video = video;
 
         if(isUpdating)
@@ -221,6 +220,7 @@ class MediaPlaybackService : Service() {
     private fun generateMediaAction(icon: Int, title: String, intent: PendingIntent) : NotificationCompat.Action {
         return NotificationCompat.Action.Builder(icon, title, intent).build();
     }
+    @OptIn(UnstableApi::class)
     private fun notifyMediaSession(video: IPlatformVideo?, desiredBitmap: Bitmap?) {
         val channel = _notificationChannel ?: return;
         val session = _mediaSession ?: return;
@@ -249,14 +249,9 @@ class MediaPlaybackService : Service() {
             .setOngoing(true)
             .setSilent(true)
             .setContentIntent(PendingIntent.getActivity(this, 5, bringUpIntent, PendingIntent.FLAG_IMMUTABLE))
-            .setStyle(if(hasQueue)
-                    androidx.media.app.NotificationCompat.MediaStyle()
-                        .setMediaSession(session.sessionToken)
-                        .setShowActionsInCompactView(0, 1, 2)
-                else
-                    androidx.media.app.NotificationCompat.MediaStyle()
-                        .setMediaSession(session.sessionToken)
-                        .setShowActionsInCompactView(0))
+            .setStyle(
+                if(hasQueue) MediaStyleNotificationHelper.MediaStyle(session)//.setShowActionsInCompactView(0, 1, 2)
+                else MediaStyleNotificationHelper.MediaStyle(session))//.setShowActionsInCompactView(0))
             .setDeleteIntent(deleteIntent)
             .setChannelId(channel.id)
 
@@ -303,7 +298,7 @@ class MediaPlaybackService : Service() {
         val notif = builder.build();
         notif.flags = notif.flags or NotificationCompat.FLAG_ONGOING_EVENT or NotificationCompat.FLAG_NO_CLEAR;
 
-        Logger.i(TAG, "Updating notification bitmap=${if (bitmap != null) "yes" else "no."} channelId=${channel.id} icon=${icon} video=${video?.name ?: ""} playWhenReady=${playWhenReady} session.sessionToken=${session.sessionToken}");
+        Logger.i(TAG, "Updating notification bitmap=${if (bitmap != null) "yes" else "no."} channelId=${channel.id} icon=${icon} video=${video?.name ?: ""} playWhenReady=${playWhenReady} session.sessionToken=${session.token}");
 
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
             // For API 29 and above
@@ -316,20 +311,7 @@ class MediaPlaybackService : Service() {
         _notif_last_bitmap = bitmap;
     }
 
-    fun updateMediaSessionPlaybackState(state: Int, pos: Long) {
-        _mediaSession?.setPlaybackState(
-            PlaybackStateCompat.Builder()
-            .setActions(
-                PlaybackStateCompat.ACTION_SEEK_TO or
-                PlaybackStateCompat.ACTION_PLAY or
-                PlaybackStateCompat.ACTION_PAUSE or
-                PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS or
-                PlaybackStateCompat.ACTION_SKIP_TO_NEXT or
-                PlaybackStateCompat.ACTION_PLAY_PAUSE
-            )
-            .setState(state, pos, 1f, SystemClock.elapsedRealtime())
-            .build());
-
+    fun updateMediaSessionPlaybackState() {
         if(_focusRequest == null)
             setAudioFocus();
     }
diff --git a/app/src/main/java/com/futo/platformplayer/states/StatePlayer.kt b/app/src/main/java/com/futo/platformplayer/states/StatePlayer.kt
index e1243a8de7c48a62d684bb1d0c4887104c0addbd..6f2ed3001255ac015548be0fd3b1695357bdb883 100644
--- a/app/src/main/java/com/futo/platformplayer/states/StatePlayer.kt
+++ b/app/src/main/java/com/futo/platformplayer/states/StatePlayer.kt
@@ -1,8 +1,12 @@
-@file:Suppress("DEPRECATION")
-
 package com.futo.platformplayer.states
 
 import android.content.Context
+import androidx.annotation.OptIn
+import androidx.media3.common.C
+import androidx.media3.common.util.UnstableApi
+import androidx.media3.exoplayer.DefaultLoadControl
+import androidx.media3.exoplayer.ExoPlayer
+import androidx.media3.exoplayer.upstream.DefaultAllocator
 import com.futo.platformplayer.R
 import com.futo.platformplayer.UIDialogs
 import com.futo.platformplayer.api.media.models.playlists.IPlatformPlaylistDetails
@@ -13,10 +17,6 @@ import com.futo.platformplayer.constructs.Event1
 import com.futo.platformplayer.models.Playlist
 import com.futo.platformplayer.services.MediaPlaybackService
 import com.futo.platformplayer.video.PlayerManager
-import com.google.android.exoplayer2.C
-import com.google.android.exoplayer2.DefaultLoadControl
-import com.google.android.exoplayer2.ExoPlayer
-import com.google.android.exoplayer2.upstream.DefaultAllocator
 import kotlin.random.Random
 
 /***
@@ -107,8 +107,8 @@ class StatePlayer {
     fun updateMediaSession(videoUpdated: IPlatformVideoDetails?) {
         MediaPlaybackService.getService()?.updateMediaSession(videoUpdated);
     }
-    fun updateMediaSessionPlaybackState(state: Int, pos: Long) {
-        MediaPlaybackService.getService()?.updateMediaSessionPlaybackState(state, pos);
+    fun updateMediaSessionPlaybackState() {
+        MediaPlaybackService.getService()?.updateMediaSessionPlaybackState();
     }
     fun closeMediaSession() {
         MediaPlaybackService.getService()?.closeMediaSession();
@@ -557,21 +557,23 @@ class StatePlayer {
     }
 
     //Player Initialization
-    fun getPlayerOrCreate(context : Context) : PlayerManager {
+    fun getPlayerOrCreate(context: Context) : PlayerManager {
         if(_exoplayer == null) {
             val player = createExoPlayer(context);
             _exoplayer = PlayerManager(player);
         }
         return _exoplayer!!;
     }
-    fun getThumbnailPlayerOrCreate(context : Context) : PlayerManager {
+    fun getThumbnailPlayerOrCreate(context: Context) : PlayerManager {
         if(_thumbnailExoPlayer == null) {
             val player = createExoPlayer(context);
             _thumbnailExoPlayer = PlayerManager(player);
         }
         return _thumbnailExoPlayer!!;
     }
-    private fun createExoPlayer(context : Context) : ExoPlayer {
+
+    @OptIn(UnstableApi::class)
+    private fun createExoPlayer(context : Context): ExoPlayer {
         return ExoPlayer.Builder(context)
             .setLoadControl(
                 DefaultLoadControl.Builder()
@@ -589,7 +591,6 @@ class StatePlayer {
             .build();
     }
 
-
     fun dispose(){
         val player = _exoplayer;
         val thumbPlayer = _thumbnailExoPlayer;
diff --git a/app/src/main/java/com/futo/platformplayer/video/PlayerManager.kt b/app/src/main/java/com/futo/platformplayer/video/PlayerManager.kt
index adce7f3ae4f2a31c2f5f5cb6eb4ed04e1537f13e..05a6eef0b0a42e10b35718b9c69d6e1b4aedc2b0 100644
--- a/app/src/main/java/com/futo/platformplayer/video/PlayerManager.kt
+++ b/app/src/main/java/com/futo/platformplayer/video/PlayerManager.kt
@@ -1,15 +1,11 @@
-@file:Suppress("DEPRECATION")
-
 package com.futo.platformplayer.video
 
-import android.media.session.PlaybackState
-import android.support.v4.media.session.PlaybackStateCompat
-import com.google.android.exoplayer2.ExoPlayer
-import com.google.android.exoplayer2.Player
-import com.google.android.exoplayer2.ui.StyledPlayerView
+import androidx.media3.common.Player
+import androidx.media3.exoplayer.ExoPlayer
+import androidx.media3.ui.PlayerView
 
 class PlayerManager {
-    private var _currentView: StyledPlayerView? = null;
+    private var _currentView: PlayerView? = null;
     private val _stateMap = HashMap<String, PlayerState>();
     private var _currentState: PlayerState? = null;
     val currentState: PlayerState get() {
@@ -25,16 +21,8 @@ class PlayerManager {
         this.player = exoPlayer;
     }
 
-    fun getPlaybackStateCompat() : Int {
-        return when(player.playbackState) {
-            ExoPlayer.STATE_READY -> if(player.playWhenReady) PlaybackStateCompat.STATE_PLAYING else PlaybackStateCompat.STATE_PAUSED;
-            ExoPlayer.STATE_BUFFERING -> PlaybackState.STATE_BUFFERING;
-            else -> PlaybackState.STATE_NONE
-        }
-    }
-
     @Synchronized
-    fun attach(view: StyledPlayerView, stateName: String) {
+    fun attach(view: PlayerView, stateName: String) {
         if(view != _currentView) {
             _currentView?.player = null;
             switchState(stateName);
diff --git a/app/src/main/java/com/futo/platformplayer/views/casting/CastView.kt b/app/src/main/java/com/futo/platformplayer/views/casting/CastView.kt
index 535520a7597540cdd2791fc392a5ac4ebdd88ff9..52106dd621757e7db933d3e2ba613b2d6b76e260 100644
--- a/app/src/main/java/com/futo/platformplayer/views/casting/CastView.kt
+++ b/app/src/main/java/com/futo/platformplayer/views/casting/CastView.kt
@@ -1,10 +1,6 @@
-@file:Suppress("DEPRECATION")
-
 package com.futo.platformplayer.views.casting
 
 import android.content.Context
-import android.media.session.PlaybackState
-import android.support.v4.media.session.PlaybackStateCompat
 import android.util.AttributeSet
 import android.util.TypedValue
 import android.view.LayoutInflater
@@ -13,7 +9,11 @@ import android.widget.FrameLayout
 import android.widget.ImageButton
 import android.widget.ImageView
 import android.widget.TextView
+import androidx.annotation.OptIn
 import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.media3.common.util.UnstableApi
+import androidx.media3.ui.DefaultTimeBar
+import androidx.media3.ui.TimeBar
 import com.bumptech.glide.Glide
 import com.futo.platformplayer.R
 import com.futo.platformplayer.api.media.models.video.IPlatformVideoDetails
@@ -23,9 +23,6 @@ import com.futo.platformplayer.constructs.Event0
 import com.futo.platformplayer.states.StatePlayer
 import com.futo.platformplayer.toHumanTime
 import com.futo.platformplayer.views.behavior.GestureControlView
-import com.google.android.exoplayer2.ui.DefaultTimeBar
-import com.google.android.exoplayer2.ui.TimeBar
-import com.google.android.exoplayer2.ui.TimeBar.OnScrubListener
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.Job
@@ -54,6 +51,7 @@ class CastView : ConstraintLayout {
     val onMinimizeClick = Event0();
     val onSettingsClick = Event0();
 
+    @OptIn(UnstableApi::class)
     constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
         LayoutInflater.from(context).inflate(R.layout.view_cast, this, true);
 
@@ -83,7 +81,7 @@ class CastView : ConstraintLayout {
         }
         _buttonLoop.setImageResource(if(StatePlayer.instance.loopVideo) R.drawable.ic_loop_active else R.drawable.ic_loop);
 
-        _timeBar.addListener(object : OnScrubListener {
+        _timeBar.addListener(object : TimeBar.OnScrubListener {
             override fun onScrubStart(timeBar: TimeBar, position: Long) {
                 StateCasting.instance.videoSeekTo(position.toDouble());
             }
@@ -149,11 +147,9 @@ class CastView : ConstraintLayout {
             _buttonPlay.visibility = View.VISIBLE;
         }
 
-        val position = StateCasting.instance.activeDevice?.expectedCurrentTime?.times(1000.0)?.toLong();
-
         if(StatePlayer.instance.hasMediaSession()) {
             StatePlayer.instance.updateMediaSession(null);
-            StatePlayer.instance.updateMediaSessionPlaybackState(getPlaybackStateCompat(), (position ?: 0));
+            StatePlayer.instance.updateMediaSessionPlaybackState();
         }
     }
 
@@ -183,6 +179,7 @@ class CastView : ConstraintLayout {
         }
     }
 
+    @OptIn(UnstableApi::class)
     fun setVideoDetails(video: IPlatformVideoDetails, position: Long) {
         Glide.with(_thumbnail)
             .load(video.thumbnails.getHQThumbnail())
@@ -194,10 +191,11 @@ class CastView : ConstraintLayout {
         _timeBar.setDuration(video.duration);
     }
 
+    @OptIn(UnstableApi::class)
     fun setTime(ms: Long) {
         _textPosition.text = ms.toHumanTime(true);
         _timeBar.setPosition(ms / 1000);
-        StatePlayer.instance.updateMediaSessionPlaybackState(getPlaybackStateCompat(), ms);
+        StatePlayer.instance.updateMediaSessionPlaybackState();
     }
 
     fun cleanup() {
@@ -207,13 +205,4 @@ class CastView : ConstraintLayout {
         _updateTimeJob = null;
         _scope.cancel();
     }
-
-    private fun getPlaybackStateCompat(): Int {
-        val d = StateCasting.instance.activeDevice ?: return PlaybackState.STATE_NONE;
-
-        return when(d.isPlaying) {
-            true -> PlaybackStateCompat.STATE_PLAYING;
-            else -> PlaybackStateCompat.STATE_PAUSED;
-        }
-    }
 }
\ No newline at end of file
diff --git a/app/src/main/java/com/futo/platformplayer/views/video/FutoThumbnailPlayer.kt b/app/src/main/java/com/futo/platformplayer/views/video/FutoThumbnailPlayer.kt
index f470115502df6c832cc60894f3dd4e0a9103fa1c..453c5ed0d37a5159387105febc0cf9a627d10bc4 100644
--- a/app/src/main/java/com/futo/platformplayer/views/video/FutoThumbnailPlayer.kt
+++ b/app/src/main/java/com/futo/platformplayer/views/video/FutoThumbnailPlayer.kt
@@ -1,5 +1,3 @@
-@file:Suppress("DEPRECATION")
-
 package com.futo.platformplayer.views.video
 
 import android.content.Context
@@ -9,6 +7,10 @@ import android.view.View
 import android.widget.ImageButton
 import android.widget.LinearLayout
 import android.widget.TextView
+import androidx.annotation.OptIn
+import androidx.media3.common.util.UnstableApi
+import androidx.media3.ui.PlayerControlView
+import androidx.media3.ui.PlayerView
 import com.futo.platformplayer.R
 import com.futo.platformplayer.Settings
 import com.futo.platformplayer.api.media.models.streams.sources.IAudioSource
@@ -17,8 +19,6 @@ import com.futo.platformplayer.api.media.models.video.IPlatformVideoDetails
 import com.futo.platformplayer.helpers.VideoHelper
 import com.futo.platformplayer.toHumanTime
 import com.futo.platformplayer.video.PlayerManager
-import com.google.android.exoplayer2.ui.PlayerControlView
-import com.google.android.exoplayer2.ui.StyledPlayerView
 
 
 class FutoThumbnailPlayer : FutoVideoPlayerBase {
@@ -28,7 +28,7 @@ class FutoThumbnailPlayer : FutoVideoPlayerBase {
     }
 
     //Views
-    private val videoView : StyledPlayerView;
+    private val videoView : PlayerView;
     private val videoControls : PlayerControlView;
     private val buttonMute : ImageButton;
     private val buttonUnMute : ImageButton;
@@ -41,7 +41,8 @@ class FutoThumbnailPlayer : FutoVideoPlayerBase {
     private val _evMuteChanged = mutableListOf<(FutoThumbnailPlayer, Boolean)->Unit>();
 
 
-    constructor(context : Context, attrs: AttributeSet? = null) : super(PLAYER_STATE_NAME, context, attrs) {
+    @OptIn(UnstableApi::class)
+    constructor(context: Context, attrs: AttributeSet? = null) : super(PLAYER_STATE_NAME, context, attrs) {
         LayoutInflater.from(context).inflate(R.layout.thumbnail_video_view, this, true);
 
         videoView = findViewById(R.id.video_player);
@@ -70,7 +71,7 @@ class FutoThumbnailPlayer : FutoVideoPlayerBase {
         }
     }
 
-    fun setLive(live : Boolean) {
+    fun setLive(live: Boolean) {
         if(live) {
             containerDuration.visibility = GONE;
             containerLive.visibility = VISIBLE;
@@ -81,7 +82,8 @@ class FutoThumbnailPlayer : FutoVideoPlayerBase {
         }
     }
 
-    fun setPlayer(player : PlayerManager?){
+    @OptIn(UnstableApi::class)
+    fun setPlayer(player: PlayerManager?){
         changePlayer(player);
         player?.attach(videoView, PLAYER_STATE_NAME);
         videoControls.player = player?.player;
diff --git a/app/src/main/java/com/futo/platformplayer/views/video/FutoVideoPlayer.kt b/app/src/main/java/com/futo/platformplayer/views/video/FutoVideoPlayer.kt
index d306b090b890cd0fb1692db0153c47c192da29e9..ad606f267b178dc321aca939f785cc2f7e3590f7 100644
--- a/app/src/main/java/com/futo/platformplayer/views/video/FutoVideoPlayer.kt
+++ b/app/src/main/java/com/futo/platformplayer/views/video/FutoVideoPlayer.kt
@@ -1,5 +1,3 @@
-@file:Suppress("DEPRECATION")
-
 package com.futo.platformplayer.views.video
 
 import android.content.Context
@@ -15,6 +13,7 @@ import android.view.ViewGroup.LayoutParams.MATCH_PARENT
 import android.widget.FrameLayout
 import android.widget.ImageButton
 import android.widget.TextView
+import androidx.annotation.OptIn
 import androidx.constraintlayout.widget.ConstraintLayout
 import androidx.core.view.setMargins
 import com.futo.platformplayer.R
@@ -31,13 +30,14 @@ import com.futo.platformplayer.logging.Logger
 import com.futo.platformplayer.states.StateApp
 import com.futo.platformplayer.states.StatePlayer
 import com.futo.platformplayer.views.behavior.GestureControlView
-import com.google.android.exoplayer2.ExoPlayer
-import com.google.android.exoplayer2.PlaybackParameters
-import com.google.android.exoplayer2.ui.AspectRatioFrameLayout
-import com.google.android.exoplayer2.ui.PlayerControlView
-import com.google.android.exoplayer2.ui.StyledPlayerView
-import com.google.android.exoplayer2.ui.TimeBar
-import com.google.android.exoplayer2.video.VideoSize
+import androidx.media3.common.PlaybackParameters
+import androidx.media3.common.VideoSize
+import androidx.media3.common.util.UnstableApi
+import androidx.media3.exoplayer.ExoPlayer
+import androidx.media3.ui.AspectRatioFrameLayout
+import androidx.media3.ui.PlayerControlView
+import androidx.media3.ui.PlayerView
+import androidx.media3.ui.TimeBar
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
@@ -58,7 +58,7 @@ class FutoVideoPlayer : FutoVideoPlayerBase {
 
     //Views
     private val _root: ConstraintLayout;
-    private val _videoView: StyledPlayerView;
+    private val _videoView: PlayerView;
 
     val videoControls: PlayerControlView;
     private val _videoControls_fullscreen: PlayerControlView;
@@ -127,6 +127,7 @@ class FutoVideoPlayer : FutoVideoPlayerBase {
     val onVideoClicked = Event0();
     val onTimeBarChanged = Event2<Long, Long>();
 
+    @OptIn(UnstableApi::class)
     constructor(context: Context, attrs: AttributeSet? = null) : super(PLAYER_STATE_NAME, context, attrs) {
         LayoutInflater.from(context).inflate(R.layout.video_view, this, true);
         _root = findViewById(R.id.videoview_root);
@@ -139,8 +140,8 @@ class FutoVideoPlayer : FutoVideoPlayerBase {
         _control_rotate_lock = videoControls.findViewById(R.id.exo_rotate_lock);
         _control_loop = videoControls.findViewById(R.id.exo_loop);
         _control_cast = videoControls.findViewById(R.id.exo_cast);
-        _control_play = videoControls.findViewById(com.google.android.exoplayer2.ui.R.id.exo_play);
-        _time_bar = videoControls.findViewById(com.google.android.exoplayer2.ui.R.id.exo_progress);
+        _control_play = videoControls.findViewById(androidx.media3.ui.R.id.exo_play);
+        _time_bar = videoControls.findViewById(androidx.media3.ui.R.id.exo_progress);
         _control_chapter = videoControls.findViewById(R.id.text_chapter_current);
         _buttonNext = videoControls.findViewById(R.id.button_next);
         _buttonPrevious = videoControls.findViewById(R.id.button_previous);
@@ -152,9 +153,9 @@ class FutoVideoPlayer : FutoVideoPlayerBase {
         _control_rotate_lock_fullscreen = _videoControls_fullscreen.findViewById(R.id.exo_rotate_lock);
         _control_loop_fullscreen = videoControls.findViewById(R.id.exo_loop);
         _control_cast_fullscreen = _videoControls_fullscreen.findViewById(R.id.exo_cast);
-        _control_play_fullscreen = videoControls.findViewById(com.google.android.exoplayer2.ui.R.id.exo_play);
+        _control_play_fullscreen = videoControls.findViewById(androidx.media3.ui.R.id.exo_play);
         _control_chapter_fullscreen = _videoControls_fullscreen.findViewById(R.id.text_chapter_current);
-        _time_bar_fullscreen = _videoControls_fullscreen.findViewById(com.google.android.exoplayer2.ui.R.id.exo_progress);
+        _time_bar_fullscreen = _videoControls_fullscreen.findViewById(androidx.media3.ui.R.id.exo_progress);
         _buttonPrevious_fullscreen = _videoControls_fullscreen.findViewById(R.id.button_previous);
         _buttonNext_fullscreen = _videoControls_fullscreen.findViewById(R.id.button_next);
 
@@ -404,14 +405,15 @@ class FutoVideoPlayer : FutoVideoPlayerBase {
         return false;
     }
 
+    @OptIn(UnstableApi::class)
     fun setArtwork(drawable: Drawable?) {
         if (drawable != null) {
             _videoView.defaultArtwork = drawable;
-            _videoView.artworkDisplayMode = StyledPlayerView.ARTWORK_DISPLAY_MODE_FILL;
+            _videoView.artworkDisplayMode = PlayerView.ARTWORK_DISPLAY_MODE_FILL;
             fitOrFill(isFullScreen);
         } else {
             _videoView.defaultArtwork = null;
-            _videoView.artworkDisplayMode = StyledPlayerView.ARTWORK_DISPLAY_MODE_OFF;
+            _videoView.artworkDisplayMode = PlayerView.ARTWORK_DISPLAY_MODE_OFF;
         }
     }
 
@@ -436,6 +438,7 @@ class FutoVideoPlayer : FutoVideoPlayerBase {
         return exoPlayer?.player?.playbackParameters?.speed ?: 1.0f;
     }
 
+    @OptIn(UnstableApi::class)
     fun setFullScreen(fullScreen: Boolean) {
         if (isFullScreen == fullScreen) {
             return;
@@ -538,6 +541,7 @@ class FutoVideoPlayer : FutoVideoPlayerBase {
     }
 
     //Sizing
+    @OptIn(UnstableApi::class)
     fun fitHeight(videoSize : VideoSize? = null){
         Logger.i(TAG, "Video Fit Height");
         if(_originalBottomMargin != 0) {
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 9a23b935c60efd8030816e10e718391b8d7336e1..9ccc0a6d8b4c747eb8ad55c6ba976a8d30ec5371 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
@@ -1,11 +1,27 @@
-@file:Suppress("DEPRECATION")
-
 package com.futo.platformplayer.views.video
 
 import android.content.Context
 import android.net.Uri
 import android.util.AttributeSet
 import android.widget.RelativeLayout
+import androidx.annotation.OptIn
+import androidx.media3.common.C
+import androidx.media3.common.MediaItem
+import androidx.media3.common.PlaybackException
+import androidx.media3.common.Player
+import androidx.media3.common.VideoSize
+import androidx.media3.common.text.CueGroup
+import androidx.media3.common.util.UnstableApi
+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.hls.HlsMediaSource
+import androidx.media3.exoplayer.source.MediaSource
+import androidx.media3.exoplayer.source.MergingMediaSource
+import androidx.media3.exoplayer.source.ProgressiveMediaSource
+import androidx.media3.exoplayer.source.SingleSampleMediaSource
+import androidx.media3.exoplayer.trackselection.DefaultTrackSelector
 import com.futo.platformplayer.Settings
 import com.futo.platformplayer.api.media.models.chapters.IChapter
 import com.futo.platformplayer.api.media.models.streams.VideoMuxedSourceDescriptor
@@ -28,22 +44,6 @@ import com.futo.platformplayer.helpers.VideoHelper
 import com.futo.platformplayer.logging.Logger
 import com.futo.platformplayer.states.StateApp
 import com.futo.platformplayer.video.PlayerManager
-import com.google.android.exoplayer2.C
-import com.google.android.exoplayer2.ExoPlayer
-import com.google.android.exoplayer2.MediaItem
-import com.google.android.exoplayer2.PlaybackException
-import com.google.android.exoplayer2.Player
-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.source.SingleSampleMediaSource
-import com.google.android.exoplayer2.source.dash.DashMediaSource
-import com.google.android.exoplayer2.source.hls.HlsMediaSource
-import com.google.android.exoplayer2.text.CueGroup
-import com.google.android.exoplayer2.trackselection.DefaultTrackSelector
-import com.google.android.exoplayer2.upstream.DefaultDataSource
-import com.google.android.exoplayer2.upstream.DefaultHttpDataSource
-import com.google.android.exoplayer2.video.VideoSize
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
@@ -251,6 +251,7 @@ abstract class FutoVideoPlayerBase : RelativeLayout {
         _targetTrackAudioBitrate = bitrate;
         updateTrackSelector();
     }
+    @OptIn(UnstableApi::class)
     private fun updateTrackSelector() {
         var builder = DefaultTrackSelector.Parameters.Builder(context);
         if(_targetTrackVideoHeight > 0) {
@@ -298,6 +299,7 @@ abstract class FutoVideoPlayerBase : RelativeLayout {
         return loadSelectedSources(play, resume);
     }
 
+    @OptIn(UnstableApi::class)
     fun swapSubtitles(scope: CoroutineScope, subtitles: ISubtitleSource?) {
         if(subtitles == null)
             clearSubtitles();
@@ -369,6 +371,7 @@ abstract class FutoVideoPlayerBase : RelativeLayout {
     }
 
     //Video loads
+    @OptIn(UnstableApi::class)
     private fun swapVideoSourceLocal(videoSource: LocalVideoSource) {
         Logger.i(TAG, "Loading VideoSource [Local]");
         val file = File(videoSource.filePath);
@@ -377,14 +380,13 @@ abstract class FutoVideoPlayerBase : RelativeLayout {
         _lastVideoMediaSource = ProgressiveMediaSource.Factory(DefaultDataSource.Factory(context))
             .createMediaSource(MediaItem.fromUri(Uri.fromFile(file)));
     }
+    @OptIn(UnstableApi::class)
     private fun swapVideoSourceUrlRange(videoSource: JSVideoUrlRangeSource) {
         Logger.i(TAG, "Loading JSVideoUrlRangeSource");
         if(videoSource.hasItag) {
             //Temporary workaround for Youtube
             try {
                 _lastVideoMediaSource = VideoHelper.convertItagSourceToChunkedDashSource(videoSource);
-                if(_lastVideoMediaSource == null)
-                    throw java.lang.IllegalStateException("Dash manifest workaround failed");
                 return;
             }
             //If it fails to create the dash workaround, fallback to standard progressive
@@ -397,18 +399,21 @@ abstract class FutoVideoPlayerBase : RelativeLayout {
         }
         else throw IllegalArgumentException("source without itag data...");
     }
+    @OptIn(UnstableApi::class)
     private fun swapVideoSourceUrl(videoSource: IVideoUrlSource) {
         Logger.i(TAG, "Loading VideoSource [Url]");
         _lastVideoMediaSource = ProgressiveMediaSource.Factory(DefaultHttpDataSource.Factory()
             .setUserAgent(DEFAULT_USER_AGENT))
             .createMediaSource(MediaItem.fromUri(videoSource.getVideoUrl()));
     }
+    @OptIn(UnstableApi::class)
     private fun swapVideoSourceDash(videoSource: IDashManifestSource) {
         Logger.i(TAG, "Loading VideoSource [Dash]");
         _lastVideoMediaSource = DashMediaSource.Factory(DefaultHttpDataSource.Factory()
             .setUserAgent(DEFAULT_USER_AGENT))
             .createMediaSource(MediaItem.fromUri(videoSource.url))
     }
+    @OptIn(UnstableApi::class)
     private fun swapVideoSourceHLS(videoSource: IHLSManifestSource) {
         Logger.i(TAG, "Loading VideoSource [HLS]");
         _lastVideoMediaSource = HlsMediaSource.Factory(DefaultHttpDataSource.Factory()
@@ -416,7 +421,9 @@ abstract class FutoVideoPlayerBase : RelativeLayout {
             .createMediaSource(MediaItem.fromUri(videoSource.url));
     }
 
+
     //Audio loads
+    @OptIn(UnstableApi::class)
     private fun swapAudioSourceLocal(audioSource: LocalAudioSource) {
         Logger.i(TAG, "Loading AudioSource [Local]");
         val file = File(audioSource.filePath);
@@ -425,6 +432,7 @@ abstract class FutoVideoPlayerBase : RelativeLayout {
         _lastAudioMediaSource = ProgressiveMediaSource.Factory(DefaultDataSource.Factory(context))
             .createMediaSource(MediaItem.fromUri(Uri.fromFile(file)));
     }
+    @OptIn(UnstableApi::class)
     private fun swapAudioSourceUrlRange(audioSource: JSAudioUrlRangeSource) {
         Logger.i(TAG, "Loading JSAudioUrlRangeSource");
         if(audioSource.hasItag) {
@@ -444,12 +452,14 @@ abstract class FutoVideoPlayerBase : RelativeLayout {
         }
         else throw IllegalArgumentException("source without itag data...")
     }
+    @OptIn(UnstableApi::class)
     private fun swapAudioSourceUrl(audioSource: IAudioUrlSource) {
         Logger.i(TAG, "Loading AudioSource [Url]");
         _lastAudioMediaSource = ProgressiveMediaSource.Factory(DefaultHttpDataSource.Factory()
             .setUserAgent(DEFAULT_USER_AGENT))
             .createMediaSource(MediaItem.fromUri(audioSource.getAudioUrl()));
     }
+    @OptIn(UnstableApi::class)
     private fun swapAudioSourceHLS(audioSource: IHLSManifestAudioSource) {
         Logger.i(TAG, "Loading AudioSource [HLS]");
         _lastAudioMediaSource = HlsMediaSource.Factory(DefaultHttpDataSource.Factory()
@@ -479,6 +489,7 @@ abstract class FutoVideoPlayerBase : RelativeLayout {
         return VideoHelper.selectBestAudioSource(video.video, PREFERED_AUDIO_CONTAINERS, preferredLanguage);
     }
 
+    @OptIn(UnstableApi::class)
     private fun loadSelectedSources(play: Boolean, resume: Boolean): Boolean {
         val sourceVideo = if(!isAudioMode || _lastAudioMediaSource == null) _lastVideoMediaSource else null;
         val sourceAudio = _lastAudioMediaSource;
@@ -506,11 +517,9 @@ abstract class FutoVideoPlayerBase : RelativeLayout {
         return true;
     }
 
+    @OptIn(UnstableApi::class)
     private fun reloadMediaSource(play: Boolean = false, resume: Boolean = true) {
-        val player = exoPlayer
-        if (player == null)
-            return;
-
+        val player = exoPlayer ?: return
         val positionBefore = player.player.currentPosition;
         if(_mediaSource != null) {
             player.player.setMediaSource(_mediaSource!!);
diff --git a/app/src/main/java/com/futo/platformplayer/views/video/datasources/JSHttpDataSource.java b/app/src/main/java/com/futo/platformplayer/views/video/datasources/JSHttpDataSource.java
index 00a49cf85d8c89f0b6d5f95fb61ea28cdc7a5b12..d153c4405606ec9488baebb7f0bc39d73ef41f1a 100644
--- a/app/src/main/java/com/futo/platformplayer/views/video/datasources/JSHttpDataSource.java
+++ b/app/src/main/java/com/futo/platformplayer/views/video/datasources/JSHttpDataSource.java
@@ -1,26 +1,28 @@
 package com.futo.platformplayer.views.video.datasources;
 
-import static com.google.android.exoplayer2.upstream.HttpUtil.buildRangeRequestHeader;
-import static com.google.android.exoplayer2.util.Assertions.checkNotNull;
-import static com.google.android.exoplayer2.util.Util.castNonNull;
+import static androidx.media3.common.util.Assertions.checkNotNull;
+import static androidx.media3.common.util.Util.castNonNull;
+import static androidx.media3.datasource.HttpUtil.buildRangeRequestHeader;
 import static java.lang.Math.min;
 
 import android.net.Uri;
+import android.util.Log;
+
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
 import com.futo.platformplayer.api.media.platforms.js.models.JSRequestModifier;
-import com.google.android.exoplayer2.C;
-import com.google.android.exoplayer2.PlaybackException;
-import com.google.android.exoplayer2.upstream.BaseDataSource;
-import com.google.android.exoplayer2.upstream.DataSourceException;
-import com.google.android.exoplayer2.upstream.DataSpec;
-import com.google.android.exoplayer2.upstream.DataSpec.HttpMethod;
-import com.google.android.exoplayer2.upstream.HttpDataSource;
-import com.google.android.exoplayer2.upstream.HttpUtil;
-import com.google.android.exoplayer2.upstream.TransferListener;
-import com.google.android.exoplayer2.util.Log;
-import com.google.android.exoplayer2.util.Util;
+import androidx.media3.common.C;
+import androidx.media3.common.PlaybackException;
+import androidx.media3.common.util.UnstableApi;
+import androidx.media3.common.util.Util;
+import androidx.media3.datasource.BaseDataSource;
+import androidx.media3.datasource.DataSourceException;
+import androidx.media3.datasource.DataSpec;
+import androidx.media3.datasource.HttpDataSource;
+import androidx.media3.datasource.HttpUtil;
+import androidx.media3.datasource.TransferListener;
+
 import com.google.common.base.Predicate;
 import com.google.common.collect.ForwardingMap;
 import com.google.common.collect.ImmutableMap;
@@ -45,6 +47,7 @@ import java.util.zip.GZIPInputStream;
  * Based on the default ExoPlayer DefaultHttpDataSource
  */
 
+@UnstableApi
 public class JSHttpDataSource extends BaseDataSource implements HttpDataSource {
     public static final class Factory implements HttpDataSource.Factory {
 
@@ -142,7 +145,7 @@ public class JSHttpDataSource extends BaseDataSource implements HttpDataSource {
         /**
          * Sets a content type {@link Predicate}. If a content type is rejected by the predicate then a
          * {@link HttpDataSource.InvalidContentTypeException} is thrown from {@link
-         * JSHttpDataSource#open(com.google.android.exoplayer2.upstream.DataSpec)}.
+         * JSHttpDataSource#open(androidx.media3.datasource.DataSpec)}.
          *
          * <p>The default is {@code null}.
          *
@@ -160,7 +163,7 @@ public class JSHttpDataSource extends BaseDataSource implements HttpDataSource {
          *
          * <p>The default is {@code null}.
          *
-         * <p>See {@link com.google.android.exoplayer2.upstream.DataSource#addTransferListener(TransferListener)}.
+         * <p>See {@link androidx.media3.datasource.DataSource#addTransferListener(TransferListener)}.
          *
          * @param transferListener The listener that will be used.
          * @return This factory.
@@ -367,12 +370,12 @@ public class JSHttpDataSource extends BaseDataSource implements HttpDataSource {
             if (dataSpec.length != C.LENGTH_UNSET) {
                 bytesToRead = dataSpec.length;
             } else {
-                long contentLength =
-                        HttpUtil.getContentLength(
-                                connection.getHeaderField(HttpHeaders.CONTENT_LENGTH),
-                                connection.getHeaderField(HttpHeaders.CONTENT_RANGE));
-                bytesToRead =
-                        contentLength != C.LENGTH_UNSET ? (contentLength - bytesToSkip) : C.LENGTH_UNSET;
+                long contentLength = HttpUtil.getContentLength(
+                    connection.getHeaderField(HttpHeaders.CONTENT_LENGTH),
+                    connection.getHeaderField(HttpHeaders.CONTENT_RANGE)
+                );
+
+                bytesToRead = contentLength != C.LENGTH_UNSET ? (contentLength - bytesToSkip) : C.LENGTH_UNSET;
             }
         } else {
             // Gzip is enabled. If the server opts to use gzip then the content length in the response
@@ -457,7 +460,7 @@ public class JSHttpDataSource extends BaseDataSource implements HttpDataSource {
     /** Establishes a connection, following redirects to do so where permitted. */
     private HttpURLConnection makeConnection(DataSpec dataSpec) throws IOException {
         URL url = new URL(dataSpec.uri.toString());
-        @HttpMethod int httpMethod = dataSpec.httpMethod;
+        @DataSpec.HttpMethod int httpMethod = dataSpec.httpMethod;
         @Nullable byte[] httpBody = dataSpec.httpBody;
         long position = dataSpec.position;
         long length = dataSpec.length;
@@ -543,7 +546,7 @@ public class JSHttpDataSource extends BaseDataSource implements HttpDataSource {
      */
     private HttpURLConnection makeConnection(
             URL url,
-            @HttpMethod int httpMethod,
+            @DataSpec.HttpMethod int httpMethod,
             @Nullable byte[] httpBody,
             long position,
             long length,
diff --git a/app/src/main/res/layout/thumbnail_video_view.xml b/app/src/main/res/layout/thumbnail_video_view.xml
index 20b4cd71430c0b6a1484ca7dd2f54097804cfcb5..b63e6715659ee7a14afb401019a521dde7689217 100644
--- a/app/src/main/res/layout/thumbnail_video_view.xml
+++ b/app/src/main/res/layout/thumbnail_video_view.xml
@@ -4,7 +4,7 @@
     android:layout_height="wrap_content"
     android:background="@color/transparent"
     xmlns:app="http://schemas.android.com/apk/res-auto">
-    <com.google.android.exoplayer2.ui.StyledPlayerView
+    <com.google.android.exoplayer2.ui.PlayerView
         android:id="@+id/video_player"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
diff --git a/app/src/main/res/layout/video_view.xml b/app/src/main/res/layout/video_view.xml
index 10f9a8fb27d8f6507321fa79d8aa97d44e23e5aa..3cb0bc597644ccfd1c74a9e122c049622a92a951 100644
--- a/app/src/main/res/layout/video_view.xml
+++ b/app/src/main/res/layout/video_view.xml
@@ -6,7 +6,7 @@
     android:id="@+id/videoview_root"
     android:background="@color/transparent"
     xmlns:app="http://schemas.android.com/apk/res-auto">
-    <com.google.android.exoplayer2.ui.StyledPlayerView
+    <com.google.android.exoplayer2.ui.PlayerView
         android:id="@+id/video_player"
         android:layout_width="match_parent"
         android:layout_height="match_parent"