From 838f74caa2153ed046bfbdc09e0845cd2f737fe6 Mon Sep 17 00:00:00 2001 From: tapframe Date: Fri, 26 Sep 2025 21:17:47 +0530 Subject: [PATCH] added back ksplayer files --- ios/KSPlayerManager.m | 42 +++ ios/KSPlayerModule.swift | 37 ++ ios/KSPlayerView.swift | 519 ++++++++++++++++++++++++++++ ios/KSPlayerViewManager.swift | 99 ++++++ ios/Nuvio.xcodeproj/project.pbxproj | 58 +++- 5 files changed, 738 insertions(+), 17 deletions(-) create mode 100644 ios/KSPlayerManager.m create mode 100644 ios/KSPlayerModule.swift create mode 100644 ios/KSPlayerView.swift create mode 100644 ios/KSPlayerViewManager.swift diff --git a/ios/KSPlayerManager.m b/ios/KSPlayerManager.m new file mode 100644 index 00000000..f5210274 --- /dev/null +++ b/ios/KSPlayerManager.m @@ -0,0 +1,42 @@ +// +// KSPlayerManager.m +// Nuvio +// +// Created by KSPlayer integration +// + +#import +#import +#import + +@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(audioTrack, NSNumber) +RCT_EXPORT_VIEW_PROPERTY(textTrack, NSNumber) + +// 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(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) + +@end + +@interface RCT_EXTERN_MODULE(KSPlayerModule, RCTEventEmitter) + +RCT_EXTERN_METHOD(getTracks:(nonnull NSNumber *)nodeTag resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) + +@end diff --git a/ios/KSPlayerModule.swift b/ios/KSPlayerModule.swift new file mode 100644 index 00000000..ee487c12 --- /dev/null +++ b/ios/KSPlayerModule.swift @@ -0,0 +1,37 @@ +// +// 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) { + 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) + } + } + } +} diff --git a/ios/KSPlayerView.swift b/ios/KSPlayerView.swift new file mode 100644 index 00000000..7570f297 --- /dev/null +++ b/ios/KSPlayerView.swift @@ -0,0 +1,519 @@ +// +// KSPlayerView.swift +// Nuvio +// +// Created by KSPlayer integration +// + +import Foundation +import KSPlayer +import React + +@objc(KSPlayerView) +class KSPlayerView: UIView { + private var playerView: IOSVideoPlayerView! + private var currentSource: NSDictionary? + private var isPaused = false + private var currentVolume: Float = 1.0 + weak var viewManager: KSPlayerViewManager? + + // Event blocks for Fabric + @objc var onLoad: RCTDirectEventBlock? + @objc var onProgress: RCTDirectEventBlock? + @objc var onBuffering: RCTDirectEventBlock? + @objc var onEnd: RCTDirectEventBlock? + @objc var onError: RCTDirectEventBlock? + @objc var onBufferingProgress: RCTDirectEventBlock? + + // Property setters that React Native will call + @objc var source: NSDictionary? { + didSet { + if let source = source { + setSource(source) + } + } + } + + @objc var paused: Bool = false { + didSet { + setPaused(paused) + } + } + + @objc var volume: NSNumber = 1.0 { + didSet { + setVolume(volume.floatValue) + } + } + + @objc var audioTrack: NSNumber = -1 { + didSet { + setAudioTrack(audioTrack.intValue) + } + } + + @objc var textTrack: NSNumber = -1 { + didSet { + setTextTrack(textTrack.intValue) + } + } + + override init(frame: CGRect) { + super.init(frame: frame) + setupPlayerView() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + setupPlayerView() + } + + private func setupPlayerView() { + playerView = IOSVideoPlayerView() + playerView.translatesAutoresizingMaskIntoConstraints = false + // Hide native controls - we use custom React Native controls + playerView.isUserInteractionEnabled = false + // Hide KSPlayer's built-in overlay/controls + playerView.controllerView.isHidden = true + playerView.contentOverlayView.isHidden = true + playerView.controllerView.alpha = 0 + playerView.contentOverlayView.alpha = 0 + playerView.controllerView.gestureRecognizers?.forEach { $0.isEnabled = false } + addSubview(playerView) + + NSLayoutConstraint.activate([ + playerView.topAnchor.constraint(equalTo: topAnchor), + playerView.leadingAnchor.constraint(equalTo: leadingAnchor), + playerView.trailingAnchor.constraint(equalTo: trailingAnchor), + playerView.bottomAnchor.constraint(equalTo: bottomAnchor) + ]) + + // Set up player delegates and callbacks + setupPlayerCallbacks() + } + + private func setupPlayerCallbacks() { + // Configure KSOptions (use static defaults where required) + KSOptions.isAutoPlay = false + #if targetEnvironment(simulator) + // Simulator: disable hardware decode and MEPlayer to avoid VT/Vulkan issues + KSOptions.hardwareDecode = false + KSOptions.asynchronousDecompression = false + KSOptions.secondPlayerType = nil + #endif + } + + func setSource(_ source: NSDictionary) { + currentSource = source + + guard let uri = source["uri"] as? String else { + print("KSPlayerView: No URI provided") + sendEvent("onError", ["error": "No URI provided in source"]) + return + } + + // Validate URL before proceeding + guard let url = URL(string: uri), url.scheme != nil else { + print("KSPlayerView: Invalid URL format: \(uri)") + sendEvent("onError", ["error": "Invalid URL format: \(uri)"]) + return + } + + var headers: [String: String] = [:] + if let headersDict = source["headers"] as? [String: String] { + headers = headersDict + } + + // Choose player pipeline based on format + let isMKV = uri.lowercased().contains(".mkv") + #if targetEnvironment(simulator) + if isMKV { + // MKV not supported on AVPlayer in Simulator and MEPlayer is disabled + sendEvent("onError", ["error": "MKV playback is not supported in the iOS Simulator. Test on a real device."]) + } + #else + if isMKV { + // Prefer MEPlayer (FFmpeg) for MKV on device + KSOptions.firstPlayerType = KSMEPlayer.self + KSOptions.secondPlayerType = nil + } else { + KSOptions.firstPlayerType = KSAVPlayer.self + KSOptions.secondPlayerType = KSMEPlayer.self + } + #endif + + // Create KSPlayerResource with validated URL + let resource = KSPlayerResource(url: url, options: createOptions(with: headers), name: "Video") + + print("KSPlayerView: Setting source: \(uri)") + print("KSPlayerView: URL scheme: \(url.scheme ?? "unknown"), host: \(url.host ?? "unknown")") + + playerView.set(resource: resource) + + // Set up delegate after setting the resource + playerView.playerLayer?.delegate = self + + // Apply current state + if isPaused { + playerView.pause() + } else { + playerView.play() + } + + setVolume(currentVolume) + } + + private func createOptions(with headers: [String: String]) -> KSOptions { + let options = KSOptions() + // Disable native player remote control center integration; use RN controls + options.registerRemoteControll = false + + // Configure audio for proper dialogue mixing using FFmpeg's pan filter + // This approach uses standard audio engineering practices for multi-channel downmixing + + // Use conservative center channel mixing that preserves spatial audio + // c0 (Left) = 70% original left + 30% center (dialogue) + 20% rear left + // c1 (Right) = 70% original right + 30% center (dialogue) + 20% rear right + // This creates natural dialogue presence without the "playing on both ears" effect + options.audioFilters.append("pan=stereo|c0=0.7*c0+0.3*c2+0.2*c4|c1=0.7*c1+0.3*c2+0.2*c5") + + // Alternative: Use FFmpeg's surround filter for more sophisticated downmixing + // This provides better spatial audio processing and natural dialogue mixing + // options.audioFilters.append("surround=ang=45") + + #if targetEnvironment(simulator) + options.hardwareDecode = false + options.asynchronousDecompression = false + #else + options.hardwareDecode = KSOptions.hardwareDecode + #endif + if !headers.isEmpty { + // Clean and validate headers before adding + var cleanHeaders: [String: String] = [:] + for (key, value) in headers { + // Remove any null or empty values + if !value.isEmpty && value != "null" { + cleanHeaders[key] = value + } + } + + if !cleanHeaders.isEmpty { + options.appendHeader(cleanHeaders) + print("KSPlayerView: Added headers: \(cleanHeaders.keys.joined(separator: ", "))") + + if let referer = cleanHeaders["Referer"] ?? cleanHeaders["referer"] { + options.referer = referer + print("KSPlayerView: Set referer: \(referer)") + } + } + } + return options + } + + func setPaused(_ paused: Bool) { + isPaused = paused + if paused { + playerView.pause() + } else { + playerView.play() + } + } + + func setVolume(_ volume: Float) { + currentVolume = volume + playerView.playerLayer?.player.playbackVolume = volume + } + + func seek(to time: TimeInterval) { + guard let playerLayer = playerView.playerLayer, + playerLayer.player.isReadyToPlay, + playerLayer.player.seekable else { + print("KSPlayerView: Cannot seek - player not ready or not seekable") + return + } + + playerView.seek(time: time) { success in + if success { + print("KSPlayerView: Seek successful to \(time)") + } else { + print("KSPlayerView: Seek failed to \(time)") + } + } + } + + func setAudioTrack(_ trackId: Int) { + if let player = playerView.playerLayer?.player { + let audioTracks = player.tracks(mediaType: .audio) + print("KSPlayerView: Available audio tracks count: \(audioTracks.count)") + print("KSPlayerView: Requested track ID: \(trackId)") + + // Debug: Print all track information + for (index, track) in audioTracks.enumerated() { + print("KSPlayerView: Track \(index) - ID: \(track.trackID), Name: '\(track.name)', Language: '\(track.language ?? "nil")', isEnabled: \(track.isEnabled)") + } + + // First try to find track by trackID (proper way) + var selectedTrack: MediaPlayerTrack? = nil + var trackIndex: Int = -1 + + // Try to find by exact trackID match + if let track = audioTracks.first(where: { Int($0.trackID) == trackId }) { + selectedTrack = track + trackIndex = audioTracks.firstIndex(where: { $0.trackID == track.trackID }) ?? -1 + print("KSPlayerView: Found track by trackID \(trackId) at index \(trackIndex)") + } + // Fallback: treat trackId as array index + else if trackId >= 0 && trackId < audioTracks.count { + selectedTrack = audioTracks[trackId] + trackIndex = trackId + print("KSPlayerView: Found track by array index \(trackId) (fallback)") + } + + if let track = selectedTrack { + print("KSPlayerView: Selecting track \(trackId) (index: \(trackIndex)): '\(track.name)' (ID: \(track.trackID))") + + // Use KSPlayer's select method which properly handles track selection + player.select(track: track) + + print("KSPlayerView: Successfully selected audio track \(trackId)") + + // Verify the selection worked + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in + let tracksAfter = player.tracks(mediaType: .audio) + for (index, track) in tracksAfter.enumerated() { + print("KSPlayerView: After selection - Track \(index) (ID: \(track.trackID)) isEnabled: \(track.isEnabled)") + } + } + + // Configure audio downmixing for multi-channel tracks + configureAudioDownmixing(for: track) + } else if trackId == -1 { + // Disable all audio tracks (mute) + for track in audioTracks { track.isEnabled = false } + print("KSPlayerView: Disabled all audio tracks") + } else { + print("KSPlayerView: Track \(trackId) not found. Available track IDs: \(audioTracks.map { Int($0.trackID) }), array indices: 0..\(audioTracks.count - 1)") + } + } else { + print("KSPlayerView: No player available for audio track selection") + } + } + + private func configureAudioDownmixing(for track: MediaPlayerTrack) { + // Check if this is a multi-channel audio track that needs downmixing + // This is a simplified check - in practice, you might want to check the actual channel layout + let trackName = track.name.lowercased() + let isMultiChannel = trackName.contains("5.1") || trackName.contains("7.1") || + trackName.contains("truehd") || trackName.contains("dts") || + trackName.contains("dolby") || trackName.contains("atmos") + + if isMultiChannel { + print("KSPlayerView: Detected multi-channel audio track '\(track.name)', ensuring proper dialogue mixing") + print("KSPlayerView: Using FFmpeg pan filter for natural stereo downmixing") + } else { + print("KSPlayerView: Stereo or mono audio track '\(track.name)', no additional downmixing needed") + } + } + + func setTextTrack(_ trackId: Int) { + if let player = playerView.playerLayer?.player { + let textTracks = player.tracks(mediaType: .subtitle) + print("KSPlayerView: Available text tracks count: \(textTracks.count)") + print("KSPlayerView: Requested text track ID: \(trackId)") + + // First try to find track by trackID (proper way) + var selectedTrack: MediaPlayerTrack? = nil + var trackIndex: Int = -1 + + // Try to find by exact trackID match + if let track = textTracks.first(where: { Int($0.trackID) == trackId }) { + selectedTrack = track + trackIndex = textTracks.firstIndex(where: { $0.trackID == track.trackID }) ?? -1 + print("KSPlayerView: Found text track by trackID \(trackId) at index \(trackIndex)") + } + // Fallback: treat trackId as array index + else if trackId >= 0 && trackId < textTracks.count { + selectedTrack = textTracks[trackId] + trackIndex = trackId + print("KSPlayerView: Found text track by array index \(trackId) (fallback)") + } + + if let track = selectedTrack { + print("KSPlayerView: Selecting text track \(trackId) (index: \(trackIndex)): '\(track.name)' (ID: \(track.trackID))") + + // Use KSPlayer's select method which properly handles track selection + player.select(track: track) + + print("KSPlayerView: Successfully selected text track \(trackId)") + } else if trackId == -1 { + // Disable all subtitles + for track in textTracks { track.isEnabled = false } + print("KSPlayerView: Disabled all text tracks") + } else { + print("KSPlayerView: Text track \(trackId) not found. Available track IDs: \(textTracks.map { Int($0.trackID) }), array indices: 0..\(textTracks.count - 1)") + } + } else { + print("KSPlayerView: No player available for text track selection") + } + } + + // Get available tracks for React Native + func getAvailableTracks() -> [String: Any] { + guard let player = playerView.playerLayer?.player else { + return ["audioTracks": [], "textTracks": []] + } + + let audioTracks = player.tracks(mediaType: .audio).enumerated().map { index, track in + return [ + "id": Int(track.trackID), // Use actual track ID, not array index + "index": index, // Keep index for backward compatibility + "name": track.name, + "language": track.language ?? "Unknown", + "languageCode": track.languageCode ?? "", + "isEnabled": track.isEnabled, + "bitRate": track.bitRate, + "bitDepth": track.bitDepth + ] + } + + let textTracks = player.tracks(mediaType: .subtitle).enumerated().map { index, track in + return [ + "id": Int(track.trackID), // Use actual track ID, not array index + "index": index, // Keep index for backward compatibility + "name": track.name, + "language": track.language ?? "Unknown", + "languageCode": track.languageCode ?? "", + "isEnabled": track.isEnabled, + "isImageSubtitle": track.isImageSubtitle + ] + } + + return [ + "audioTracks": audioTracks, + "textTracks": textTracks + ] + } + + // Get current player state for React Native + func getCurrentState() -> [String: Any] { + guard let player = playerView.playerLayer?.player else { + return [:] + } + + return [ + "currentTime": player.currentPlaybackTime, + "duration": player.duration, + "buffered": player.playableTime, + "isPlaying": !isPaused, + "volume": currentVolume + ] + } +} + +extension KSPlayerView: KSPlayerLayerDelegate { + func player(layer: KSPlayerLayer, state: KSPlayerState) { + switch state { + case .readyToPlay: + // Send onLoad event to React Native with track information + let p = layer.player + let tracks = getAvailableTracks() + sendEvent("onLoad", [ + "duration": p.duration, + "currentTime": p.currentPlaybackTime, + "naturalSize": [ + "width": p.naturalSize.width, + "height": p.naturalSize.height + ], + "audioTracks": tracks["audioTracks"] ?? [], + "textTracks": tracks["textTracks"] ?? [] + ]) + case .buffering: + sendEvent("onBuffering", ["isBuffering": true]) + case .bufferFinished: + sendEvent("onBuffering", ["isBuffering": false]) + case .playedToTheEnd: + sendEvent("onEnd", [:]) + case .error: + // Error will be handled by the finish delegate method + break + default: + break + } + } + + func player(layer: KSPlayerLayer, currentTime: TimeInterval, totalTime: TimeInterval) { + let p = layer.player + // Ensure we have valid duration before sending progress updates + if totalTime > 0 { + sendEvent("onProgress", [ + "currentTime": currentTime, + "duration": totalTime, + "bufferTime": p.playableTime + ]) + } + } + + func player(layer: KSPlayerLayer, finish error: Error?) { + if let error = error { + let errorMessage = error.localizedDescription + print("KSPlayerView: Player finished with error: \(errorMessage)") + + // Provide more specific error messages for common issues + var detailedError = errorMessage + if errorMessage.contains("avformat can't open input") { + detailedError = "Unable to open video stream. This could be due to:\n• Invalid or malformed URL\n• Network connectivity issues\n• Server blocking the request\n• Unsupported video format\n• Missing required headers" + } else if errorMessage.contains("timeout") { + detailedError = "Stream connection timed out. The server may be slow or unreachable." + } else if errorMessage.contains("404") || errorMessage.contains("Not Found") { + detailedError = "Video stream not found. The URL may be expired or incorrect." + } else if errorMessage.contains("403") || errorMessage.contains("Forbidden") { + detailedError = "Access denied. The server may be blocking requests or require authentication." + } + + sendEvent("onError", ["error": detailedError]) + } + } + + func player(layer: KSPlayerLayer, bufferedCount: Int, consumeTime: TimeInterval) { + // Handle buffering progress if needed + sendEvent("onBufferingProgress", [ + "bufferedCount": bufferedCount, + "consumeTime": consumeTime + ]) + } +} + +extension KSPlayerView { + private func sendEvent(_ eventName: String, _ body: [String: Any]) { + DispatchQueue.main.async { + switch eventName { + case "onLoad": + self.onLoad?(body) + case "onProgress": + self.onProgress?(body) + case "onBuffering": + self.onBuffering?(body) + case "onEnd": + self.onEnd?([:]) + case "onError": + self.onError?(body) + case "onBufferingProgress": + self.onBufferingProgress?(body) + default: + break + } + } + } + // Renamed to avoid clashing with React's UIView category method + private func findHostViewController() -> UIViewController? { + var responder: UIResponder? = self + while let nextResponder = responder?.next { + if let viewController = nextResponder as? UIViewController { + return viewController + } + responder = nextResponder + } + return nil + } +} + diff --git a/ios/KSPlayerViewManager.swift b/ios/KSPlayerViewManager.swift new file mode 100644 index 00000000..ce9e3f03 --- /dev/null +++ b/ios/KSPlayerViewManager.swift @@ -0,0 +1,99 @@ +// +// 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 RCT_EXPORT_VIEW_PROPERTY + 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 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) { + DispatchQueue.main.async { + if let view = self.bridge.uiManager.view(forReactTag: node) as? KSPlayerView { + view.setTextTrack(Int(truncating: trackId)) + } + } + } + + @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) + } + } + } +} diff --git a/ios/Nuvio.xcodeproj/project.pbxproj b/ios/Nuvio.xcodeproj/project.pbxproj index 2e66d105..d69b1bdb 100644 --- a/ios/Nuvio.xcodeproj/project.pbxproj +++ b/ios/Nuvio.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ @@ -13,6 +13,10 @@ 2F6C4443E26F4184A8EA68F1 /* noop-file.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C4B16751559433B951A993C /* noop-file.swift */; }; 3E461D99554A48A4959DE609 /* SplashScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */; }; 96905EF65AED1B983A6B3ABC /* libPods-Nuvio.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 58EEBF8E8E6FB1BC6CAF49B5 /* 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 */; }; B18059E884C0ABDD17F3DC3D /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = FAC715A2D49A985799AEE119 /* ExpoModulesProvider.swift */; }; BB2F792D24A3F905000567C9 /* Expo.plist in Resources */ = {isa = PBXBuildFile; fileRef = BB2F792C24A3F905000567C9 /* Expo.plist */; }; E660DA8F5C7B39AACB568229 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 8F20890D58E6A611113A359A /* PrivacyInfo.xcprivacy */; }; @@ -30,7 +34,12 @@ 58EEBF8E8E6FB1BC6CAF49B5 /* libPods-Nuvio.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Nuvio.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 6C2E3173556A471DD304B334 /* 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 = ""; }; 7A4D352CD337FB3A3BF06240 /* 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 = ""; }; - 8F20890D58E6A611113A359A /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; name = PrivacyInfo.xcprivacy; path = Nuvio/PrivacyInfo.xcprivacy; sourceTree = ""; }; + 8F20890D58E6A611113A359A /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xml; name = PrivacyInfo.xcprivacy; path = Nuvio/PrivacyInfo.xcprivacy; sourceTree = ""; }; + 9F9D45D12E85C42200C88FAD /* NuvioRelease.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = NuvioRelease.entitlements; path = Nuvio/NuvioRelease.entitlements; sourceTree = ""; }; + 9FBA88F02E86ECD700892850 /* KSPlayerManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KSPlayerManager.m; sourceTree = ""; }; + 9FBA88F12E86ECD700892850 /* KSPlayerModule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KSPlayerModule.swift; sourceTree = ""; }; + 9FBA88F22E86ECD700892850 /* KSPlayerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KSPlayerView.swift; sourceTree = ""; }; + 9FBA88F32E86ECD700892850 /* KSPlayerViewManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KSPlayerViewManager.swift; sourceTree = ""; }; AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = SplashScreen.storyboard; path = Nuvio/SplashScreen.storyboard; sourceTree = ""; }; BB2F792C24A3F905000567C9 /* Expo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Expo.plist; sourceTree = ""; }; ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; @@ -52,6 +61,7 @@ 13B07FAE1A68108700A75B9A /* Nuvio */ = { isa = PBXGroup; children = ( + 9F9D45D12E85C42200C88FAD /* NuvioRelease.entitlements */, BB2F792B24A3F905000567C9 /* Supporting */, 13B07FAF1A68108700A75B9A /* AppDelegate.h */, 13B07FB01A68108700A75B9A /* AppDelegate.mm */, @@ -85,6 +95,10 @@ 83CBB9F61A601CBA00E9B192 = { isa = PBXGroup; children = ( + 9FBA88F02E86ECD700892850 /* KSPlayerManager.m */, + 9FBA88F12E86ECD700892850 /* KSPlayerModule.swift */, + 9FBA88F22E86ECD700892850 /* KSPlayerView.swift */, + 9FBA88F32E86ECD700892850 /* KSPlayerViewManager.swift */, 13B07FAE1A68108700A75B9A /* Nuvio */, 832341AE1AAA6A7D00B99B32 /* Libraries */, 83CBBA001A601CBA00E9B192 /* Products */, @@ -373,6 +387,10 @@ buildActionMask = 2147483647; files = ( 13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */, + 9FBA88F42E86ECD700892850 /* KSPlayerViewManager.swift in Sources */, + 9FBA88F52E86ECD700892850 /* KSPlayerModule.swift in Sources */, + 9FBA88F62E86ECD700892850 /* KSPlayerManager.m in Sources */, + 9FBA88F72E86ECD700892850 /* KSPlayerView.swift in Sources */, 13B07FC11A68108700A75B9A /* main.m in Sources */, B18059E884C0ABDD17F3DC3D /* ExpoModulesProvider.swift in Sources */, 2F6C4443E26F4184A8EA68F1 /* noop-file.swift in Sources */, @@ -400,7 +418,10 @@ ); INFOPLIST_FILE = Nuvio/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 15.1; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); MARKETING_VERSION = 1.0; OTHER_LDFLAGS = ( "$(inherited)", @@ -409,7 +430,7 @@ ); OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG"; PRODUCT_BUNDLE_IDENTIFIER = com.nuvio.app; - PRODUCT_NAME = "Nuvio"; + PRODUCT_NAME = Nuvio; SWIFT_OBJC_BRIDGING_HEADER = "Nuvio/Nuvio-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; @@ -424,14 +445,17 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = Nuvio/Nuvio.entitlements; + CODE_SIGN_ENTITLEMENTS = Nuvio/NuvioRelease.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = NLXTHANK2N; INFOPLIST_FILE = Nuvio/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 15.1; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); MARKETING_VERSION = 1.0; OTHER_LDFLAGS = ( "$(inherited)", @@ -440,7 +464,7 @@ ); OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; PRODUCT_BUNDLE_IDENTIFIER = com.nuvio.app; - PRODUCT_NAME = "Nuvio"; + PRODUCT_NAME = Nuvio; SWIFT_OBJC_BRIDGING_HEADER = "Nuvio/Nuvio-Bridging-Header.h"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; @@ -496,14 +520,14 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 15.1; - LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)"; + LD_RUNPATH_SEARCH_PATHS = ( + /usr/lib/swift, + "$(inherited)", + ); LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift\"$(inherited)\""; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; - OTHER_LDFLAGS = ( - "$(inherited)", - " ", - ); + OTHER_LDFLAGS = "$(inherited) "; REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG"; @@ -552,13 +576,13 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 15.1; - LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)"; + LD_RUNPATH_SEARCH_PATHS = ( + /usr/lib/swift, + "$(inherited)", + ); LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift\"$(inherited)\""; MTL_ENABLE_DEBUG_INFO = NO; - OTHER_LDFLAGS = ( - "$(inherited)", - " ", - ); + OTHER_LDFLAGS = "$(inherited) "; REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; USE_HERMES = true;