mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-01-11 12:00:33 +00:00
ksp sub updates
This commit is contained in:
parent
a85cc93026
commit
eb6fcf639f
20 changed files with 171 additions and 1423 deletions
1
MPVKit
1
MPVKit
|
|
@ -1 +0,0 @@
|
|||
Subproject commit 28de60c09651b8ca899d67456e02d285c92ceee2
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
//
|
||||
// KSPlayerManager.m
|
||||
// Nuvio
|
||||
//
|
||||
// Created by KSPlayer integration
|
||||
//
|
||||
|
||||
#import <React/RCTBridgeModule.h>
|
||||
#import <React/RCTEventEmitter.h>
|
||||
#import <React/RCTViewManager.h>
|
||||
|
||||
@interface RCT_EXTERN_MODULE (KSPlayerViewManager, RCTViewManager)
|
||||
|
||||
RCT_EXPORT_VIEW_PROPERTY(source, NSDictionary)
|
||||
RCT_EXPORT_VIEW_PROPERTY(paused, BOOL)
|
||||
RCT_EXPORT_VIEW_PROPERTY(volume, NSNumber)
|
||||
RCT_EXPORT_VIEW_PROPERTY(rate, NSNumber)
|
||||
RCT_EXPORT_VIEW_PROPERTY(audioTrack, NSNumber)
|
||||
RCT_EXPORT_VIEW_PROPERTY(textTrack, NSNumber)
|
||||
RCT_EXPORT_VIEW_PROPERTY(allowsExternalPlayback, BOOL)
|
||||
RCT_EXPORT_VIEW_PROPERTY(usesExternalPlaybackWhileExternalScreenIsActive, BOOL)
|
||||
RCT_EXPORT_VIEW_PROPERTY(subtitleBottomOffset, NSNumber)
|
||||
RCT_EXPORT_VIEW_PROPERTY(subtitleFontSize, NSNumber)
|
||||
RCT_EXPORT_VIEW_PROPERTY(subtitleTextColor, NSString)
|
||||
RCT_EXPORT_VIEW_PROPERTY(subtitleBackgroundColor, NSString)
|
||||
RCT_EXPORT_VIEW_PROPERTY(resizeMode, NSString)
|
||||
|
||||
// Event properties
|
||||
RCT_EXPORT_VIEW_PROPERTY(onLoad, RCTDirectEventBlock)
|
||||
RCT_EXPORT_VIEW_PROPERTY(onProgress, RCTDirectEventBlock)
|
||||
RCT_EXPORT_VIEW_PROPERTY(onBuffering, RCTDirectEventBlock)
|
||||
RCT_EXPORT_VIEW_PROPERTY(onEnd, RCTDirectEventBlock)
|
||||
RCT_EXPORT_VIEW_PROPERTY(onError, RCTDirectEventBlock)
|
||||
RCT_EXPORT_VIEW_PROPERTY(onBufferingProgress, RCTDirectEventBlock)
|
||||
|
||||
RCT_EXTERN_METHOD(seek : (nonnull NSNumber *)node toTime : (nonnull NSNumber *)
|
||||
time)
|
||||
RCT_EXTERN_METHOD(setSource : (nonnull NSNumber *)
|
||||
node source : (nonnull NSDictionary *)source)
|
||||
RCT_EXTERN_METHOD(setPaused : (nonnull NSNumber *)node paused : (BOOL)paused)
|
||||
RCT_EXTERN_METHOD(setVolume : (nonnull NSNumber *)
|
||||
node volume : (nonnull NSNumber *)volume)
|
||||
RCT_EXTERN_METHOD(setPlaybackRate : (nonnull NSNumber *)
|
||||
node rate : (nonnull NSNumber *)rate)
|
||||
RCT_EXTERN_METHOD(setAudioTrack : (nonnull NSNumber *)
|
||||
node trackId : (nonnull NSNumber *)trackId)
|
||||
RCT_EXTERN_METHOD(setTextTrack : (nonnull NSNumber *)
|
||||
node trackId : (nonnull NSNumber *)trackId)
|
||||
RCT_EXTERN_METHOD(getTracks : (nonnull NSNumber *)node resolve : (
|
||||
RCTPromiseResolveBlock)resolve reject : (RCTPromiseRejectBlock)reject)
|
||||
RCT_EXTERN_METHOD(setAllowsExternalPlayback : (nonnull NSNumber *)
|
||||
node allows : (BOOL)allows)
|
||||
RCT_EXTERN_METHOD(setUsesExternalPlaybackWhileExternalScreenIsActive : (
|
||||
nonnull NSNumber *)node uses : (BOOL)uses)
|
||||
RCT_EXTERN_METHOD(getAirPlayState : (nonnull NSNumber *)node resolve : (
|
||||
RCTPromiseResolveBlock)resolve reject : (RCTPromiseRejectBlock)reject)
|
||||
RCT_EXTERN_METHOD(showAirPlayPicker : (nonnull NSNumber *)node)
|
||||
|
||||
@end
|
||||
|
||||
@interface RCT_EXTERN_MODULE (KSPlayerModule, RCTEventEmitter)
|
||||
|
||||
RCT_EXTERN_METHOD(getTracks : (NSNumber *)nodeTag resolve : (
|
||||
RCTPromiseResolveBlock)resolve reject : (RCTPromiseRejectBlock)reject)
|
||||
RCT_EXTERN_METHOD(getAirPlayState : (NSNumber *)nodeTag resolve : (
|
||||
RCTPromiseResolveBlock)resolve reject : (RCTPromiseRejectBlock)reject)
|
||||
RCT_EXTERN_METHOD(showAirPlayPicker : (NSNumber *)nodeTag)
|
||||
|
||||
@end
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
//
|
||||
// KSPlayerModule.swift
|
||||
// Nuvio
|
||||
//
|
||||
// Created by KSPlayer integration
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import KSPlayer
|
||||
import React
|
||||
|
||||
@objc(KSPlayerModule)
|
||||
class KSPlayerModule: RCTEventEmitter {
|
||||
override static func requiresMainQueueSetup() -> Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
override func supportedEvents() -> [String]! {
|
||||
return [
|
||||
"KSPlayer-onLoad",
|
||||
"KSPlayer-onProgress",
|
||||
"KSPlayer-onBuffering",
|
||||
"KSPlayer-onEnd",
|
||||
"KSPlayer-onError"
|
||||
]
|
||||
}
|
||||
|
||||
@objc func getTracks(_ nodeTag: NSNumber?, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
|
||||
guard let nodeTag = nodeTag else {
|
||||
reject("INVALID_ARGUMENT", "nodeTag must not be nil", nil)
|
||||
return
|
||||
}
|
||||
DispatchQueue.main.async {
|
||||
if let viewManager = self.bridge.module(for: KSPlayerViewManager.self) as? KSPlayerViewManager {
|
||||
viewManager.getTracks(nodeTag, resolve: resolve, reject: reject)
|
||||
} else {
|
||||
reject("NO_VIEW_MANAGER", "KSPlayerViewManager not found", nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc func getAirPlayState(_ nodeTag: NSNumber?, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
|
||||
guard let nodeTag = nodeTag else {
|
||||
reject("INVALID_ARGUMENT", "nodeTag must not be nil", nil)
|
||||
return
|
||||
}
|
||||
DispatchQueue.main.async {
|
||||
if let viewManager = self.bridge.module(for: KSPlayerViewManager.self) as? KSPlayerViewManager {
|
||||
viewManager.getAirPlayState(nodeTag, resolve: resolve, reject: reject)
|
||||
} else {
|
||||
reject("NO_VIEW_MANAGER", "KSPlayerViewManager not found", nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc func showAirPlayPicker(_ nodeTag: NSNumber?) {
|
||||
guard let nodeTag = nodeTag else {
|
||||
print("[KSPlayerModule] showAirPlayPicker called with nil nodeTag")
|
||||
return
|
||||
}
|
||||
print("[KSPlayerModule] showAirPlayPicker called for nodeTag: \(nodeTag)")
|
||||
DispatchQueue.main.async {
|
||||
if let viewManager = self.bridge.module(for: KSPlayerViewManager.self) as? KSPlayerViewManager {
|
||||
print("[KSPlayerModule] Found KSPlayerViewManager, calling showAirPlayPicker")
|
||||
viewManager.showAirPlayPicker(nodeTag)
|
||||
} else {
|
||||
print("[KSPlayerModule] Could not find KSPlayerViewManager")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,152 +0,0 @@
|
|||
//
|
||||
// KSPlayerViewManager.swift
|
||||
// Nuvio
|
||||
//
|
||||
// Created by KSPlayer integration
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import KSPlayer
|
||||
import React
|
||||
|
||||
@objc(KSPlayerViewManager)
|
||||
class KSPlayerViewManager: RCTViewManager {
|
||||
|
||||
// Not needed for RCTViewManager-based views; events are exported via Objective-C externs in KSPlayerManager.m
|
||||
override func view() -> UIView! {
|
||||
let view = KSPlayerView()
|
||||
view.viewManager = self
|
||||
return view
|
||||
}
|
||||
|
||||
override static func requiresMainQueueSetup() -> Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
override func constantsToExport() -> [AnyHashable : Any]! {
|
||||
return [
|
||||
"EventTypes": [
|
||||
"onLoad": "onLoad",
|
||||
"onProgress": "onProgress",
|
||||
"onBuffering": "onBuffering",
|
||||
"onEnd": "onEnd",
|
||||
"onError": "onError",
|
||||
"onBufferingProgress": "onBufferingProgress"
|
||||
]
|
||||
]
|
||||
}
|
||||
|
||||
// No-op: events are sent via direct event blocks on the view
|
||||
|
||||
@objc func seek(_ node: NSNumber, toTime time: NSNumber) {
|
||||
DispatchQueue.main.async {
|
||||
if let view = self.bridge.uiManager.view(forReactTag: node) as? KSPlayerView {
|
||||
view.seek(to: TimeInterval(truncating: time))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc func setSource(_ node: NSNumber, source: NSDictionary) {
|
||||
DispatchQueue.main.async {
|
||||
if let view = self.bridge.uiManager.view(forReactTag: node) as? KSPlayerView {
|
||||
view.setSource(source)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc func setPaused(_ node: NSNumber, paused: Bool) {
|
||||
DispatchQueue.main.async {
|
||||
if let view = self.bridge.uiManager.view(forReactTag: node) as? KSPlayerView {
|
||||
view.setPaused(paused)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc func setVolume(_ node: NSNumber, volume: NSNumber) {
|
||||
DispatchQueue.main.async {
|
||||
if let view = self.bridge.uiManager.view(forReactTag: node) as? KSPlayerView {
|
||||
view.setVolume(Float(truncating: volume))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc func setPlaybackRate(_ node: NSNumber, rate: NSNumber) {
|
||||
DispatchQueue.main.async {
|
||||
if let view = self.bridge.uiManager.view(forReactTag: node) as? KSPlayerView {
|
||||
view.setPlaybackRate(Float(truncating: rate))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc func setAudioTrack(_ node: NSNumber, trackId: NSNumber) {
|
||||
DispatchQueue.main.async {
|
||||
if let view = self.bridge.uiManager.view(forReactTag: node) as? KSPlayerView {
|
||||
view.setAudioTrack(Int(truncating: trackId))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc func setTextTrack(_ node: NSNumber, trackId: NSNumber) {
|
||||
NSLog("[KSPlayerViewManager] setTextTrack called - node: %@, trackId: %@", node, trackId)
|
||||
DispatchQueue.main.async {
|
||||
NSLog("[KSPlayerViewManager] setTextTrack on main queue - looking for view with tag: %@", node)
|
||||
if let view = self.bridge.uiManager.view(forReactTag: node) as? KSPlayerView {
|
||||
NSLog("[KSPlayerViewManager] Found view, calling setTextTrack(%d)", Int(truncating: trackId))
|
||||
view.setTextTrack(Int(truncating: trackId))
|
||||
} else {
|
||||
NSLog("[KSPlayerViewManager] ERROR - Could not find KSPlayerView for tag: %@", node)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc func getTracks(_ node: NSNumber, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
|
||||
DispatchQueue.main.async {
|
||||
if let view = self.bridge.uiManager.view(forReactTag: node) as? KSPlayerView {
|
||||
let tracks = view.getAvailableTracks()
|
||||
resolve(tracks)
|
||||
} else {
|
||||
reject("NO_VIEW", "KSPlayerView not found", nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// AirPlay methods
|
||||
@objc func setAllowsExternalPlayback(_ node: NSNumber, allows: Bool) {
|
||||
DispatchQueue.main.async {
|
||||
if let view = self.bridge.uiManager.view(forReactTag: node) as? KSPlayerView {
|
||||
view.setAllowsExternalPlayback(allows)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc func setUsesExternalPlaybackWhileExternalScreenIsActive(_ node: NSNumber, uses: Bool) {
|
||||
DispatchQueue.main.async {
|
||||
if let view = self.bridge.uiManager.view(forReactTag: node) as? KSPlayerView {
|
||||
view.setUsesExternalPlaybackWhileExternalScreenIsActive(uses)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc func getAirPlayState(_ node: NSNumber, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
|
||||
DispatchQueue.main.async {
|
||||
if let view = self.bridge.uiManager.view(forReactTag: node) as? KSPlayerView {
|
||||
let airPlayState = view.getAirPlayState()
|
||||
resolve(airPlayState)
|
||||
} else {
|
||||
reject("NO_VIEW", "KSPlayerView not found", nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc func showAirPlayPicker(_ node: NSNumber) {
|
||||
print("[KSPlayerViewManager] showAirPlayPicker called for node: \(node)")
|
||||
DispatchQueue.main.async {
|
||||
if let view = self.bridge.uiManager.view(forReactTag: node) as? KSPlayerView {
|
||||
print("[KSPlayerViewManager] Found KSPlayerView, calling showAirPlayPicker")
|
||||
view.showAirPlayPicker()
|
||||
} else {
|
||||
print("[KSPlayerViewManager] Could not find KSPlayerView for node: \(node)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -11,12 +11,12 @@
|
|||
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
|
||||
2AA769395C1242F225F875AF /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E007C0BAC8C453623E81663 /* ExpoModulesProvider.swift */; };
|
||||
3E461D99554A48A4959DE609 /* SplashScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */; };
|
||||
72D4090694139E0DAD9B066E /* libPods-Nuvio.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 406CD0CEF46F8EAEEAD92BF7 /* libPods-Nuvio.a */; };
|
||||
9FBA88F42E86ECD700892850 /* KSPlayerViewManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FBA88F32E86ECD700892850 /* KSPlayerViewManager.swift */; };
|
||||
9FBA88F52E86ECD700892850 /* KSPlayerModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FBA88F12E86ECD700892850 /* KSPlayerModule.swift */; };
|
||||
9FBA88F62E86ECD700892850 /* KSPlayerManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 9FBA88F02E86ECD700892850 /* KSPlayerManager.m */; };
|
||||
9FBA88F72E86ECD700892850 /* KSPlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FBA88F22E86ECD700892850 /* KSPlayerView.swift */; };
|
||||
BB2F792D24A3F905000567C9 /* Expo.plist in Resources */ = {isa = PBXBuildFile; fileRef = BB2F792C24A3F905000567C9 /* Expo.plist */; };
|
||||
D66ACCC72CB69F1FF14A2585 /* libPods-Nuvio.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 436A6FCA2C83F29076E121BA /* libPods-Nuvio.a */; };
|
||||
F11748422D0307B40044C1D9 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F11748412D0307B40044C1D9 /* AppDelegate.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
|
|
@ -24,16 +24,16 @@
|
|||
13B07F961A680F5B00A75B9A /* Nuvio.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Nuvio.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = Nuvio/Images.xcassets; sourceTree = "<group>"; };
|
||||
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = Nuvio/Info.plist; sourceTree = "<group>"; };
|
||||
15864A7148A4384BAA9F0B37 /* Pods-Nuvio.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Nuvio.release.xcconfig"; path = "Target Support Files/Pods-Nuvio/Pods-Nuvio.release.xcconfig"; sourceTree = "<group>"; };
|
||||
406CD0CEF46F8EAEEAD92BF7 /* libPods-Nuvio.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Nuvio.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
436A6FCA2C83F29076E121BA /* libPods-Nuvio.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Nuvio.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
49055D6E250FAFA21141FE49 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xml; name = PrivacyInfo.xcprivacy; path = Nuvio/PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
|
||||
5346BAA9EF8C9C8182D4485C /* Pods-Nuvio.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Nuvio.release.xcconfig"; path = "Target Support Files/Pods-Nuvio/Pods-Nuvio.release.xcconfig"; sourceTree = "<group>"; };
|
||||
6E007C0BAC8C453623E81663 /* ExpoModulesProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ExpoModulesProvider.swift; path = "Pods/Target Support Files/Pods-Nuvio/ExpoModulesProvider.swift"; sourceTree = "<group>"; };
|
||||
73BB213C2E9EEAC700EC03F8 /* NuvioRelease.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = NuvioRelease.entitlements; path = Nuvio/NuvioRelease.entitlements; sourceTree = "<group>"; };
|
||||
819F6DCD44DFE0C72440FDCF /* Pods-Nuvio.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Nuvio.debug.xcconfig"; path = "Target Support Files/Pods-Nuvio/Pods-Nuvio.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
9FBA88F02E86ECD700892850 /* KSPlayerManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KSPlayerManager.m; sourceTree = "<group>"; };
|
||||
9FBA88F12E86ECD700892850 /* KSPlayerModule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KSPlayerModule.swift; sourceTree = "<group>"; };
|
||||
9FBA88F22E86ECD700892850 /* KSPlayerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KSPlayerView.swift; sourceTree = "<group>"; };
|
||||
9FBA88F32E86ECD700892850 /* KSPlayerViewManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KSPlayerViewManager.swift; sourceTree = "<group>"; };
|
||||
904B4A0A0308D3727268BA5E /* Pods-Nuvio.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Nuvio.debug.xcconfig"; path = "Target Support Files/Pods-Nuvio/Pods-Nuvio.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
9FBA88F02E86ECD700892850 /* KSPlayerManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ../KSPlayer/RNBridge/KSPlayerManager.m; sourceTree = "<group>"; };
|
||||
9FBA88F12E86ECD700892850 /* KSPlayerModule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ../KSPlayer/RNBridge/KSPlayerModule.swift; sourceTree = "<group>"; };
|
||||
9FBA88F22E86ECD700892850 /* KSPlayerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ../KSPlayer/RNBridge/KSPlayerView.swift; sourceTree = "<group>"; };
|
||||
9FBA88F32E86ECD700892850 /* KSPlayerViewManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ../KSPlayer/RNBridge/KSPlayerViewManager.swift; sourceTree = "<group>"; };
|
||||
AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = SplashScreen.storyboard; path = Nuvio/SplashScreen.storyboard; sourceTree = "<group>"; };
|
||||
BB2F792C24A3F905000567C9 /* Expo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Expo.plist; sourceTree = "<group>"; };
|
||||
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
|
||||
|
|
@ -46,7 +46,7 @@
|
|||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
72D4090694139E0DAD9B066E /* libPods-Nuvio.a in Frameworks */,
|
||||
D66ACCC72CB69F1FF14A2585 /* libPods-Nuvio.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
@ -76,7 +76,7 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
|
||||
406CD0CEF46F8EAEEAD92BF7 /* libPods-Nuvio.a */,
|
||||
436A6FCA2C83F29076E121BA /* libPods-Nuvio.a */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -131,8 +131,8 @@
|
|||
D90A3959C97EE9926C513293 /* Pods */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
819F6DCD44DFE0C72440FDCF /* Pods-Nuvio.debug.xcconfig */,
|
||||
15864A7148A4384BAA9F0B37 /* Pods-Nuvio.release.xcconfig */,
|
||||
904B4A0A0308D3727268BA5E /* Pods-Nuvio.debug.xcconfig */,
|
||||
5346BAA9EF8C9C8182D4485C /* Pods-Nuvio.release.xcconfig */,
|
||||
);
|
||||
path = Pods;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -152,15 +152,15 @@
|
|||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "Nuvio" */;
|
||||
buildPhases = (
|
||||
E060D0359630A7C0F4793812 /* [CP] Check Pods Manifest.lock */,
|
||||
3B2D9C1D63379C2F30AC0F2B /* [CP] Check Pods Manifest.lock */,
|
||||
99A79B70155E84EE1FB7F466 /* [Expo] Configure project */,
|
||||
13B07F871A680F5B00A75B9A /* Sources */,
|
||||
13B07F8C1A680F5B00A75B9A /* Frameworks */,
|
||||
13B07F8E1A680F5B00A75B9A /* Resources */,
|
||||
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
|
||||
9B977D89FE30470F8C59964C /* Upload Debug Symbols to Sentry */,
|
||||
B84E40F81DF927F34032D68B /* [CP] Embed Pods Frameworks */,
|
||||
AA47AE6072D35F0490B53926 /* [CP] Copy Pods Resources */,
|
||||
9F740EE07B5F97C85979C145 /* [CP] Embed Pods Frameworks */,
|
||||
550CD54859274FE505BA4957 /* [CP] Copy Pods Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
|
|
@ -234,45 +234,29 @@
|
|||
shellPath = /bin/sh;
|
||||
shellScript = "if [[ -f \"$PODS_ROOT/../.xcode.env\" ]]; then\n source \"$PODS_ROOT/../.xcode.env\"\nfi\nif [[ -f \"$PODS_ROOT/../.xcode.env.local\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.local\"\nfi\n\n# The project root by default is one level up from the ios directory\nexport PROJECT_ROOT=\"$PROJECT_DIR\"/..\n\nif [[ \"$CONFIGURATION\" = *Debug* ]]; then\n export SKIP_BUNDLING=1\nfi\nif [[ -z \"$ENTRY_FILE\" ]]; then\n # Set the entry JS file using the bundler's entry resolution.\n export ENTRY_FILE=\"$(\"$NODE_BINARY\" -e \"require('expo/scripts/resolveAppEntry')\" \"$PROJECT_ROOT\" ios absolute | tail -n 1)\"\nfi\n\nif [[ -z \"$CLI_PATH\" ]]; then\n # Use Expo CLI\n export CLI_PATH=\"$(\"$NODE_BINARY\" --print \"require.resolve('@expo/cli', { paths: [require.resolve('expo/package.json')] })\")\"\nfi\nif [[ -z \"$BUNDLE_COMMAND\" ]]; then\n # Default Expo CLI command for bundling\n export BUNDLE_COMMAND=\"export:embed\"\nfi\n\n# Source .xcode.env.updates if it exists to allow\n# SKIP_BUNDLING to be unset if needed\nif [[ -f \"$PODS_ROOT/../.xcode.env.updates\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.updates\"\nfi\n# Source local changes to allow overrides\n# if needed\nif [[ -f \"$PODS_ROOT/../.xcode.env.local\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.local\"\nfi\n\n/bin/sh `\"$NODE_BINARY\" --print \"require('path').dirname(require.resolve('@sentry/react-native/package.json')) + '/scripts/sentry-xcode.sh'\"` `\"$NODE_BINARY\" --print \"require('path').dirname(require.resolve('react-native/package.json')) + '/scripts/react-native-xcode.sh'\"`\n\n";
|
||||
};
|
||||
99A79B70155E84EE1FB7F466 /* [Expo] Configure project */ = {
|
||||
3B2D9C1D63379C2F30AC0F2B /* [CP] Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
alwaysOutOfDate = 1;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
"$(SRCROOT)/.xcode.env",
|
||||
"$(SRCROOT)/.xcode.env.local",
|
||||
"$(SRCROOT)/Nuvio/Nuvio.entitlements",
|
||||
"$(SRCROOT)/Pods/Target Support Files/Pods-Nuvio/expo-configure-project.sh",
|
||||
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||
"${PODS_ROOT}/Manifest.lock",
|
||||
);
|
||||
name = "[Expo] Configure project";
|
||||
name = "[CP] Check Pods Manifest.lock";
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
"$(SRCROOT)/Pods/Target Support Files/Pods-Nuvio/ExpoModulesProvider.swift",
|
||||
"$(DERIVED_FILE_DIR)/Pods-Nuvio-checkManifestLockResult.txt",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "# This script configures Expo modules and generates the modules provider file.\nbash -l -c \"./Pods/Target\\ Support\\ Files/Pods-Nuvio/expo-configure-project.sh\"\n";
|
||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
9B977D89FE30470F8C59964C /* Upload Debug Symbols to Sentry */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Upload Debug Symbols to Sentry";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "/bin/sh `${NODE_BINARY:-node} --print \"require('path').dirname(require.resolve('@sentry/react-native/package.json')) + '/scripts/sentry-xcode-debug-files.sh'\"`";
|
||||
};
|
||||
AA47AE6072D35F0490B53926 /* [CP] Copy Pods Resources */ = {
|
||||
550CD54859274FE505BA4957 /* [CP] Copy Pods Resources */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
|
|
@ -384,7 +368,45 @@
|
|||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Nuvio/Pods-Nuvio-resources.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
B84E40F81DF927F34032D68B /* [CP] Embed Pods Frameworks */ = {
|
||||
99A79B70155E84EE1FB7F466 /* [Expo] Configure project */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
alwaysOutOfDate = 1;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
"$(SRCROOT)/.xcode.env",
|
||||
"$(SRCROOT)/.xcode.env.local",
|
||||
"$(SRCROOT)/Nuvio/Nuvio.entitlements",
|
||||
"$(SRCROOT)/Pods/Target Support Files/Pods-Nuvio/expo-configure-project.sh",
|
||||
);
|
||||
name = "[Expo] Configure project";
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
"$(SRCROOT)/Pods/Target Support Files/Pods-Nuvio/ExpoModulesProvider.swift",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "# This script configures Expo modules and generates the modules provider file.\nbash -l -c \"./Pods/Target\\ Support\\ Files/Pods-Nuvio/expo-configure-project.sh\"\n";
|
||||
};
|
||||
9B977D89FE30470F8C59964C /* Upload Debug Symbols to Sentry */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Upload Debug Symbols to Sentry";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "/bin/sh `${NODE_BINARY:-node} --print \"require('path').dirname(require.resolve('@sentry/react-native/package.json')) + '/scripts/sentry-xcode-debug-files.sh'\"`";
|
||||
};
|
||||
9F740EE07B5F97C85979C145 /* [CP] Embed Pods Frameworks */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
|
|
@ -406,28 +428,6 @@
|
|||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Nuvio/Pods-Nuvio-frameworks.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
E060D0359630A7C0F4793812 /* [CP] Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||
"${PODS_ROOT}/Manifest.lock",
|
||||
);
|
||||
name = "[CP] Check Pods Manifest.lock";
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
"$(DERIVED_FILE_DIR)/Pods-Nuvio-checkManifestLockResult.txt",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
|
|
@ -449,7 +449,7 @@
|
|||
/* Begin XCBuildConfiguration section */
|
||||
13B07F941A680F5B00A75B9A /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 819F6DCD44DFE0C72440FDCF /* Pods-Nuvio.debug.xcconfig */;
|
||||
baseConfigurationReference = 904B4A0A0308D3727268BA5E /* Pods-Nuvio.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
|
|
@ -487,7 +487,7 @@
|
|||
};
|
||||
13B07F951A680F5B00A75B9A /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 15864A7148A4384BAA9F0B37 /* Pods-Nuvio.release.xcconfig */;
|
||||
baseConfigurationReference = 5346BAA9EF8C9C8182D4485C /* Pods-Nuvio.release.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
|
|
|
|||
|
|
@ -50,8 +50,9 @@ target 'Nuvio' do
|
|||
)
|
||||
|
||||
# KSPlayer dependencies
|
||||
pod 'KSPlayer', :git => 'https://github.com/kingslay/KSPlayer.git', :branch => 'main'
|
||||
pod 'DisplayCriteria', :git => 'https://github.com/kingslay/KSPlayer.git', :branch => 'main', :modular_headers => true
|
||||
# Use the local checkout so we can patch subtitle rendering (and other behaviors) without forking.
|
||||
pod 'KSPlayer', :path => '../KSPlayer'
|
||||
pod 'DisplayCriteria', :path => '../KSPlayer', :modular_headers => true
|
||||
pod 'FFmpegKit', :git => 'https://github.com/kingslay/FFmpegKit.git', :branch => 'main', :modular_headers => true
|
||||
pod 'Libass', :git => 'https://github.com/kingslay/FFmpegKit.git', :branch => 'main'
|
||||
|
||||
|
|
|
|||
|
|
@ -2760,7 +2760,7 @@ PODS:
|
|||
- Yoga (0.0.0)
|
||||
|
||||
DEPENDENCIES:
|
||||
- DisplayCriteria (from `https://github.com/kingslay/KSPlayer.git`, branch `main`)
|
||||
- DisplayCriteria (from `../KSPlayer`)
|
||||
- EASClient (from `../node_modules/expo-eas-client/ios`)
|
||||
- EXApplication (from `../node_modules/expo-application/ios`)
|
||||
- EXConstants (from `../node_modules/expo-constants/ios`)
|
||||
|
|
@ -2800,7 +2800,7 @@ DEPENDENCIES:
|
|||
- FFmpegKit (from `https://github.com/kingslay/FFmpegKit.git`, branch `main`)
|
||||
- hermes-engine (from `../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec`)
|
||||
- ImageColors (from `../node_modules/react-native-image-colors/ios`)
|
||||
- KSPlayer (from `https://github.com/kingslay/KSPlayer.git`, branch `main`)
|
||||
- KSPlayer (from `../KSPlayer`)
|
||||
- Libass (from `https://github.com/kingslay/FFmpegKit.git`, branch `main`)
|
||||
- lottie-react-native (from `../node_modules/lottie-react-native`)
|
||||
- NitroMmkv (from `../node_modules/react-native-mmkv`)
|
||||
|
|
@ -2910,8 +2910,7 @@ SPEC REPOS:
|
|||
|
||||
EXTERNAL SOURCES:
|
||||
DisplayCriteria:
|
||||
:branch: main
|
||||
:git: https://github.com/kingslay/KSPlayer.git
|
||||
:path: "../KSPlayer"
|
||||
EASClient:
|
||||
:path: "../node_modules/expo-eas-client/ios"
|
||||
EXApplication:
|
||||
|
|
@ -2993,8 +2992,7 @@ EXTERNAL SOURCES:
|
|||
ImageColors:
|
||||
:path: "../node_modules/react-native-image-colors/ios"
|
||||
KSPlayer:
|
||||
:branch: main
|
||||
:git: https://github.com/kingslay/KSPlayer.git
|
||||
:path: "../KSPlayer"
|
||||
Libass:
|
||||
:branch: main
|
||||
:git: https://github.com/kingslay/FFmpegKit.git
|
||||
|
|
@ -3174,15 +3172,9 @@ EXTERNAL SOURCES:
|
|||
:path: "../node_modules/react-native/ReactCommon/yoga"
|
||||
|
||||
CHECKOUT OPTIONS:
|
||||
DisplayCriteria:
|
||||
:commit: a7cddd878f557afa6a1f2faad9d756949406adde
|
||||
:git: https://github.com/kingslay/KSPlayer.git
|
||||
FFmpegKit:
|
||||
:commit: d7048037a2eb94a3b08113fbf43aa92bdcb332d9
|
||||
:git: https://github.com/kingslay/FFmpegKit.git
|
||||
KSPlayer:
|
||||
:commit: a7cddd878f557afa6a1f2faad9d756949406adde
|
||||
:git: https://github.com/kingslay/KSPlayer.git
|
||||
Libass:
|
||||
:commit: d7048037a2eb94a3b08113fbf43aa92bdcb332d9
|
||||
:git: https://github.com/kingslay/FFmpegKit.git
|
||||
|
|
@ -3332,6 +3324,6 @@ SPEC CHECKSUMS:
|
|||
SwiftUIIntrospect: fee9aa07293ee280373a591e1824e8ddc869ba5d
|
||||
Yoga: 051f086b5ccf465ff2ed38a2cf5a558ae01aaaa1
|
||||
|
||||
PODFILE CHECKSUM: 7c74c9cd2c7f3df7ab68b4284d9f324282e54542
|
||||
PODFILE CHECKSUM: b884d1ff07ac4a43323bce2e2e1342592513858c
|
||||
|
||||
COCOAPODS: 1.16.2
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ interface KSPlayerViewProps {
|
|||
subtitleFontSize?: number;
|
||||
subtitleTextColor?: string;
|
||||
subtitleBackgroundColor?: string;
|
||||
subtitleOutlineEnabled?: boolean;
|
||||
resizeMode?: 'contain' | 'cover' | 'stretch';
|
||||
onLoad?: (data: any) => void;
|
||||
onProgress?: (data: any) => void;
|
||||
|
|
@ -60,6 +61,7 @@ export interface KSPlayerProps {
|
|||
subtitleFontSize?: number;
|
||||
subtitleTextColor?: string;
|
||||
subtitleBackgroundColor?: string;
|
||||
subtitleOutlineEnabled?: boolean;
|
||||
resizeMode?: 'contain' | 'cover' | 'stretch';
|
||||
onLoad?: (data: any) => void;
|
||||
onProgress?: (data: any) => void;
|
||||
|
|
@ -210,6 +212,7 @@ const KSPlayer = forwardRef<KSPlayerRef, KSPlayerProps>((props, ref) => {
|
|||
subtitleFontSize={props.subtitleFontSize}
|
||||
subtitleTextColor={props.subtitleTextColor}
|
||||
subtitleBackgroundColor={props.subtitleBackgroundColor}
|
||||
subtitleOutlineEnabled={props.subtitleOutlineEnabled}
|
||||
resizeMode={props.resizeMode}
|
||||
onLoad={(e: any) => props.onLoad?.(e?.nativeEvent ?? e)}
|
||||
onProgress={(e: any) => props.onProgress?.(e?.nativeEvent ?? e)}
|
||||
|
|
|
|||
|
|
@ -616,6 +616,10 @@ const KSPlayerCore: React.FC = () => {
|
|||
/>
|
||||
|
||||
{/* Video Surface & Pinch Zoom */}
|
||||
{/*
|
||||
For KSPlayer built-in subtitles (internal text tracks), we intentionally force background OFF.
|
||||
Background styling is only supported/used for custom (external/addon) subtitles overlay.
|
||||
*/}
|
||||
<KSPlayerSurface
|
||||
ksPlayerRef={ksPlayerRef}
|
||||
uri={uri}
|
||||
|
|
@ -656,7 +660,20 @@ const KSPlayerCore: React.FC = () => {
|
|||
screenHeight={screenDimensions.height}
|
||||
customVideoStyles={{ width: '100%', height: '100%' }}
|
||||
subtitleTextColor={customSubs.subtitleTextColor}
|
||||
subtitleBackgroundColor={customSubs.subtitleBackground ? `rgba(0,0,0,${customSubs.subtitleBgOpacity})` : 'transparent'}
|
||||
subtitleBackgroundColor={
|
||||
tracks.selectedTextTrack !== null &&
|
||||
tracks.selectedTextTrack >= 0 &&
|
||||
!customSubs.useCustomSubtitles
|
||||
? 'rgba(0,0,0,0)'
|
||||
: (customSubs.subtitleBackground ? `rgba(0,0,0,${customSubs.subtitleBgOpacity})` : 'transparent')
|
||||
}
|
||||
subtitleOutlineEnabled={
|
||||
tracks.selectedTextTrack !== null &&
|
||||
tracks.selectedTextTrack >= 0 &&
|
||||
!customSubs.useCustomSubtitles
|
||||
? customSubs.subtitleOutline
|
||||
: false
|
||||
}
|
||||
subtitleFontSize={customSubs.subtitleSize}
|
||||
subtitleBottomOffset={customSubs.subtitleBottomOffset}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ interface KSPlayerSurfaceProps {
|
|||
subtitleBackgroundColor?: string;
|
||||
subtitleFontSize?: number;
|
||||
subtitleBottomOffset?: number;
|
||||
subtitleOutlineEnabled?: boolean;
|
||||
}
|
||||
|
||||
export const KSPlayerSurface: React.FC<KSPlayerSurfaceProps> = ({
|
||||
|
|
@ -74,7 +75,8 @@ export const KSPlayerSurface: React.FC<KSPlayerSurfaceProps> = ({
|
|||
subtitleTextColor,
|
||||
subtitleBackgroundColor,
|
||||
subtitleFontSize,
|
||||
subtitleBottomOffset
|
||||
subtitleBottomOffset,
|
||||
subtitleOutlineEnabled
|
||||
}) => {
|
||||
const pinchRef = useRef<PinchGestureHandler>(null);
|
||||
|
||||
|
|
@ -146,6 +148,7 @@ export const KSPlayerSurface: React.FC<KSPlayerSurfaceProps> = ({
|
|||
subtitleBackgroundColor={subtitleBackgroundColor}
|
||||
subtitleFontSize={subtitleFontSize}
|
||||
subtitleBottomOffset={subtitleBottomOffset}
|
||||
subtitleOutlineEnabled={subtitleOutlineEnabled}
|
||||
onLoad={handleLoad}
|
||||
onProgress={onProgress}
|
||||
onBuffering={handleBuffering}
|
||||
|
|
|
|||
|
|
@ -239,7 +239,8 @@ export const SubtitleModals: React.FC<SubtitleModalsProps> = ({
|
|||
<View style={{ height: previewHeight, justifyContent: 'flex-end' }}>
|
||||
<View style={{ alignItems: subtitleAlign === 'center' ? 'center' : subtitleAlign === 'left' ? 'flex-start' : 'flex-end', marginBottom: Math.min(80, subtitleBottomOffset) }}>
|
||||
<View style={{
|
||||
backgroundColor: subtitleBackground ? `rgba(0,0,0,${subtitleBgOpacity})` : 'transparent',
|
||||
// Built-in (KSPlayer internal) subtitles: force background off in UI preview.
|
||||
backgroundColor: isUsingInternalSubtitle ? 'transparent' : (subtitleBackground ? `rgba(0,0,0,${subtitleBgOpacity})` : 'transparent'),
|
||||
borderRadius: 8,
|
||||
paddingHorizontal: isCompact ? 10 : 12,
|
||||
paddingVertical: isCompact ? 6 : 8,
|
||||
|
|
@ -331,19 +332,21 @@ export const SubtitleModals: React.FC<SubtitleModalsProps> = ({
|
|||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
{/* Show Background */}
|
||||
<View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
|
||||
<MaterialIcons name="layers" size={16} color="rgba(255,255,255,0.7)" />
|
||||
<Text style={{ color: '#fff', fontWeight: '600', marginLeft: 8 }}>{t('player_ui.show_background')}</Text>
|
||||
{/* Background is only for CustomSubtitles (external/addon). Built-in subtitles force background OFF. */}
|
||||
{!isUsingInternalSubtitle && (
|
||||
<View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
|
||||
<MaterialIcons name="layers" size={16} color="rgba(255,255,255,0.7)" />
|
||||
<Text style={{ color: '#fff', fontWeight: '600', marginLeft: 8 }}>{t('player_ui.show_background')}</Text>
|
||||
</View>
|
||||
<TouchableOpacity
|
||||
style={{ width: isCompact ? 48 : 54, height: isCompact ? 28 : 30, backgroundColor: subtitleBackground ? 'white' : 'rgba(255,255,255,0.25)', borderRadius: 15, justifyContent: 'center', alignItems: subtitleBackground ? 'flex-end' : 'flex-start', paddingHorizontal: 3 }}
|
||||
onPress={toggleSubtitleBackground}
|
||||
>
|
||||
<View style={{ width: 24, height: 24, backgroundColor: subtitleBackground ? 'black' : 'white', borderRadius: 12 }} />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
<TouchableOpacity
|
||||
style={{ width: isCompact ? 48 : 54, height: isCompact ? 28 : 30, backgroundColor: subtitleBackground ? 'white' : 'rgba(255,255,255,0.25)', borderRadius: 15, justifyContent: 'center', alignItems: subtitleBackground ? 'flex-end' : 'flex-start', paddingHorizontal: 3 }}
|
||||
onPress={toggleSubtitleBackground}
|
||||
>
|
||||
<View style={{ width: 24, height: 24, backgroundColor: subtitleBackground ? 'black' : 'white', borderRadius: 12 }} />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
|
||||
{/* Advanced controls */}
|
||||
|
|
@ -392,21 +395,23 @@ export const SubtitleModals: React.FC<SubtitleModalsProps> = ({
|
|||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
{/* Background Opacity */}
|
||||
<View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
<Text style={{ color: 'white', fontWeight: '600' }}>{t('player_ui.background_opacity')}</Text>
|
||||
<View style={{ flexDirection: 'row', gap: 8, alignItems: 'center' }}>
|
||||
<TouchableOpacity onPress={() => setSubtitleBgOpacity(Math.max(0, +(subtitleBgOpacity - 0.1).toFixed(1)))} style={{ width: controlBtn.size, height: controlBtn.size, borderRadius: controlBtn.radius, backgroundColor: 'rgba(255,255,255,0.18)', alignItems: 'center', justifyContent: 'center' }}>
|
||||
<MaterialIcons name="remove" color="#fff" size={18} />
|
||||
</TouchableOpacity>
|
||||
<View style={{ minWidth: 48, paddingHorizontal: 6, paddingVertical: 4, borderRadius: 10, backgroundColor: 'rgba(255,255,255,0.12)' }}>
|
||||
<Text style={{ color: 'white', textAlign: 'center', fontWeight: '700' }}>{subtitleBgOpacity.toFixed(1)}</Text>
|
||||
{/* Background Opacity (CustomSubtitles only) */}
|
||||
{!isUsingInternalSubtitle && (
|
||||
<View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
<Text style={{ color: 'white', fontWeight: '600' }}>{t('player_ui.background_opacity')}</Text>
|
||||
<View style={{ flexDirection: 'row', gap: 8, alignItems: 'center' }}>
|
||||
<TouchableOpacity onPress={() => setSubtitleBgOpacity(Math.max(0, +(subtitleBgOpacity - 0.1).toFixed(1)))} style={{ width: controlBtn.size, height: controlBtn.size, borderRadius: controlBtn.radius, backgroundColor: 'rgba(255,255,255,0.18)', alignItems: 'center', justifyContent: 'center' }}>
|
||||
<MaterialIcons name="remove" color="#fff" size={18} />
|
||||
</TouchableOpacity>
|
||||
<View style={{ minWidth: 48, paddingHorizontal: 6, paddingVertical: 4, borderRadius: 10, backgroundColor: 'rgba(255,255,255,0.12)' }}>
|
||||
<Text style={{ color: 'white', textAlign: 'center', fontWeight: '700' }}>{subtitleBgOpacity.toFixed(1)}</Text>
|
||||
</View>
|
||||
<TouchableOpacity onPress={() => setSubtitleBgOpacity(Math.min(1, +(subtitleBgOpacity + 0.1).toFixed(1)))} style={{ width: controlBtn.size, height: controlBtn.size, borderRadius: controlBtn.radius, backgroundColor: 'rgba(255,255,255,0.18)', alignItems: 'center', justifyContent: 'center' }}>
|
||||
<MaterialIcons name="add" color="#fff" size={18} />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
<TouchableOpacity onPress={() => setSubtitleBgOpacity(Math.min(1, +(subtitleBgOpacity + 0.1).toFixed(1)))} style={{ width: controlBtn.size, height: controlBtn.size, borderRadius: controlBtn.radius, backgroundColor: 'rgba(255,255,255,0.18)', alignItems: 'center', justifyContent: 'center' }}>
|
||||
<MaterialIcons name="add" color="#fff" size={18} />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
{!isUsingInternalSubtitle && (
|
||||
<View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
<Text style={{ color: 'white', fontWeight: '600' }}>{t('player_ui.text_shadow')}</Text>
|
||||
|
|
@ -416,28 +421,43 @@ export const SubtitleModals: React.FC<SubtitleModalsProps> = ({
|
|||
</View>
|
||||
)}
|
||||
{/* Outline controls (now supported for ExoPlayer internal via native patch) */}
|
||||
<View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
<Text style={{ color: 'white' }}>{t('player_ui.outline_color')}</Text>
|
||||
<View style={{ flexDirection: 'row', gap: 8 }}>
|
||||
{['#000000', '#FFFFFF', '#00E5FF', '#FF5C5C'].map(c => (
|
||||
<TouchableOpacity key={c} onPress={() => setSubtitleOutlineColor(c)} style={{ width: 22, height: 22, borderRadius: 11, backgroundColor: c, borderWidth: 2, borderColor: subtitleOutlineColor === c ? '#fff' : 'rgba(255,255,255,0.3)' }} />
|
||||
))}
|
||||
</View>
|
||||
</View>
|
||||
<View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
<Text style={{ color: 'white' }}>{t('player_ui.outline_width')}</Text>
|
||||
<View style={{ flexDirection: 'row', gap: 8, alignItems: 'center' }}>
|
||||
<TouchableOpacity onPress={() => setSubtitleOutlineWidth(Math.max(0, subtitleOutlineWidth - 1))} style={{ width: controlBtn.size, height: controlBtn.size, borderRadius: controlBtn.radius, backgroundColor: 'rgba(255,255,255,0.18)', alignItems: 'center', justifyContent: 'center' }}>
|
||||
<MaterialIcons name="remove" color="#fff" size={18} />
|
||||
{isUsingInternalSubtitle ? (
|
||||
// KSPlayer built-in subtitles: only expose an Outline on/off toggle (no width control).
|
||||
<View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
<Text style={{ color: 'white', fontWeight: '600' }}>{t('player_ui.outline')}</Text>
|
||||
<TouchableOpacity
|
||||
onPress={() => setSubtitleOutline(!subtitleOutline)}
|
||||
style={{ paddingHorizontal: 10, paddingVertical: 8, borderRadius: 10, backgroundColor: subtitleOutline ? 'rgba(255,255,255,0.18)' : 'rgba(255,255,255,0.08)', borderWidth: 1, borderColor: 'rgba(255,255,255,0.15)', alignItems: 'center' }}
|
||||
>
|
||||
<Text style={{ color: '#fff', fontWeight: '700' }}>{subtitleOutline ? t('player_ui.on') : t('player_ui.off')}</Text>
|
||||
</TouchableOpacity>
|
||||
<View style={{ minWidth: 42, paddingHorizontal: 6, paddingVertical: 4, borderRadius: 10, backgroundColor: 'rgba(255,255,255,0.12)' }}>
|
||||
<Text style={{ color: 'white', textAlign: 'center', fontWeight: '700' }}>{subtitleOutlineWidth}</Text>
|
||||
</View>
|
||||
) : (
|
||||
<>
|
||||
<View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
<Text style={{ color: 'white' }}>{t('player_ui.outline_color')}</Text>
|
||||
<View style={{ flexDirection: 'row', gap: 8 }}>
|
||||
{['#000000', '#FFFFFF', '#00E5FF', '#FF5C5C'].map(c => (
|
||||
<TouchableOpacity key={c} onPress={() => setSubtitleOutlineColor(c)} style={{ width: 22, height: 22, borderRadius: 11, backgroundColor: c, borderWidth: 2, borderColor: subtitleOutlineColor === c ? '#fff' : 'rgba(255,255,255,0.3)' }} />
|
||||
))}
|
||||
</View>
|
||||
</View>
|
||||
<TouchableOpacity onPress={() => setSubtitleOutlineWidth(subtitleOutlineWidth + 1)} style={{ width: controlBtn.size, height: controlBtn.size, borderRadius: controlBtn.radius, backgroundColor: 'rgba(255,255,255,0.18)', alignItems: 'center', justifyContent: 'center' }}>
|
||||
<MaterialIcons name="add" color="#fff" size={18} />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
<View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
<Text style={{ color: 'white' }}>{t('player_ui.outline_width')}</Text>
|
||||
<View style={{ flexDirection: 'row', gap: 8, alignItems: 'center' }}>
|
||||
<TouchableOpacity onPress={() => setSubtitleOutlineWidth(Math.max(0, subtitleOutlineWidth - 1))} style={{ width: controlBtn.size, height: controlBtn.size, borderRadius: controlBtn.radius, backgroundColor: 'rgba(255,255,255,0.18)', alignItems: 'center', justifyContent: 'center' }}>
|
||||
<MaterialIcons name="remove" color="#fff" size={18} />
|
||||
</TouchableOpacity>
|
||||
<View style={{ minWidth: 42, paddingHorizontal: 6, paddingVertical: 4, borderRadius: 10, backgroundColor: 'rgba(255,255,255,0.12)' }}>
|
||||
<Text style={{ color: 'white', textAlign: 'center', fontWeight: '700' }}>{subtitleOutlineWidth}</Text>
|
||||
</View>
|
||||
<TouchableOpacity onPress={() => setSubtitleOutlineWidth(subtitleOutlineWidth + 1)} style={{ width: controlBtn.size, height: controlBtn.size, borderRadius: controlBtn.radius, backgroundColor: 'rgba(255,255,255,0.18)', alignItems: 'center', justifyContent: 'center' }}>
|
||||
<MaterialIcons name="add" color="#fff" size={18} />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
</>
|
||||
)}
|
||||
{!isUsingInternalSubtitle && (
|
||||
<View style={{ flexDirection: isCompact ? 'column' : 'row', justifyContent: 'space-between', gap: 12 }}>
|
||||
<View style={{ flex: 1 }}>
|
||||
|
|
|
|||
|
|
@ -410,6 +410,7 @@
|
|||
"on": "تشغيل",
|
||||
"off": "إيقاف",
|
||||
"outline_color": "لون الإطار",
|
||||
"outline": "الإطار",
|
||||
"outline_width": "عرض الإطار",
|
||||
"letter_spacing": "تباعد الأحرف",
|
||||
"line_height": "ارتفاع السطر",
|
||||
|
|
|
|||
|
|
@ -410,6 +410,7 @@
|
|||
"on": "Ein",
|
||||
"off": "Aus",
|
||||
"outline_color": "Umrandungsfarbe",
|
||||
"outline": "Umrandung",
|
||||
"outline_width": "Umrandungsbreite",
|
||||
"letter_spacing": "Zeichenabstand",
|
||||
"line_height": "Zeilenhöhe",
|
||||
|
|
|
|||
|
|
@ -410,6 +410,7 @@
|
|||
"on": "On",
|
||||
"off": "Off",
|
||||
"outline_color": "Outline Color",
|
||||
"outline": "Outline",
|
||||
"outline_width": "Outline Width",
|
||||
"letter_spacing": "Letter Spacing",
|
||||
"line_height": "Line Height",
|
||||
|
|
|
|||
|
|
@ -410,6 +410,7 @@
|
|||
"on": "Sí",
|
||||
"off": "No",
|
||||
"outline_color": "Color de contorno",
|
||||
"outline": "Contorno",
|
||||
"outline_width": "Ancho de contorno",
|
||||
"letter_spacing": "Espaciado de letras",
|
||||
"line_height": "Altura de línea",
|
||||
|
|
|
|||
|
|
@ -410,6 +410,7 @@
|
|||
"on": "Activé",
|
||||
"off": "Désactivé",
|
||||
"outline_color": "Couleur du contour",
|
||||
"outline": "Contour",
|
||||
"outline_width": "Largeur du contour",
|
||||
"letter_spacing": "Espacement des lettres",
|
||||
"line_height": "Hauteur de ligne",
|
||||
|
|
|
|||
|
|
@ -410,6 +410,7 @@
|
|||
"on": "Attivo",
|
||||
"off": "Disattivo",
|
||||
"outline_color": "Colore contorno",
|
||||
"outline": "Contorno",
|
||||
"outline_width": "Larghezza contorno",
|
||||
"letter_spacing": "Spaziatura lettere",
|
||||
"line_height": "Altezza riga",
|
||||
|
|
|
|||
|
|
@ -410,6 +410,7 @@
|
|||
"on": "Ligado",
|
||||
"off": "Desligado",
|
||||
"outline_color": "Cor do Contorno",
|
||||
"outline": "Contorno",
|
||||
"outline_width": "Largura do Contorno",
|
||||
"letter_spacing": "Espaçamento de Letras",
|
||||
"line_height": "Altura da Linha",
|
||||
|
|
|
|||
|
|
@ -410,6 +410,7 @@
|
|||
"on": "Ligado",
|
||||
"off": "Desligado",
|
||||
"outline_color": "Cor do Contorno",
|
||||
"outline": "Contorno",
|
||||
"outline_width": "Largura do Contorno",
|
||||
"letter_spacing": "Espaçamento de Letras",
|
||||
"line_height": "Altura da Linha",
|
||||
|
|
|
|||
Loading…
Reference in a new issue