mirror of
https://github.com/cranci1/Sora.git
synced 2026-01-11 20:10:24 +00:00
Tv os (#119)
* First tvOS commit Barely usable on tvOS: - changed documentDirectory to cachesDirectory because is the only writeable folder - changed default player on default because custom has too many issues to fix on tvOS - commented a lot of lines because that property or function does not exist on tvOS - to add modules copy the link from Iphone * add conditional compilation Only searchview need a separation of a class due to compilation timeout issue * Fix incompatibility on tvOS --------- Co-authored-by: K <kimiko88@users.noreply.github.com>
This commit is contained in:
parent
713046ce64
commit
28533651f0
21 changed files with 408 additions and 66 deletions
|
|
@ -14,6 +14,7 @@ class DropManager {
|
|||
private init() {}
|
||||
|
||||
func showDrop(title: String, subtitle: String, duration: TimeInterval, icon: UIImage?) {
|
||||
#if !os(tvOS)
|
||||
let position: Drop.Position = .top
|
||||
|
||||
let drop = Drop(
|
||||
|
|
@ -24,5 +25,6 @@ class DropManager {
|
|||
duration: .seconds(duration)
|
||||
)
|
||||
Drops.show(drop)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ public extension UIDevice {
|
|||
}
|
||||
|
||||
func mapToDevice(identifier: String) -> String { // swiftlint:disable:this cyclomatic_complexity
|
||||
#if os(iOS)
|
||||
#if !os(tvOS)
|
||||
switch identifier {
|
||||
case "iPod5,1":
|
||||
return "iPod touch (5th generation)"
|
||||
|
|
|
|||
|
|
@ -21,7 +21,12 @@ class Logger {
|
|||
private let logFilterViewModel = LogFilterViewModel.shared
|
||||
|
||||
private init() {
|
||||
let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
|
||||
#if !os(tvOS)
|
||||
let directory = FileManager.SearchPathDirectory.documentDirectory
|
||||
#elseif os(tvOS)
|
||||
let directory = FileManager.SearchPathDirectory.cachesDirectory
|
||||
#endif
|
||||
let documentDirectory = FileManager.default.urls(for: directory, in: .userDomainMask).first!
|
||||
logFileURL = documentDirectory.appendingPathComponent("logs.txt")
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -94,6 +94,7 @@ struct MusicProgressSlider<T: BinaryFloatingPoint>: View {
|
|||
}
|
||||
.frame(width: bounds.size.width, height: bounds.size.height, alignment: .center)
|
||||
.contentShape(Rectangle())
|
||||
#if !os(tvOS)
|
||||
.gesture(
|
||||
DragGesture(minimumDistance: 0, coordinateSpace: .local)
|
||||
.updating($isActive) { _, state, _ in
|
||||
|
|
@ -108,6 +109,7 @@ struct MusicProgressSlider<T: BinaryFloatingPoint>: View {
|
|||
localTempProgress = 0
|
||||
}
|
||||
)
|
||||
#endif
|
||||
.onChange(of: isActive) { newValue in
|
||||
value = max(min(getPrgValue(), inRange.upperBound), inRange.lowerBound)
|
||||
onEditingChanged(newValue)
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ struct VolumeSlider<T: BinaryFloatingPoint>: View {
|
|||
.animation(animation, value: isActive)
|
||||
}
|
||||
.frame(width: bounds.size.width, height: bounds.size.height)
|
||||
#if !os(tvOS)
|
||||
.gesture(
|
||||
DragGesture(minimumDistance: 0, coordinateSpace: .local)
|
||||
.updating($isActive) { _, state, _ in state = true }
|
||||
|
|
@ -68,6 +69,7 @@ struct VolumeSlider<T: BinaryFloatingPoint>: View {
|
|||
localTempProgress = 0
|
||||
}
|
||||
)
|
||||
#endif
|
||||
.onChange(of: isActive) { newValue in
|
||||
if !newValue {
|
||||
value = sliderValueInRange()
|
||||
|
|
|
|||
|
|
@ -162,7 +162,9 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele
|
|||
private var volumeObserver: NSKeyValueObservation?
|
||||
private var audioSession = AVAudioSession.sharedInstance()
|
||||
private var hiddenVolumeView = MPVolumeView(frame: .zero)
|
||||
#if !os(tvOS)
|
||||
private var systemVolumeSlider: UISlider?
|
||||
#endif
|
||||
private var volumeValue: Double = 0.0
|
||||
private var volumeViewModel = VolumeViewModel()
|
||||
var volumeSliderHostingView: UIView?
|
||||
|
|
@ -281,14 +283,16 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele
|
|||
}
|
||||
}
|
||||
|
||||
#if !os(tvOS)
|
||||
if #available(iOS 16.0, *) {
|
||||
playerViewController.allowsVideoFrameAnalysis = false
|
||||
}
|
||||
#endif
|
||||
|
||||
if let url = subtitlesURL, !url.isEmpty {
|
||||
subtitlesLoader.load(from: url)
|
||||
}
|
||||
|
||||
|
||||
DispatchQueue.main.async {
|
||||
self.isControlsVisible = true
|
||||
NSLayoutConstraint.deactivate(self.watchNextButtonNormalConstraints)
|
||||
|
|
@ -297,7 +301,9 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele
|
|||
self.view.layoutIfNeeded()
|
||||
}
|
||||
|
||||
#if !os(tvOS)
|
||||
hiddenVolumeView.showsRouteButton = false
|
||||
#endif
|
||||
hiddenVolumeView.isHidden = true
|
||||
view.addSubview(hiddenVolumeView)
|
||||
|
||||
|
|
@ -307,9 +313,11 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele
|
|||
hiddenVolumeView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
|
||||
hiddenVolumeView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
|
||||
|
||||
#if !os(tvOS)
|
||||
if let slider = hiddenVolumeView.subviews.first(where: { $0 is UISlider }) as? UISlider {
|
||||
systemVolumeSlider = slider
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
|
||||
|
|
@ -397,10 +405,17 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele
|
|||
}
|
||||
|
||||
private func getSegmentsColor() -> Color {
|
||||
#if !os(tvOS)
|
||||
if let data = UserDefaults.standard.data(forKey: "segmentsColorData"),
|
||||
let uiColor = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? UIColor {
|
||||
let uiColor = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? UIColor {
|
||||
return Color(uiColor)
|
||||
}
|
||||
#elseif os(tvOS)
|
||||
if let data = UserDefaults.standard.data(forKey: "segmentsColorData"),
|
||||
let uiColor = try? NSKeyedUnarchiver.unarchivedObject(ofClass: UIColor.self, from: data) {
|
||||
return Color(uiColor)
|
||||
}
|
||||
#endif
|
||||
return .yellow
|
||||
}
|
||||
|
||||
|
|
@ -596,7 +611,9 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele
|
|||
func holdForPause() {
|
||||
let holdForPauseGesture = UILongPressGestureRecognizer(target: self, action: #selector(handleHoldForPause(_:)))
|
||||
holdForPauseGesture.minimumPressDuration = 1
|
||||
#if !os(tvOS)
|
||||
holdForPauseGesture.numberOfTouchesRequired = 2
|
||||
#endif
|
||||
view.addGestureRecognizer(holdForPauseGesture)
|
||||
}
|
||||
|
||||
|
|
@ -832,9 +849,11 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele
|
|||
|
||||
func volumeSlider() {
|
||||
let container = VolumeSliderContainer(volumeVM: self.volumeViewModel) { newVal in
|
||||
#if !os(tvOS)
|
||||
if let sysSlider = self.systemVolumeSlider {
|
||||
sysSlider.value = Float(newVal)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
let hostingController = UIHostingController(rootView: container)
|
||||
|
|
@ -1042,7 +1061,9 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele
|
|||
skipIntroButton.setImage(introImage, for: .normal)
|
||||
|
||||
skipIntroButton.backgroundColor = UIColor(red: 51/255.0, green: 51/255.0, blue: 51/255.0, alpha: 0.8)
|
||||
#if !os(tvOS)
|
||||
skipIntroButton.contentEdgeInsets = UIEdgeInsets(top: 6, left: 10, bottom: 6, right: 10)
|
||||
#endif
|
||||
skipIntroButton.tintColor = .white
|
||||
skipIntroButton.setTitleColor(.white, for: .normal)
|
||||
skipIntroButton.layer.cornerRadius = 21
|
||||
|
|
@ -1074,7 +1095,9 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele
|
|||
skipOutroButton.setImage(outroImage, for: .normal)
|
||||
|
||||
skipOutroButton.backgroundColor = UIColor(red: 51/255.0, green: 51/255.0, blue: 51/255.0, alpha: 0.8)
|
||||
#if !os(tvOS)
|
||||
skipOutroButton.contentEdgeInsets = UIEdgeInsets(top: 6, left: 10, bottom: 6, right: 10)
|
||||
#endif
|
||||
skipOutroButton.tintColor = .white
|
||||
skipOutroButton.setTitleColor(.white, for: .normal)
|
||||
skipOutroButton.layer.cornerRadius = 21
|
||||
|
|
@ -1248,7 +1271,9 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele
|
|||
skip85Button.setImage(image, for: .normal)
|
||||
|
||||
skip85Button.backgroundColor = UIColor(red: 51/255.0, green: 51/255.0, blue: 51/255.0, alpha: 0.8)
|
||||
#if !os(tvOS)
|
||||
skip85Button.contentEdgeInsets = UIEdgeInsets(top: 6, left: 10, bottom: 6, right: 10)
|
||||
#endif
|
||||
skip85Button.tintColor = .white
|
||||
skip85Button.setTitleColor(.white, for: .normal)
|
||||
skip85Button.layer.cornerRadius = 21
|
||||
|
|
@ -2092,7 +2117,8 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele
|
|||
self.subtitleBackgroundEnabled = settings.backgroundEnabled
|
||||
self.subtitleBottomPadding = settings.bottomPadding
|
||||
}
|
||||
|
||||
|
||||
#if !os(tvOS)
|
||||
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
|
||||
if UserDefaults.standard.bool(forKey: "alwaysLandscape") {
|
||||
return .landscape
|
||||
|
|
@ -2108,13 +2134,14 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele
|
|||
override var prefersStatusBarHidden: Bool {
|
||||
return true
|
||||
}
|
||||
#endif
|
||||
|
||||
func setupAudioSession() {
|
||||
do {
|
||||
let audioSession = AVAudioSession.sharedInstance()
|
||||
try audioSession.setCategory(.playback, mode: .moviePlayback, options: .mixWithOthers)
|
||||
try audioSession.setActive(true)
|
||||
try audioSession.overrideOutputAudioPort(.speaker)
|
||||
// try audioSession.overrideOutputAudioPort(.speaker)
|
||||
} catch {
|
||||
Logger.shared.log("Didn't set up AVAudioSession: \(error)", type: "Debug")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,9 @@ class NormalPlayer: AVPlayerViewController {
|
|||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
setupHoldGesture()
|
||||
setupAudioSession()
|
||||
#if !os(tvOS)
|
||||
setupAudioSession()
|
||||
#endif
|
||||
}
|
||||
|
||||
private func setupHoldGesture() {
|
||||
|
|
@ -52,8 +54,9 @@ class NormalPlayer: AVPlayerViewController {
|
|||
let audioSession = AVAudioSession.sharedInstance()
|
||||
try audioSession.setCategory(.playback, mode: .moviePlayback, options: .mixWithOthers)
|
||||
try audioSession.setActive(true)
|
||||
|
||||
#if !os(tvOS)
|
||||
try audioSession.overrideOutputAudioPort(.speaker)
|
||||
#endif
|
||||
} catch {
|
||||
Logger.shared.log("Didn't set up AVAudioSession: \(error)", type: "Debug")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -147,7 +147,7 @@ class VideoPlayerViewController: UIViewController {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if !os(tvOS)
|
||||
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
|
||||
if UserDefaults.standard.bool(forKey: "alwaysLandscape") {
|
||||
return .landscape
|
||||
|
|
@ -163,7 +163,7 @@ class VideoPlayerViewController: UIViewController {
|
|||
override var prefersStatusBarHidden: Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
#endif
|
||||
deinit {
|
||||
player?.pause()
|
||||
if let timeObserverToken = timeObserverToken {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,9 @@
|
|||
//
|
||||
|
||||
import SwiftUI
|
||||
#if !os(tvOS)
|
||||
import WebKit
|
||||
#endif
|
||||
|
||||
private struct ModuleLink: Identifiable {
|
||||
let id = UUID()
|
||||
|
|
@ -28,7 +30,7 @@ struct CommunityLibraryView: View {
|
|||
.foregroundColor(.red)
|
||||
.padding(.horizontal)
|
||||
}
|
||||
|
||||
#if !os(tvOS)
|
||||
WebView(url: webURL) { linkURL in
|
||||
|
||||
if let comps = URLComponents(url: linkURL, resolvingAgainstBaseURL: false),
|
||||
|
|
@ -37,6 +39,7 @@ struct CommunityLibraryView: View {
|
|||
}
|
||||
}
|
||||
.ignoresSafeArea(edges: .top)
|
||||
#endif
|
||||
}
|
||||
.onAppear(perform: loadURL)
|
||||
.sheet(item: $moduleLinkToAdd) { link in
|
||||
|
|
@ -61,6 +64,7 @@ struct CommunityLibraryView: View {
|
|||
}
|
||||
}
|
||||
|
||||
#if !os(tvOS)
|
||||
struct WebView: UIViewRepresentable {
|
||||
let url: URL?
|
||||
let onCustomScheme: (URL) -> Void
|
||||
|
|
@ -102,3 +106,4 @@ struct WebView: UIViewRepresentable {
|
|||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -67,15 +67,19 @@ struct ModuleAdditionSettingsView: View {
|
|||
InfoRow(title: "Quality", value: metadata.quality)
|
||||
InfoRow(title: "Stream Typed", value: metadata.streamType)
|
||||
InfoRow(title: "Base URL", value: metadata.baseUrl)
|
||||
#if !os(tvOS)
|
||||
.onLongPressGesture {
|
||||
UIPasteboard.general.string = metadata.baseUrl
|
||||
DropManager.shared.showDrop(title: "Copied to Clipboard", subtitle: "", duration: 1.0, icon: UIImage(systemName: "doc.on.clipboard.fill"))
|
||||
}
|
||||
#endif
|
||||
InfoRow(title: "Script URL", value: metadata.scriptUrl)
|
||||
#if !os(tvOS)
|
||||
.onLongPressGesture {
|
||||
UIPasteboard.general.string = metadata.scriptUrl
|
||||
DropManager.shared.showDrop(title: "Copied to Clipboard", subtitle: "", duration: 1.0, icon: UIImage(systemName: "doc.on.clipboard.fill"))
|
||||
}
|
||||
#endif
|
||||
}
|
||||
.padding(.horizontal)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
class ModuleManager: ObservableObject {
|
||||
class ModuleManager: ObservableObject, @unchecked Sendable {
|
||||
@Published var modules: [ScrapingModule] = []
|
||||
|
||||
private let fileManager = FileManager.default
|
||||
|
|
@ -59,7 +59,12 @@ class ModuleManager: ObservableObject {
|
|||
}
|
||||
|
||||
private func getDocumentsDirectory() -> URL {
|
||||
fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0]
|
||||
#if !os(tvOS)
|
||||
let directory = FileManager.SearchPathDirectory.documentDirectory
|
||||
#elseif os(tvOS)
|
||||
let directory = FileManager.SearchPathDirectory.cachesDirectory
|
||||
#endif
|
||||
return fileManager.urls(for: directory, in: .userDomainMask)[0]
|
||||
}
|
||||
|
||||
private func getModulesFilePath() -> URL {
|
||||
|
|
|
|||
|
|
@ -21,8 +21,9 @@ struct LibraryView: View {
|
|||
@State private var isDetailActive: Bool = false
|
||||
|
||||
@State private var continueWatchingItems: [ContinueWatchingItem] = []
|
||||
#if !os(tvOS)
|
||||
@State private var isLandscape: Bool = UIDevice.current.orientation.isLandscape
|
||||
|
||||
#endif
|
||||
private let columns = [
|
||||
GridItem(.adaptive(minimum: 150), spacing: 12)
|
||||
]
|
||||
|
|
@ -166,9 +167,11 @@ struct LibraryView: View {
|
|||
.onAppear {
|
||||
updateOrientation()
|
||||
}
|
||||
#if !os(tvOS)
|
||||
.onReceive(NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)) { _ in
|
||||
updateOrientation()
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
.padding(.vertical, 20)
|
||||
|
|
@ -201,13 +204,19 @@ struct LibraryView: View {
|
|||
|
||||
private func updateOrientation() {
|
||||
DispatchQueue.main.async {
|
||||
#if !os(tvOS)
|
||||
isLandscape = UIDevice.current.orientation.isLandscape
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
private func determineColumns() -> Int {
|
||||
if UIDevice.current.userInterfaceIdiom == .pad {
|
||||
#if !os(tvOS)
|
||||
return isLandscape ? mediaColumnsLandscape : mediaColumnsPortrait
|
||||
#elseif os(tvOS)
|
||||
return mediaColumnsLandscape
|
||||
#endif
|
||||
} else {
|
||||
return verticalSizeClass == .compact ? mediaColumnsLandscape : mediaColumnsPortrait
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ struct EpisodeCell: View {
|
|||
}
|
||||
|
||||
var body: some View {
|
||||
#if !os(tvOS)
|
||||
HStack {
|
||||
ZStack {
|
||||
KFImage(URL(string: episodeImageUrl.isEmpty ? defaultBannerImage : episodeImageUrl))
|
||||
|
|
@ -58,13 +59,7 @@ struct EpisodeCell: View {
|
|||
.aspectRatio(16/9, contentMode: .fill)
|
||||
.frame(width: 100, height: 56)
|
||||
.cornerRadius(8)
|
||||
|
||||
if isLoading {
|
||||
ProgressView()
|
||||
.progressViewStyle(CircularProgressViewStyle())
|
||||
}
|
||||
}
|
||||
|
||||
VStack(alignment: .leading) {
|
||||
Text("Episode \(episodeID + 1)")
|
||||
.font(.system(size: 15))
|
||||
|
|
@ -73,44 +68,102 @@ struct EpisodeCell: View {
|
|||
.font(.system(size: 13))
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer()
|
||||
|
||||
CircularProgressBar(progress: currentProgress)
|
||||
.frame(width: 40, height: 40)
|
||||
}
|
||||
.contentShape(Rectangle())
|
||||
.contextMenu {
|
||||
if progress <= 0.9 {
|
||||
Button(action: markAsWatched) {
|
||||
Label("Mark as Watched", systemImage: "checkmark.circle")
|
||||
Spacer()
|
||||
CircularProgressBar(progress: currentProgress)
|
||||
.frame(width: 40, height: 40)
|
||||
}.contentShape(Rectangle())
|
||||
.contextMenu {
|
||||
if progress <= 0.9 {
|
||||
Button(action: markAsWatched) {
|
||||
Label("Mark as Watched", systemImage: "checkmark.circle")
|
||||
}
|
||||
if progress != 0 {
|
||||
Button(action: resetProgress) {
|
||||
Label("Reset Progress", systemImage: "arrow.counterclockwise")
|
||||
if episodeIndex > 0 {
|
||||
Button(action: onMarkAllPrevious) {
|
||||
Label("Mark All Previous Watched", systemImage: "checkmark.circle.fill")
|
||||
}
|
||||
.onAppear {
|
||||
updateProgress()
|
||||
fetchEpisodeDetails()
|
||||
}
|
||||
.onChange(of: progress) { _ in
|
||||
updateProgress()
|
||||
}
|
||||
.onTapGesture {
|
||||
let imageUrl = episodeImageUrl.isEmpty ? defaultBannerImage : episodeImageUrl
|
||||
onTap(imageUrl)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if progress != 0 {
|
||||
Button(action: resetProgress) {
|
||||
Label("Reset Progress", systemImage: "arrow.counterclockwise")
|
||||
}
|
||||
}
|
||||
|
||||
if episodeIndex > 0 {
|
||||
Button(action: onMarkAllPrevious) {
|
||||
Label("Mark All Previous Watched", systemImage: "checkmark.circle.fill")
|
||||
}
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
updateProgress()
|
||||
fetchEpisodeDetails()
|
||||
}
|
||||
.onChange(of: progress) { _ in
|
||||
updateProgress()
|
||||
}
|
||||
.onTapGesture {
|
||||
#elseif os(tvOS)
|
||||
Button{
|
||||
let imageUrl = episodeImageUrl.isEmpty ? defaultBannerImage : episodeImageUrl
|
||||
onTap(imageUrl)
|
||||
} label: {
|
||||
HStack {
|
||||
ZStack {
|
||||
KFImage(URL(string: episodeImageUrl.isEmpty ? defaultBannerImage : episodeImageUrl))
|
||||
.resizable()
|
||||
.aspectRatio(16/9, contentMode: .fill)
|
||||
.frame(width: 100, height: 56)
|
||||
.cornerRadius(8)
|
||||
|
||||
if isLoading {
|
||||
ProgressView()
|
||||
.progressViewStyle(CircularProgressViewStyle())
|
||||
}
|
||||
}
|
||||
|
||||
VStack(alignment: .leading) {
|
||||
Text("Episode \(episodeID + 1)")
|
||||
.font(.system(size: 15))
|
||||
if !episodeTitle.isEmpty {
|
||||
Text(episodeTitle)
|
||||
.font(.system(size: 13))
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer()
|
||||
|
||||
CircularProgressBar(progress: currentProgress)
|
||||
.frame(width: 40, height: 40)
|
||||
}
|
||||
.contentShape(Rectangle())
|
||||
.contextMenu {
|
||||
if progress <= 0.9 {
|
||||
Button(action: markAsWatched) {
|
||||
Label("Mark as Watched", systemImage: "checkmark.circle")
|
||||
}
|
||||
}
|
||||
|
||||
if progress != 0 {
|
||||
Button(action: resetProgress) {
|
||||
Label("Reset Progress", systemImage: "arrow.counterclockwise")
|
||||
}
|
||||
}
|
||||
|
||||
if episodeIndex > 0 {
|
||||
Button(action: onMarkAllPrevious) {
|
||||
Label("Mark All Previous Watched", systemImage: "checkmark.circle.fill")
|
||||
}
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
updateProgress()
|
||||
fetchEpisodeDetails()
|
||||
}
|
||||
.onChange(of: progress) { oldValue, _ in
|
||||
updateProgress()
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
private func markAsWatched() {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,9 @@
|
|||
|
||||
import SwiftUI
|
||||
import Kingfisher
|
||||
#if !os(tvOS)
|
||||
import SafariServices
|
||||
#endif
|
||||
|
||||
struct MediaItem: Identifiable {
|
||||
let id = UUID()
|
||||
|
|
@ -92,8 +94,10 @@ struct MediaInfoView: View {
|
|||
.font(.system(size: 17))
|
||||
.fontWeight(.bold)
|
||||
.onLongPressGesture {
|
||||
#if !os(tvOS)
|
||||
UIPasteboard.general.string = title
|
||||
DropManager.shared.showDrop(title: "Copied to Clipboard", subtitle: "", duration: 1.0, icon: UIImage(systemName: "doc.on.clipboard.fill"))
|
||||
#endif
|
||||
}
|
||||
|
||||
if !aliases.isEmpty && aliases != title && aliases != "N/A" && aliases != "No Data" {
|
||||
|
|
@ -122,7 +126,9 @@ struct MediaInfoView: View {
|
|||
|
||||
HStack(alignment: .center, spacing: 12) {
|
||||
Button(action: {
|
||||
#if !os(tvOS)
|
||||
openSafariViewController(with: href)
|
||||
#endif
|
||||
}) {
|
||||
HStack(spacing: 4) {
|
||||
Text(module.metadata.sourceName)
|
||||
|
|
@ -164,9 +170,11 @@ struct MediaInfoView: View {
|
|||
|
||||
if let id = itemID ?? customAniListID {
|
||||
Button(action: {
|
||||
#if !os(tvOS)
|
||||
if let url = URL(string: "https://anilist.co/anime/\(id)") {
|
||||
openSafariViewController(with: url.absoluteString)
|
||||
}
|
||||
#endif
|
||||
}) {
|
||||
Label("Open in AniList", systemImage: "link")
|
||||
}
|
||||
|
|
@ -418,7 +426,9 @@ struct MediaInfoView: View {
|
|||
}
|
||||
}
|
||||
.padding()
|
||||
#if !os(tvOS)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
#endif
|
||||
.navigationBarTitle("")
|
||||
.navigationViewStyle(StackNavigationViewStyle())
|
||||
}
|
||||
|
|
@ -452,10 +462,11 @@ struct MediaInfoView: View {
|
|||
}
|
||||
selectedRange = 0..<episodeChunkSize
|
||||
}
|
||||
#if !os(tvOS)
|
||||
.onReceive(NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)) { _ in
|
||||
orientationChanged.toggle()
|
||||
}
|
||||
|
||||
#endif
|
||||
if showStreamLoadingView {
|
||||
VStack(spacing: 16) {
|
||||
Text("Loading \(currentStreamTitle)…")
|
||||
|
|
@ -774,8 +785,9 @@ struct MediaInfoView: View {
|
|||
AnalyticsManager.shared.sendEvent(event: "error", additionalData: ["error": error, "message": "Failed to fetch stream"])
|
||||
}
|
||||
DropManager.shared.showDrop(title: "Stream not Found", subtitle: "", duration: 0.5, icon: UIImage(systemName: "xmark"))
|
||||
|
||||
#if !os(tvOS)
|
||||
UINotificationFeedbackGenerator().notificationOccurred(.error)
|
||||
#endif
|
||||
self.isLoading = false
|
||||
}
|
||||
|
||||
|
|
@ -847,7 +859,7 @@ struct MediaInfoView: View {
|
|||
self.isFetchingEpisode = false
|
||||
self.showStreamLoadingView = false
|
||||
DispatchQueue.main.async {
|
||||
let externalPlayer = UserDefaults.standard.string(forKey: "externalPlayer") ?? "Sora"
|
||||
let externalPlayer = UserDefaults.standard.string(forKey: "externalPlayer") ?? "Default"
|
||||
var scheme: String?
|
||||
|
||||
switch externalPlayer {
|
||||
|
|
@ -929,6 +941,7 @@ struct MediaInfoView: View {
|
|||
DropManager.shared.showDrop(title: "Fetching Next Episode", subtitle: "", duration: 0.5, icon: UIImage(systemName: "arrow.triangle.2.circlepath"))
|
||||
}
|
||||
|
||||
#if !os(tvOS)
|
||||
private func openSafariViewController(with urlString: String) {
|
||||
guard let url = URL(string: urlString), UIApplication.shared.canOpenURL(url) else {
|
||||
Logger.shared.log("Unable to open the webpage", type: "Error")
|
||||
|
|
@ -940,6 +953,7 @@ struct MediaInfoView: View {
|
|||
rootVC.present(safariViewController, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
private func cleanTitle(_ title: String?) -> String {
|
||||
guard let title = title else { return "Unknown" }
|
||||
|
|
|
|||
|
|
@ -30,7 +30,9 @@ struct SearchView: View {
|
|||
@State private var isSearching = false
|
||||
@State private var searchText = ""
|
||||
@State private var hasNoResults = false
|
||||
#if !os(tvOS)
|
||||
@State private var isLandscape: Bool = UIDevice.current.orientation.isLandscape
|
||||
#endif
|
||||
@State private var isModuleSelectorPresented = false
|
||||
|
||||
private var selectedModule: ScrapingModule? {
|
||||
|
|
@ -47,12 +49,16 @@ struct SearchView: View {
|
|||
]
|
||||
|
||||
private var columnsCount: Int {
|
||||
#if !os(tvOS)
|
||||
if UIDevice.current.userInterfaceIdiom == .pad {
|
||||
let isLandscape = UIScreen.main.bounds.width > UIScreen.main.bounds.height
|
||||
return isLandscape ? mediaColumnsLandscape : mediaColumnsPortrait
|
||||
} else {
|
||||
return verticalSizeClass == .compact ? mediaColumnsLandscape : mediaColumnsPortrait
|
||||
}
|
||||
#elseif os(tvOS)
|
||||
return mediaColumnsLandscape
|
||||
#endif
|
||||
}
|
||||
|
||||
private var cellWidth: CGFloat {
|
||||
|
|
@ -61,11 +67,16 @@ struct SearchView: View {
|
|||
.first
|
||||
let safeAreaInsets = keyWindow?.safeAreaInsets ?? .zero
|
||||
let safeWidth = UIScreen.main.bounds.width - safeAreaInsets.left - safeAreaInsets.right
|
||||
#if !os(tvOS)
|
||||
let totalSpacing: CGFloat = 16 * CGFloat(columnsCount + 1)
|
||||
#elseif os(tvOS)
|
||||
let totalSpacing: CGFloat = 32 * CGFloat(columnsCount + 1)
|
||||
#endif
|
||||
let availableWidth = safeWidth - totalSpacing
|
||||
return availableWidth / CGFloat(columnsCount)
|
||||
}
|
||||
|
||||
|
||||
#if !os(tvOS)
|
||||
var body: some View {
|
||||
NavigationView {
|
||||
ScrollView {
|
||||
|
|
@ -222,6 +233,159 @@ struct SearchView: View {
|
|||
}
|
||||
}
|
||||
|
||||
#elseif os(tvOS)
|
||||
var body: some View {
|
||||
NavigationView {
|
||||
ScrollView {
|
||||
let columnsCount = determineColumns()
|
||||
VStack(spacing: 0) {
|
||||
HStack {
|
||||
SearchBar(text: $searchText, onSearchButtonClicked: performSearch)
|
||||
.padding(.leading)
|
||||
.padding(.trailing, searchText.isEmpty ? 16 : 0)
|
||||
.disabled(selectedModule == nil)
|
||||
.padding(.top)
|
||||
|
||||
if !searchText.isEmpty {
|
||||
Button("Cancel") {
|
||||
searchText = ""
|
||||
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
|
||||
}
|
||||
.padding(.trailing)
|
||||
.padding(.top)
|
||||
}
|
||||
}
|
||||
|
||||
if selectedModule == nil {
|
||||
VStack(spacing: 8) {
|
||||
Image(systemName: "questionmark.app")
|
||||
.font(.largeTitle)
|
||||
.foregroundColor(.secondary)
|
||||
Text("No Module Selected")
|
||||
.font(.headline)
|
||||
Text("Please select a module from settings")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
.padding()
|
||||
.frame(maxWidth: .infinity)
|
||||
.shadow(color: Color.black.opacity(0.1), radius: 2, y: 1)
|
||||
}
|
||||
|
||||
if !searchText.isEmpty {
|
||||
if isSearching {
|
||||
LazyVGrid(columns: Array(repeating: GridItem(.flexible(), spacing: 16), count: columnsCount), spacing: 16) {
|
||||
ForEach(0..<columnsCount*4, id: \.self) { _ in
|
||||
SearchSkeletonCell(cellWidth: cellWidth)
|
||||
}
|
||||
}
|
||||
.padding(.top)
|
||||
.padding()
|
||||
} else if hasNoResults {
|
||||
VStack(spacing: 8) {
|
||||
Image(systemName: "magnifyingglass")
|
||||
.font(.largeTitle)
|
||||
.foregroundColor(.secondary)
|
||||
Text("No Results Found")
|
||||
.font(.headline)
|
||||
Text("Try different keywords")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
.padding()
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding(.top)
|
||||
} else {
|
||||
LazyVGrid(columns: Array(repeating: GridItem(.flexible(), spacing: 16), count: columnsCount), spacing: 16) {
|
||||
ForEach(searchItems) { item in
|
||||
NavigationLink(destination: MediaInfoView(title: item.title, imageUrl: item.imageUrl, href: item.href, module: selectedModule!)) {
|
||||
VStack {
|
||||
KFImage(URL(string: item.imageUrl))
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fill)
|
||||
.frame(height: cellWidth * 3 / 2)
|
||||
.frame(maxWidth: cellWidth - 60)
|
||||
.cornerRadius(10)
|
||||
.clipped()
|
||||
Text(item.title)
|
||||
.font(.subheadline)
|
||||
.foregroundColor(Color.primary)
|
||||
.padding([.leading, .bottom], 8)
|
||||
.lineLimit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding(.top)
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationTitle("Search")
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .principal) {
|
||||
|
||||
Menu {
|
||||
ForEach(getModuleLanguageGroups(), id: \.self) { language in
|
||||
Menu {
|
||||
ForEach(getModulesForLanguage(language), id: \.id) { module in
|
||||
Button {
|
||||
selectedModuleId = module.id.uuidString
|
||||
} label: {
|
||||
HStack {
|
||||
KFImage(URL(string: module.metadata.iconUrl))
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 20, height: 20)
|
||||
.cornerRadius(4)
|
||||
Text(module.metadata.sourceName)
|
||||
if module.id.uuidString == selectedModuleId {
|
||||
Image(systemName: "checkmark")
|
||||
.foregroundColor(.accentColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
label: {
|
||||
Text(language)
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
HStack(spacing: 4) {
|
||||
if let selectedModule = selectedModule {
|
||||
Text(selectedModule.metadata.sourceName)
|
||||
.font(.headline)
|
||||
.foregroundColor(.secondary)
|
||||
} else {
|
||||
Text("Select Module")
|
||||
.font(.headline)
|
||||
.foregroundColor(.accentColor)
|
||||
}
|
||||
Image(systemName: "chevron.down")
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationViewStyle(StackNavigationViewStyle())
|
||||
.onChange(of: selectedModuleId) { oldValue, _ in
|
||||
if !searchText.isEmpty {
|
||||
performSearch()
|
||||
}
|
||||
}
|
||||
.onChange(of: searchText) { oldValue, newValue in
|
||||
if newValue.isEmpty {
|
||||
searchItems = []
|
||||
hasNoResults = false
|
||||
isSearching = false
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
private func performSearch() {
|
||||
Logger.shared.log("Searching for: \(searchText)", type: "General")
|
||||
guard !searchText.isEmpty, let module = selectedModule else {
|
||||
|
|
@ -260,19 +424,25 @@ struct SearchView: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if !os(tvOS)
|
||||
private func updateOrientation() {
|
||||
DispatchQueue.main.async {
|
||||
isLandscape = UIDevice.current.orientation.isLandscape
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
private func determineColumns() -> Int {
|
||||
#if !os(tvOS)
|
||||
if UIDevice.current.userInterfaceIdiom == .pad {
|
||||
return isLandscape ? mediaColumnsLandscape : mediaColumnsPortrait
|
||||
} else {
|
||||
return verticalSizeClass == .compact ? mediaColumnsLandscape : mediaColumnsPortrait
|
||||
}
|
||||
#elseif os(tvOS)
|
||||
return mediaColumnsLandscape
|
||||
#endif
|
||||
}
|
||||
|
||||
private func cleanLanguageName(_ language: String?) -> String {
|
||||
|
|
@ -321,9 +491,11 @@ struct SearchBar: View {
|
|||
TextField("Search...", text: $text, onCommit: onSearchButtonClicked)
|
||||
.padding(7)
|
||||
.padding(.horizontal, 25)
|
||||
#if !os(tvOS)
|
||||
.background(Color(.systemGray6))
|
||||
#endif
|
||||
.cornerRadius(8)
|
||||
.onChange(of: text){newValue in
|
||||
.onChange(of: text){ newValue in
|
||||
debounceTimer?.invalidate()
|
||||
// Start a new timer to wait before performing the action
|
||||
debounceTimer = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false) { _ in
|
||||
|
|
|
|||
|
|
@ -83,7 +83,12 @@ struct SettingsViewData: View {
|
|||
|
||||
func removeAllFilesInDocuments() {
|
||||
let fileManager = FileManager.default
|
||||
if let documentsURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first {
|
||||
#if !os(tvOS)
|
||||
let directory = FileManager.SearchPathDirectory.documentDirectory
|
||||
#elseif os(tvOS)
|
||||
let directory = FileManager.SearchPathDirectory.cachesDirectory
|
||||
#endif
|
||||
if let documentsURL = fileManager.urls(for: directory, in: .userDomainMask).first {
|
||||
do {
|
||||
let fileURLs = try fileManager.contentsOfDirectory(at: documentsURL, includingPropertiesForKeys: nil)
|
||||
for fileURL in fileURLs {
|
||||
|
|
|
|||
|
|
@ -25,7 +25,9 @@ struct SettingsViewGeneral: View {
|
|||
var body: some View {
|
||||
Form {
|
||||
Section(header: Text("Interface")) {
|
||||
#if !os(tvOS)
|
||||
ColorPicker("Accent Color", selection: $settings.accentColor)
|
||||
#endif
|
||||
HStack {
|
||||
Text("Appearance")
|
||||
Picker("Appearance", selection: $settings.selectedAppearance) {
|
||||
|
|
|
|||
|
|
@ -19,7 +19,9 @@ struct SettingsViewLogger: View {
|
|||
.foregroundColor(.secondary)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.padding()
|
||||
#if !os(tvOS)
|
||||
.textSelection(.enabled)
|
||||
#endif
|
||||
}
|
||||
.navigationTitle("Logs")
|
||||
.onAppear {
|
||||
|
|
@ -30,12 +32,14 @@ struct SettingsViewLogger: View {
|
|||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
HStack {
|
||||
Menu {
|
||||
#if !os(tvOS)
|
||||
Button(action: {
|
||||
UIPasteboard.general.string = logs
|
||||
DropManager.shared.showDrop(title: "Copied to Clipboard", subtitle: "", duration: 1.0, icon: UIImage(systemName: "doc.on.clipboard.fill"))
|
||||
}) {
|
||||
Label("Copy to Clipboard", systemImage: "doc.on.doc")
|
||||
}
|
||||
#endif
|
||||
Button(role: .destructive, action: {
|
||||
Logger.shared.clearLogs()
|
||||
logs = Logger.shared.getLogs()
|
||||
|
|
|
|||
|
|
@ -88,12 +88,14 @@ struct SettingsViewModule: View {
|
|||
selectedModuleId = module.id.uuidString
|
||||
}
|
||||
.contextMenu {
|
||||
#if !os(tvOS)
|
||||
Button(action: {
|
||||
UIPasteboard.general.string = module.metadataUrl
|
||||
DropManager.shared.showDrop(title: "Copied to Clipboard", subtitle: "", duration: 1.0, icon: UIImage(systemName: "doc.on.clipboard.fill"))
|
||||
}) {
|
||||
Label("Copy URL", systemImage: "doc.on.doc")
|
||||
}
|
||||
#endif
|
||||
Button(role: .destructive) {
|
||||
if selectedModuleId != module.id.uuidString {
|
||||
moduleManager.deleteModule(module)
|
||||
|
|
@ -104,6 +106,7 @@ struct SettingsViewModule: View {
|
|||
}
|
||||
.disabled(selectedModuleId == module.id.uuidString)
|
||||
}
|
||||
#if !os(tvOS)
|
||||
.swipeActions {
|
||||
if selectedModuleId != module.id.uuidString {
|
||||
Button(role: .destructive) {
|
||||
|
|
@ -114,6 +117,7 @@ struct SettingsViewModule: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -180,7 +184,11 @@ struct SettingsViewModule: View {
|
|||
}
|
||||
|
||||
func showAddModuleAlert() {
|
||||
#if !os(tvOS)
|
||||
let pasteboardString = UIPasteboard.general.string ?? ""
|
||||
#elseif os(tvOS)
|
||||
let pasteboardString = ""
|
||||
#endif
|
||||
|
||||
if !pasteboardString.isEmpty {
|
||||
let clipboardAlert = UIAlertController(
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ struct SettingsViewPlayer: View {
|
|||
HStack {
|
||||
Text("Hold Speed:")
|
||||
Spacer()
|
||||
#if !os(tvOS)
|
||||
Stepper(
|
||||
value: $holdSpeedPlayer,
|
||||
in: 0.25...2.5,
|
||||
|
|
@ -59,9 +60,10 @@ struct SettingsViewPlayer: View {
|
|||
) {
|
||||
Text(String(format: "%.2f", holdSpeedPlayer))
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#if !os(tvOS)
|
||||
Section(header: Text("Progress bar Marker Color")) {
|
||||
ColorPicker("Segments Color", selection: Binding(
|
||||
get: {
|
||||
|
|
@ -82,18 +84,23 @@ struct SettingsViewPlayer: View {
|
|||
}
|
||||
))
|
||||
}
|
||||
#endif
|
||||
|
||||
Section(header: Text("Skip Settings"), footer : Text("Double tapping the screen on it's sides will skip with the short tap setting.")) {
|
||||
HStack {
|
||||
Text("Tap Skip:")
|
||||
Spacer()
|
||||
#if !os(tvOS)
|
||||
Stepper("\(Int(skipIncrement))s", value: $skipIncrement, in: 5...300, step: 5)
|
||||
#endif
|
||||
}
|
||||
|
||||
HStack {
|
||||
Text("Long press Skip:")
|
||||
Spacer()
|
||||
#if !os(tvOS)
|
||||
Stepper("\(Int(skipIncrementHold))s", value: $skipIncrementHold, in: 5...300, step: 5)
|
||||
#endif
|
||||
}
|
||||
|
||||
Toggle("Double Tap to Seek", isOn: $doubleTapSeekEnabled)
|
||||
|
|
@ -159,32 +166,38 @@ struct SubtitleSettingsSection: View {
|
|||
|
||||
Toggle("Background Enabled", isOn: $backgroundEnabled)
|
||||
.tint(.accentColor)
|
||||
#if !os(tvOS)
|
||||
.onChange(of: backgroundEnabled) { newValue in
|
||||
SubtitleSettingsManager.shared.update { settings in
|
||||
settings.backgroundEnabled = newValue
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
HStack {
|
||||
Text("Font Size:")
|
||||
Spacer()
|
||||
#if !os(tvOS)
|
||||
Stepper("\(Int(fontSize))", value: $fontSize, in: 12...36, step: 1)
|
||||
.onChange(of: fontSize) { newValue in
|
||||
SubtitleSettingsManager.shared.update { settings in
|
||||
settings.fontSize = newValue
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
HStack {
|
||||
Text("Bottom Padding:")
|
||||
Spacer()
|
||||
#if !os(tvOS)
|
||||
Stepper("\(Int(bottomPadding))", value: $bottomPadding, in: 0...50, step: 1)
|
||||
.onChange(of: bottomPadding) { newValue in
|
||||
SubtitleSettingsManager.shared.update { settings in
|
||||
settings.bottomPadding = newValue
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -474,7 +474,7 @@
|
|||
attributes = {
|
||||
BuildIndependentTargetsInParallel = 1;
|
||||
LastSwiftUpdateCheck = 1320;
|
||||
LastUpgradeCheck = 1320;
|
||||
LastUpgradeCheck = 1630;
|
||||
TargetAttributes = {
|
||||
133D7C692D2BE2500075467E = {
|
||||
CreatedOnToolsVersion = 13.2.1;
|
||||
|
|
@ -487,6 +487,7 @@
|
|||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = 133D7C612D2BE2500075467E;
|
||||
packageReferences = (
|
||||
|
|
@ -615,8 +616,10 @@
|
|||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = 399LMK6Q2Y;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
|
|
@ -677,8 +680,10 @@
|
|||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = 399LMK6Q2Y;
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
|
|
@ -709,7 +714,7 @@
|
|||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"Sora/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = 399LMK6Q2Y;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"ENABLE_HARDENED_RUNTIME[sdk=macosx*]" = NO;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
|
|
@ -734,10 +739,11 @@
|
|||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
"PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "";
|
||||
SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator";
|
||||
SUPPORTS_MACCATALYST = NO;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
TARGETED_DEVICE_FAMILY = "1,2,3";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
|
|
@ -752,7 +758,7 @@
|
|||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"Sora/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = 399LMK6Q2Y;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"ENABLE_HARDENED_RUNTIME[sdk=macosx*]" = NO;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
|
|
@ -777,10 +783,11 @@
|
|||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
"PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "";
|
||||
SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator";
|
||||
SUPPORTS_MACCATALYST = NO;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
TARGETED_DEVICE_FAMILY = "1,2,3";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue