diff --git a/.gitignore b/.gitignore
index 16951d6..2f077ca 100644
--- a/.gitignore
+++ b/.gitignore
@@ -80,6 +80,7 @@ bottomnav.md
mmkv.md
fix-android-scroll-lag-summary.md
server/cache-server
+server/campaign-manager
carousal.md
node_modules
expofs.md
diff --git a/App.tsx b/App.tsx
index 39207b5..ff4b26f 100644
--- a/App.tsx
+++ b/App.tsx
@@ -42,6 +42,7 @@ import { AccountProvider, useAccount } from './src/contexts/AccountContext';
import { ToastProvider } from './src/contexts/ToastContext';
import { mmkvStorage } from './src/services/mmkvStorage';
import AnnouncementOverlay from './src/components/AnnouncementOverlay';
+import { CampaignManager } from './src/components/promotions/CampaignManager';
Sentry.init({
dsn: 'https://1a58bf436454d346e5852b7bfd3c95e8@o4509536317276160.ingest.de.sentry.io/4509536317734992',
@@ -232,6 +233,7 @@ const ThemedApp = () => {
onActionPress={handleNavigateToDebrid}
actionButtonText="Connect Now"
/>
+
diff --git a/android/app/build.gradle b/android/app/build.gradle
index d8870d1..f852b41 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -246,6 +246,9 @@ dependencies {
// Include only FFmpeg decoder AAR to avoid duplicates with Maven Media3
implementation files("libs/lib-decoder-ffmpeg-release.aar")
+
+ // MPV Player library
+ implementation files("libs/libmpv-release.aar")
// Google Cast Framework
implementation "com.google.android.gms:play-services-cast-framework:${safeExtGet('castFrameworkVersion', '+')}"
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 63ac1b6..e241f30 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -1,4 +1,6 @@
-
+
+
diff --git a/android/app/src/main/java/com/nuvio/app/MainApplication.kt b/android/app/src/main/java/com/nuvio/app/MainApplication.kt
index 2a6f8de..497e21a 100644
--- a/android/app/src/main/java/com/nuvio/app/MainApplication.kt
+++ b/android/app/src/main/java/com/nuvio/app/MainApplication.kt
@@ -15,6 +15,7 @@ import com.facebook.react.defaults.DefaultReactNativeHost
import expo.modules.ApplicationLifecycleDispatcher
import expo.modules.ReactNativeHostWrapper
+import com.nuvio.app.mpv.MpvPackage
class MainApplication : Application(), ReactApplication {
@@ -24,7 +25,7 @@ class MainApplication : Application(), ReactApplication {
override fun getPackages(): List =
PackageList(this).packages.apply {
// Packages that cannot be autolinked yet can be added manually here, for example:
- // add(MyReactNativePackage())
+ add(com.nuvio.app.mpv.MpvPackage())
}
override fun getJSMainModuleName(): String = ".expo/.virtual-metro-entry"
diff --git a/android/app/src/main/java/com/nuvio/app/mpv/MPVView.kt b/android/app/src/main/java/com/nuvio/app/mpv/MPVView.kt
new file mode 100644
index 0000000..43e32c6
--- /dev/null
+++ b/android/app/src/main/java/com/nuvio/app/mpv/MPVView.kt
@@ -0,0 +1,480 @@
+package com.nuvio.app.mpv
+
+import android.content.Context
+import android.graphics.SurfaceTexture
+import android.util.AttributeSet
+import android.util.Log
+import android.view.Surface
+import android.view.TextureView
+import dev.jdtech.mpv.MPVLib
+
+class MPVView @JvmOverloads constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0
+) : TextureView(context, attrs, defStyleAttr), TextureView.SurfaceTextureListener, MPVLib.EventObserver {
+
+ companion object {
+ private const val TAG = "MPVView"
+ }
+
+ private var isMpvInitialized = false
+ private var pendingDataSource: String? = null
+ private var isPaused: Boolean = true
+ private var surface: Surface? = null
+ private var httpHeaders: Map? = null
+
+ // Hardware decoding setting (default: false = software decoding)
+ var useHardwareDecoding: Boolean = false
+
+ // Flag to track if onLoad has been fired (prevents multiple fires for HLS streams)
+ private var hasLoadEventFired: Boolean = false
+
+ // Event listener for React Native
+ var onLoadCallback: ((duration: Double, width: Int, height: Int) -> Unit)? = null
+ var onProgressCallback: ((position: Double, duration: Double) -> Unit)? = null
+ var onEndCallback: (() -> Unit)? = null
+ var onErrorCallback: ((message: String) -> Unit)? = null
+ var onTracksChangedCallback: ((audioTracks: List