This commit is contained in:
tapframe 2025-11-09 20:18:35 +05:30
parent f2f503b9ab
commit ffaac958c4
18 changed files with 270 additions and 36 deletions

View file

@ -185,6 +185,9 @@ android {
}
dependencies {
// @generated begin react-native-google-cast-dependencies - expo prebuild (DO NOT MODIFY) sync-3822a3c86222e7aca74039b551612aab7e75365d
implementation "com.google.android.gms:play-services-cast-framework:${safeExtGet('castFrameworkVersion', '+')}"
// @generated end react-native-google-cast-dependencies
// The version of react-native is set by the React Native Gradle Plugin
implementation("com.facebook.react:react-android")
@ -214,4 +217,11 @@ dependencies {
// Include only FFmpeg decoder AAR to avoid duplicates with Maven Media3
implementation files("libs/lib-decoder-ffmpeg-release.aar")
// Google Cast Framework
implementation "com.google.android.gms:play-services-cast-framework:${safeExtGet('castFrameworkVersion', '+')}"
}
def safeExtGet(prop, fallback) {
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
}

View file

@ -14,6 +14,8 @@
</intent>
</queries>
<application android:name=".MainApplication" android:label="@string/app_name" android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" android:allowBackup="true" android:theme="@style/AppTheme" android:supportsRtl="true" android:enableOnBackInvokedCallback="false">
<meta-data android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME" android:value="com.reactnative.googlecast.GoogleCastOptionsProvider"/>
<meta-data android:name="com.reactnative.googlecast.RECEIVER_APPLICATION_ID" android:value="CC1AD845"/>
<meta-data android:name="expo.modules.updates.ENABLED" android:value="true"/>
<meta-data android:name="expo.modules.updates.EXPO_RUNTIME_VERSION" android:value="@string/expo_runtime_version"/>
<meta-data android:name="expo.modules.updates.EXPO_UPDATES_CHECK_ON_LAUNCH" android:value="ERROR_RECOVERY_ONLY"/>

View file

@ -9,6 +9,7 @@ import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnable
import com.facebook.react.defaults.DefaultReactActivityDelegate
import expo.modules.ReactActivityDelegateWrapper
import com.reactnative.googlecast.api.RNGCCastContext
class MainActivity : ReactActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
@ -17,6 +18,12 @@ class MainActivity : ReactActivity() {
// This is required for expo-splash-screen.
setTheme(R.style.AppTheme);
super.onCreate(null)
// @generated begin react-native-google-cast-onCreate - expo prebuild (DO NOT MODIFY) sync-489050f2bf9933a98bbd9d93137016ae14c22faa
RNGCCastContext.getSharedInstance(this)
// @generated end react-native-google-cast-onCreate
// Initialize Google Cast context
RNGCCastContext.getSharedInstance(this)
}
/**

View file

@ -1,6 +1,13 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext {
buildToolsVersion = "34.0.0"
minSdkVersion = 24
compileSdkVersion = 34
targetSdkVersion = 34
castFrameworkVersion = "22.1.0"
}
repositories {
google()
mavenCentral()

View file

@ -24,9 +24,11 @@
"NSAllowsArbitraryLoads": true
},
"NSBonjourServices": [
"_http._tcp"
"_http._tcp",
"_googlecast._tcp",
"_CC1AD845._googlecast._tcp"
],
"NSLocalNetworkUsageDescription": "App uses the local network to discover and connect to devices.",
"NSLocalNetworkUsageDescription": "Nuvio uses the local network to discover Cast-enabled devices on your WiFi network and to connect to local media servers.",
"NSMicrophoneUsageDescription": "This app does not require microphone access.",
"UIBackgroundModes": [
"audio"
@ -59,7 +61,6 @@
],
"jsEngine": "hermes"
},
"extra": {
"eas": {
"projectId": "909107b8-fe61-45ce-b02f-b02510d306a6"
@ -89,7 +90,14 @@
"supportsBackgroundPlayback": true
}
],
"react-native-bottom-tabs"
"react-native-bottom-tabs",
[
"react-native-google-cast",
{
"receiverAppId": "CC1AD845",
"iosStartDiscoveryAfterFirstTapOnCastButton": true
}
]
],
"updates": {
"enabled": true,

View file

@ -297,6 +297,7 @@
"${PODS_CONFIGURATION_BUILD_DIR}/ExpoLocalization/ExpoLocalization_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/ExpoSystemUI/ExpoSystemUI_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/KSPlayer/KSPlayer_KSPlayer.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/PromisesObjC/FBLPromises_Privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/RNSVG/RNSVGFilters.bundle",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/AntDesign.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Entypo.ttf",
@ -324,6 +325,14 @@
"${PODS_CONFIGURATION_BUILD_DIR}/Sentry/Sentry.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/expo-dev-launcher/EXDevLauncher.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/expo-dev-menu/EXDevMenu.bundle",
"${PODS_ROOT}/google-cast-sdk/GoogleCastSDK-ios-4.8.4_static_xcframework/GoogleCast.xcframework/ios-arm64/GoogleCast.framework/GoogleCastCoreResources.bundle",
"${PODS_ROOT}/google-cast-sdk/GoogleCastSDK-ios-4.8.4_static_xcframework/GoogleCast.xcframework/ios-arm64/GoogleCast.framework/GoogleCastUIResources.bundle",
"${PODS_ROOT}/google-cast-sdk/GoogleCastSDK-ios-4.8.4_static_xcframework/GoogleCast.xcframework/ios-arm64/GoogleCast.framework/GoogleCastOptionalUIResources.bundle",
"${PODS_ROOT}/google-cast-sdk/GoogleCastSDK-ios-4.8.4_static_xcframework/GoogleCast.xcframework/ios-arm64/GoogleCast.framework/MaterialDialogs.bundle",
"${PODS_ROOT}/google-cast-sdk/GoogleCastSDK-ios-4.8.4_static_xcframework/GoogleCast.xcframework/ios-arm64/GoogleCast.framework/GoogleSansBold.bundle",
"${PODS_ROOT}/google-cast-sdk/GoogleCastSDK-ios-4.8.4_static_xcframework/GoogleCast.xcframework/ios-arm64/GoogleCast.framework/GoogleSansMedium.bundle",
"${PODS_ROOT}/google-cast-sdk/GoogleCastSDK-ios-4.8.4_static_xcframework/GoogleCast.xcframework/ios-arm64/GoogleCast.framework/GoogleSansRegular.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/google-cast-sdk/GoogleCast.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios/LottiePrivacyInfo.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/lottie-react-native/Lottie_React_Native_Privacy.bundle",
);
@ -339,6 +348,7 @@
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoLocalization_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoSystemUI_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/KSPlayer_KSPlayer.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FBLPromises_Privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNSVGFilters.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AntDesign.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Entypo.ttf",
@ -366,6 +376,14 @@
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Sentry.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXDevLauncher.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXDevMenu.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GoogleCastCoreResources.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GoogleCastUIResources.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GoogleCastOptionalUIResources.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MaterialDialogs.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GoogleSansBold.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GoogleSansMedium.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GoogleSansRegular.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GoogleCast.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/LottiePrivacyInfo.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Lottie_React_Native_Privacy.bundle",
);

View file

@ -1,4 +1,9 @@
import Expo
// @generated begin react-native-google-cast-import - expo prebuild (DO NOT MODIFY) sync-4cd300bca26a1d1fcc83f4baf37b0e62afcc1867
#if canImport(GoogleCast) && os(iOS)
import GoogleCast
#endif
// @generated end react-native-google-cast-import
import React
import ReactAppDependencyProvider
@ -13,6 +18,18 @@ public class AppDelegate: ExpoAppDelegate {
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
) -> Bool {
// @generated begin react-native-google-cast-didFinishLaunchingWithOptions - expo prebuild (DO NOT MODIFY) sync-3f476aa248b3451597781fe1ea72c7d4127ed7f9
#if canImport(GoogleCast) && os(iOS)
let receiverAppID = "CC1AD845"
let criteria = GCKDiscoveryCriteria(applicationID: receiverAppID)
let options = GCKCastOptions(discoveryCriteria: criteria)
options.disableDiscoveryAutostart = false
options.startDiscoveryAfterFirstTapOnCastButton = true
options.suspendSessionsWhenBackgrounded = true
GCKCastContext.setSharedInstanceWith(options)
GCKCastContext.sharedInstance().useDefaultExpandedMediaControls = true
#endif
// @generated end react-native-google-cast-didFinishLaunchingWithOptions
let delegate = ReactNativeDelegate()
let factory = ExpoReactNativeFactory(delegate: delegate)
delegate.dependencyProvider = RCTAppDependencyProvider()

View file

@ -54,6 +54,8 @@
<key>NSBonjourServices</key>
<array>
<string>_http._tcp</string>
<string>_googlecast._tcp</string>
<string>_CC1AD845._googlecast._tcp</string>
</array>
<key>NSLocalNetworkUsageDescription</key>
<string>Allow $(PRODUCT_NAME) to access your local network</string>

View file

@ -20,6 +20,7 @@
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>CA92.1</string>
<string>C56D.1</string>
</array>
</dict>
<dict>

View file

@ -229,7 +229,7 @@ PODS:
- ExpoModulesCore
- ExpoKeepAwake (15.0.7):
- ExpoModulesCore
- ExpoLibVlcPlayer (2.2.1):
- ExpoLibVlcPlayer (2.2.3):
- ExpoModulesCore
- MobileVLCKit (= 3.6.1b1)
- ExpoLinearGradient (15.0.7):
@ -328,6 +328,7 @@ PODS:
- FFmpegKit/FFmpegKit (= 6.1.0)
- FFmpegKit/FFmpegKit (6.1.0):
- Libass
- google-cast-sdk (4.8.4)
- hermes-engine (0.81.4):
- hermes-engine/Pre-built (= 0.81.4)
- hermes-engine/Pre-built (0.81.4)
@ -454,6 +455,7 @@ PODS:
- ReactCommon/turbomodule/core
- ReactNativeDependencies
- Yoga
- PromisesObjC (2.4.0)
- RCTDeprecation (0.81.4)
- RCTRequired (0.81.4)
- RCTTypeSafety (0.81.4):
@ -1807,6 +1809,10 @@ PODS:
- React
- react-native-get-random-values (1.11.0):
- React-Core
- react-native-google-cast (4.9.1):
- google-cast-sdk
- PromisesObjC
- React
- react-native-netinfo (11.4.1):
- React-Core
- react-native-safe-area-context (5.6.1):
@ -1878,6 +1884,30 @@ PODS:
- ReactCommon/turbomodule/core
- ReactNativeDependencies
- Yoga
- react-native-skia (2.2.12):
- hermes-engine
- RCTRequired
- RCTTypeSafety
- React
- React-callinvoker
- React-Core
- React-Core-prebuilt
- React-debug
- React-Fabric
- React-featureflags
- React-graphics
- React-ImageManager
- React-jsi
- React-NativeModulesApple
- React-RCTFabric
- React-renderercss
- React-rendererdebug
- React-utils
- ReactCodegen
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- ReactNativeDependencies
- Yoga
- react-native-slider (5.0.1):
- hermes-engine
- RCTRequired
@ -2792,8 +2822,10 @@ DEPENDENCIES:
- react-native-bottom-tabs (from `../node_modules/react-native-bottom-tabs`)
- "react-native-device-brightness (from `../node_modules/@adrianso/react-native-device-brightness`)"
- react-native-get-random-values (from `../node_modules/react-native-get-random-values`)
- react-native-google-cast (from `../node_modules/react-native-google-cast`)
- "react-native-netinfo (from `../node_modules/@react-native-community/netinfo`)"
- react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`)
- "react-native-skia (from `../node_modules/@shopify/react-native-skia`)"
- "react-native-slider (from `../node_modules/@react-native-community/slider`)"
- react-native-video (from `../node_modules/react-native-video`)
- React-NativeModulesApple (from `../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios`)
@ -2840,12 +2872,14 @@ DEPENDENCIES:
SPEC REPOS:
trunk:
- google-cast-sdk
- libavif
- libdav1d
- libwebp
- lottie-ios
- MMKVCore
- MobileVLCKit
- PromisesObjC
- ReachabilitySwift
- SDWebImage
- SDWebImageAVIFCoder
@ -3023,10 +3057,14 @@ EXTERNAL SOURCES:
:path: "../node_modules/@adrianso/react-native-device-brightness"
react-native-get-random-values:
:path: "../node_modules/react-native-get-random-values"
react-native-google-cast:
:path: "../node_modules/react-native-google-cast"
react-native-netinfo:
:path: "../node_modules/@react-native-community/netinfo"
react-native-safe-area-context:
:path: "../node_modules/react-native-safe-area-context"
react-native-skia:
:path: "../node_modules/@shopify/react-native-skia"
react-native-slider:
:path: "../node_modules/@react-native-community/slider"
react-native-video:
@ -3152,7 +3190,7 @@ SPEC CHECKSUMS:
ExpoGlassEffect: 744bf0c58c26a1b0212dff92856be07b98d01d8c
ExpoHaptics: 807476b0c39e9d82b7270349d6487928ce32df84
ExpoKeepAwake: 1a2e820692e933c94a565ec3fbbe38ac31658ffe
ExpoLibVlcPlayer: dce3d0b5847838cd5f8c5f3c3aa1bc55c92e911d
ExpoLibVlcPlayer: 6b4a27f54f5300550227cffcf25cc88ab4f6c7c9
ExpoLinearGradient: a464898cb95153125e3b81894fd479bcb1c7dd27
ExpoLinking: f051f28e50ea9269ff539317c166adec81d9342d
ExpoLocalization: b852a5d8ec14c5349c1593eca87896b5b3ebfcca
@ -3167,6 +3205,7 @@ SPEC CHECKSUMS:
EXUpdatesInterface: 5adf50cb41e079c861da6d9b4b954c3db9a50734
FBLazyVector: 9e0cd874afd81d9a4d36679daca991b58b260d42
FFmpegKit: 3885085fbbc320745838ee4c8a1f9c5e5953dab2
google-cast-sdk: 32f65af50d164e3c475e79ad123db3cc26fbcd37
hermes-engine: 35c763d57c9832d0eef764316ca1c4d043581394
ImageColors: 51cd79f7a9d2524b7a681c660b0a50574085563b
KSPlayer: f163ac6195f240b6fa5b8225aeb39ec811a70c62
@ -3180,6 +3219,7 @@ SPEC CHECKSUMS:
MobileVLCKit: 2d9c7c373393ae43086aeeff890bf0b1afc15c5c
NitroMmkv: 7fe66a61d5acab6516098a64f42af575595e7566
NitroModules: 8c4eca403e6f45f474608d24cd11ab664ed2961c
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
RCTDeprecation: 7487d6dda857ccd4cb3dd6ecfccdc3170e85dcbc
RCTRequired: 54128b7df8be566881d48c7234724a78cb9b6157
RCTTypeSafety: d2b07797a79e45d7b19e1cd2f53c79ab419fe217
@ -3218,8 +3258,10 @@ SPEC CHECKSUMS:
react-native-bottom-tabs: e37c9d1565b1ee48c4c0e4b4fa4b804775f82dfa
react-native-device-brightness: 1a997350d060c3df9f303b1df84a4f7c5cbeb924
react-native-get-random-values: d16467cf726c618e9c7a8c3c39c31faa2244bbba
react-native-google-cast: 7be68a5d0b7eeb95a5924c3ecef8d319ef6c0a44
react-native-netinfo: cec9c4e86083cb5b6aba0e0711f563e2fbbff187
react-native-safe-area-context: 42a1b4f8774b577d03b53de7326e3d5757fe9513
react-native-skia: 05327e7acee05e7c27b68a5da0bc80d65f5d790c
react-native-slider: 8c562583722c396a3682f451f0b6e68e351ec3b9
react-native-video: 5d9635903e562e0c5eb47c5fa401f1c807d6e068
React-NativeModulesApple: a9464983ccc0f66f45e93558671f60fc7536e438

82
package-lock.json generated
View file

@ -29,6 +29,7 @@
"@react-navigation/stack": "^7.2.10",
"@sentry/react-native": "~7.3.0",
"@shopify/flash-list": "^2.1.0",
"@shopify/react-native-skia": "2.2.12",
"@types/lodash": "^4.17.16",
"@types/react-native-video": "^5.0.20",
"axios": "^1.12.2",
@ -50,7 +51,7 @@
"expo-glass-effect": "~0.1.4",
"expo-haptics": "~15.0.7",
"expo-intent-launcher": "~13.0.7",
"expo-libvlc-player": "^2.1.7",
"expo-libvlc-player": "^2.2.3",
"expo-linear-gradient": "~15.0.7",
"expo-localization": "~17.0.7",
"expo-notifications": "~0.32.12",
@ -70,6 +71,7 @@
"react-native-bottom-tabs": "^0.12.2",
"react-native-gesture-handler": "~2.28.0",
"react-native-get-random-values": "^1.11.0",
"react-native-google-cast": "^4.9.1",
"react-native-image-colors": "^2.5.0",
"react-native-immersive-mode": "^2.0.2",
"react-native-markdown-display": "^7.0.2",
@ -3624,6 +3626,32 @@
"react-native": "*"
}
},
"node_modules/@shopify/react-native-skia": {
"version": "2.2.12",
"resolved": "https://registry.npmjs.org/@shopify/react-native-skia/-/react-native-skia-2.2.12.tgz",
"integrity": "sha512-P5wZSMPTp00hM0do+awNFtb5aPh5hSpodMGwy7NaxK90AV+SmUu7wZe6NGevzQIwgFa89Epn6xK3j4jKWdQi+A==",
"license": "MIT",
"dependencies": {
"canvaskit-wasm": "0.40.0",
"react-reconciler": "0.31.0"
},
"bin": {
"setup-skia-web": "scripts/setup-canvaskit.js"
},
"peerDependencies": {
"react": ">=19.0",
"react-native": ">=0.78",
"react-native-reanimated": ">=3.19.1"
},
"peerDependenciesMeta": {
"react-native": {
"optional": true
},
"react-native-reanimated": {
"optional": true
}
}
},
"node_modules/@sinclair/typebox": {
"version": "0.27.8",
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz",
@ -4430,6 +4458,12 @@
"url": "https://github.com/sponsors/crutchcorn"
}
},
"node_modules/@webgpu/types": {
"version": "0.1.21",
"resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.21.tgz",
"integrity": "sha512-pUrWq3V5PiSGFLeLxoGqReTZmiiXwY3jRkIG5sLLKjyqNxrwm/04b4nw7LSmGWJcKk59XOM/YRTUwOzo4MMlow==",
"license": "BSD-3-Clause"
},
"node_modules/@xmldom/xmldom": {
"version": "0.8.11",
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.11.tgz",
@ -5308,6 +5342,15 @@
],
"license": "CC-BY-4.0"
},
"node_modules/canvaskit-wasm": {
"version": "0.40.0",
"resolved": "https://registry.npmjs.org/canvaskit-wasm/-/canvaskit-wasm-0.40.0.tgz",
"integrity": "sha512-Od2o+ZmoEw9PBdN/yCGvzfu0WVqlufBPEWNG452wY7E9aT8RBE+ChpZF526doOlg7zumO4iCS+RAeht4P0Gbpw==",
"license": "BSD-3-Clause",
"dependencies": {
"@webgpu/types": "0.1.21"
}
},
"node_modules/caseless": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
@ -6666,9 +6709,9 @@
}
},
"node_modules/expo-libvlc-player": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/expo-libvlc-player/-/expo-libvlc-player-2.2.1.tgz",
"integrity": "sha512-RYp5t+2B5v8b5h1vfQfKwRYi8WjMaZQ6gWYFJJ+Phrx3tND+moTDIvG+OvPn6Uxn9TslzlbX1n6ad9jv/cypDw==",
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/expo-libvlc-player/-/expo-libvlc-player-2.2.3.tgz",
"integrity": "sha512-HuTmcawtYACeYfX+Ft0RbWRFOh/Wu2OswS4HjSICEW909UY/ZvtHOqPRLym47VjA1oulLHGB7SGGvSgvPd2/4A==",
"license": "MIT",
"peerDependencies": {
"expo": "*",
@ -10770,6 +10813,16 @@
"react-native": ">=0.56"
}
},
"node_modules/react-native-google-cast": {
"version": "4.9.1",
"resolved": "https://registry.npmjs.org/react-native-google-cast/-/react-native-google-cast-4.9.1.tgz",
"integrity": "sha512-/HvIKAaWHtG6aTNCxrNrqA2ftWGkfH0M/2iN+28pdGUXpKmueb33mgL1m8D4zzwEODQMcmpfoCsym1IwDvugBQ==",
"license": "MIT",
"peerDependencies": {
"react": "*",
"react-native": "*"
}
},
"node_modules/react-native-image-colors": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/react-native-image-colors/-/react-native-image-colors-2.5.0.tgz",
@ -11373,6 +11426,27 @@
"async-limiter": "~1.0.0"
}
},
"node_modules/react-reconciler": {
"version": "0.31.0",
"resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.31.0.tgz",
"integrity": "sha512-7Ob7Z+URmesIsIVRjnLoDGwBEG/tVitidU0nMsqX/eeJaLY89RISO/10ERe0MqmzuKUUB1rmY+h1itMbUHg9BQ==",
"license": "MIT",
"dependencies": {
"scheduler": "^0.25.0"
},
"engines": {
"node": ">=0.10.0"
},
"peerDependencies": {
"react": "^19.0.0"
}
},
"node_modules/react-reconciler/node_modules/scheduler": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0.tgz",
"integrity": "sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==",
"license": "MIT"
},
"node_modules/react-refresh": {
"version": "0.14.2",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz",

View file

@ -29,6 +29,7 @@
"@react-navigation/stack": "^7.2.10",
"@sentry/react-native": "~7.3.0",
"@shopify/flash-list": "^2.1.0",
"@shopify/react-native-skia": "2.2.12",
"@types/lodash": "^4.17.16",
"@types/react-native-video": "^5.0.20",
"axios": "^1.12.2",
@ -50,7 +51,7 @@
"expo-glass-effect": "~0.1.4",
"expo-haptics": "~15.0.7",
"expo-intent-launcher": "~13.0.7",
"expo-libvlc-player": "^2.1.7",
"expo-libvlc-player": "^2.2.3",
"expo-linear-gradient": "~15.0.7",
"expo-localization": "~17.0.7",
"expo-notifications": "~0.32.12",
@ -70,6 +71,7 @@
"react-native-bottom-tabs": "^0.12.2",
"react-native-gesture-handler": "~2.28.0",
"react-native-get-random-values": "^1.11.0",
"react-native-google-cast": "^4.9.1",
"react-native-image-colors": "^2.5.0",
"react-native-immersive-mode": "^2.0.2",
"react-native-markdown-display": "^7.0.2",

View file

@ -145,7 +145,10 @@ const ContentItem = ({ item, onPress, shouldLoadImage: shouldLoadImageProp, defe
}, []);
const handlePress = useCallback(() => {
// Validate ID before pressing to prevent errors with NaN/undefined IDs
if (item.id && item.id !== 'NaN' && item.id !== 'undefined') {
onPress(item.id, item.type);
}
}, [item.id, item.type, onPress]);
const handleOptionSelect = useCallback(async (option: string) => {

View file

@ -3105,9 +3105,6 @@ const AndroidVideoPlayer: React.FC = () => {
logo={metadata?.logo}
backgroundFadeAnim={backgroundFadeAnim}
backdropImageOpacityAnim={backdropImageOpacityAnim}
logoScaleAnim={logoScaleAnim}
logoOpacityAnim={logoOpacityAnim}
pulseAnim={pulseAnim}
onClose={handleClose}
width={screenDimensions.width}
height={screenDimensions.height}

View file

@ -2486,9 +2486,6 @@ const KSPlayerCore: React.FC = () => {
logo={metadata?.logo}
backgroundFadeAnim={backgroundFadeAnim}
backdropImageOpacityAnim={backdropImageOpacityAnim}
logoScaleAnim={logoScaleAnim}
logoOpacityAnim={logoOpacityAnim}
pulseAnim={pulseAnim}
onClose={handleClose}
width={shouldUseFullscreen ? effectiveDimensions.width : screenDimensions.width}
height={shouldUseFullscreen ? effectiveDimensions.height : screenDimensions.height}

View file

@ -1,8 +1,17 @@
import React from 'react';
import React, { useEffect } from 'react';
import { View, TouchableOpacity, Animated, ActivityIndicator, StyleSheet, Image } from 'react-native';
import { MaterialIcons } from '@expo/vector-icons';
import { LinearGradient } from 'expo-linear-gradient';
import FastImage from '@d11/react-native-fast-image';
import Reanimated, {
useSharedValue,
useAnimatedStyle,
withTiming,
withRepeat,
withSequence,
Easing,
withDelay
} from 'react-native-reanimated';
import { styles } from '../utils/playerStyles';
interface LoadingOverlayProps {
@ -12,9 +21,6 @@ interface LoadingOverlayProps {
logo: string | null | undefined;
backgroundFadeAnim: Animated.Value;
backdropImageOpacityAnim: Animated.Value;
logoScaleAnim: Animated.Value;
logoOpacityAnim: Animated.Value;
pulseAnim: Animated.Value;
onClose: () => void;
width: number | string;
height: number | string;
@ -28,14 +34,54 @@ const LoadingOverlay: React.FC<LoadingOverlayProps> = ({
logo,
backgroundFadeAnim,
backdropImageOpacityAnim,
logoScaleAnim,
logoOpacityAnim,
pulseAnim,
onClose,
width,
height,
useFastImage = false,
}) => {
const logoOpacity = useSharedValue(0);
const logoScale = useSharedValue(1);
useEffect(() => {
if (visible && hasLogo && logo) {
// Reset
logoOpacity.value = 0;
logoScale.value = 1;
// Start animations after 1 second delay
logoOpacity.value = withDelay(
1000,
withTiming(1, {
duration: 800,
easing: Easing.out(Easing.cubic),
})
);
logoScale.value = withDelay(
1000,
withRepeat(
withSequence(
withTiming(1.04, {
duration: 2000,
easing: Easing.inOut(Easing.ease),
}),
withTiming(1, {
duration: 2000,
easing: Easing.inOut(Easing.ease),
})
),
-1,
false
)
);
}
}, [visible, hasLogo, logo]);
const logoAnimatedStyle = useAnimatedStyle(() => ({
opacity: logoOpacity.value,
transform: [{ scale: logoScale.value }],
}));
if (!visible) return null;
return (
@ -93,13 +139,12 @@ const LoadingOverlay: React.FC<LoadingOverlayProps> = ({
<View style={styles.openingContent}>
{hasLogo && logo ? (
<Animated.View style={{
transform: [
{ scale: Animated.multiply(logoScaleAnim, pulseAnim) }
],
opacity: logoOpacityAnim,
<Reanimated.View style={[
{
alignItems: 'center',
}}>
},
logoAnimatedStyle
]}>
<FastImage
source={{ uri: logo }}
style={{
@ -108,7 +153,7 @@ const LoadingOverlay: React.FC<LoadingOverlayProps> = ({
}}
resizeMode={FastImage.resizeMode.contain}
/>
</Animated.View>
</Reanimated.View>
) : (
<ActivityIndicator size="large" color="#E50914" />
)}

View file

@ -81,7 +81,7 @@ export const SourcesModal: React.FC<SourcesModalProps> = ({
const sortedProviders = Object.entries(availableStreams);
const handleStreamSelect = (stream: Stream) => {
if (stream.url !== currentStreamUrl && (!isChangingSource || isChangingSource === false)) {
if (stream.url !== currentStreamUrl && !isChangingSource) {
onSelectStream(stream);
}
};

View file

@ -494,7 +494,8 @@ export function useFeaturedContent() {
}
} else {
// For carousel items - check if saved and toggle
const isItemSaved = await catalogService.isInLibrary(contentToUse.type, contentToUse.id);
const libraryItems = await catalogService.getLibraryItems();
const isItemSaved = libraryItems.some(libItem => libItem.id === contentToUse.id && libItem.type === contentToUse.type);
await Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
if (isItemSaved) {
@ -514,7 +515,8 @@ export function useFeaturedContent() {
const isItemSaved = useCallback(async (item: StreamingContent) => {
try {
return await catalogService.isInLibrary(item.type, item.id);
const items = await catalogService.getLibraryItems();
return items.some(libItem => libItem.id === item.id && libItem.type === item.type);
} catch (error) {
logger.error('Error checking if item is saved:', error);
return false;