mirror of
https://github.com/cranci1/Sora.git
synced 2026-05-07 02:30:10 +00:00
Merge branch 'dev' into dev
This commit is contained in:
commit
63c7b8229b
9 changed files with 164 additions and 26 deletions
|
|
@ -2,6 +2,10 @@
|
|||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.developer.icloud-container-identifiers</key>
|
||||
<array/>
|
||||
<key>com.apple.developer.ubiquity-kvstore-identifier</key>
|
||||
<string>$(TeamIdentifierPrefix)$(CFBundleIdentifier)</string>
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<true/>
|
||||
<key>com.apple.security.network.client</key>
|
||||
|
|
|
|||
|
|
@ -13,6 +13,10 @@ struct SoraApp: App {
|
|||
@StateObject private var moduleManager = ModuleManager()
|
||||
@StateObject private var librarykManager = LibraryManager()
|
||||
|
||||
init() {
|
||||
_ = iCloudSyncManager.shared
|
||||
}
|
||||
|
||||
var body: some Scene {
|
||||
WindowGroup {
|
||||
ContentView()
|
||||
|
|
|
|||
|
|
@ -11,7 +11,13 @@ class ContinueWatchingManager {
|
|||
static let shared = ContinueWatchingManager()
|
||||
private let storageKey = "continueWatchingItems"
|
||||
|
||||
private init() {}
|
||||
private init() {
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(handleiCloudSync), name: .iCloudSyncDidComplete, object: nil)
|
||||
}
|
||||
|
||||
@objc private func handleiCloudSync() {
|
||||
NotificationCenter.default.post(name: .ContinueWatchingDidUpdate, object: nil)
|
||||
}
|
||||
|
||||
func save(item: ContinueWatchingItem) {
|
||||
if item.progress >= 0.9 {
|
||||
|
|
|
|||
|
|
@ -9,10 +9,6 @@ import Foundation
|
|||
import FFmpegSupport
|
||||
import UIKit
|
||||
|
||||
extension Notification.Name {
|
||||
static let DownloadManagerStatusUpdate = Notification.Name("DownloadManagerStatusUpdate")
|
||||
}
|
||||
|
||||
class DownloadManager {
|
||||
static let shared = DownloadManager()
|
||||
|
||||
|
|
|
|||
14
Sora/Utils/Extensions/Notification+Name.swift
Normal file
14
Sora/Utils/Extensions/Notification+Name.swift
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
//
|
||||
// Notification+Name.swift
|
||||
// Sulfur
|
||||
//
|
||||
// Created by Francesco on 17/04/25.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension Notification.Name {
|
||||
static let iCloudSyncDidComplete = Notification.Name("iCloudSyncDidComplete")
|
||||
static let ContinueWatchingDidUpdate = Notification.Name("ContinueWatchingDidUpdate")
|
||||
static let DownloadManagerStatusUpdate = Notification.Name("DownloadManagerStatusUpdate")
|
||||
}
|
||||
|
|
@ -190,7 +190,6 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele
|
|||
view.backgroundColor = .black
|
||||
|
||||
setupHoldGesture()
|
||||
setInitialPlayerRate()
|
||||
loadSubtitleSettings()
|
||||
setupPlayerViewController()
|
||||
setupControls()
|
||||
|
|
@ -226,7 +225,6 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele
|
|||
|
||||
volumeViewModel.value = Double(audioSession.outputVolume)
|
||||
|
||||
|
||||
volumeObserver = audioSession.observe(\.outputVolume, options: [.new]) { [weak self] session, change in
|
||||
guard let newVol = change.newValue else { return }
|
||||
DispatchQueue.main.async {
|
||||
|
|
@ -240,8 +238,6 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele
|
|||
playerViewController.allowsVideoFrameAnalysis = false
|
||||
}
|
||||
|
||||
player.play()
|
||||
|
||||
if let url = subtitlesURL, !url.isEmpty {
|
||||
subtitlesLoader.load(from: url)
|
||||
}
|
||||
|
|
@ -293,36 +289,36 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele
|
|||
}
|
||||
}
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
player?.play()
|
||||
setInitialPlayerRate()
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
NotificationCenter.default.addObserver(self,
|
||||
selector: #selector(playerItemDidChange),
|
||||
name: .AVPlayerItemNewAccessLogEntry,
|
||||
object: nil)
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(playerItemDidChange), name: .AVPlayerItemNewAccessLogEntry, object: nil)
|
||||
skip85Button?.isHidden = !isSkip85Visible
|
||||
}
|
||||
|
||||
override func viewWillDisappear(_ animated: Bool) {
|
||||
super.viewWillDisappear(animated)
|
||||
|
||||
loadedTimeRangesObservation?.invalidate()
|
||||
loadedTimeRangesObservation = nil
|
||||
if let playbackSpeed = player?.rate {
|
||||
UserDefaults.standard.set(playbackSpeed, forKey: "lastPlaybackSpeed")
|
||||
}
|
||||
|
||||
if let token = timeObserverToken {
|
||||
player.removeTimeObserver(token)
|
||||
timeObserverToken = nil
|
||||
}
|
||||
|
||||
loadedTimeRangesObservation?.invalidate()
|
||||
loadedTimeRangesObservation = nil
|
||||
|
||||
updateTimer?.invalidate()
|
||||
inactivityTimer?.invalidate()
|
||||
|
||||
player.pause()
|
||||
|
||||
if let playbackSpeed = player?.rate {
|
||||
UserDefaults.standard.set(playbackSpeed, forKey: "lastPlaybackSpeed")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
|
||||
|
|
@ -1098,7 +1094,7 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele
|
|||
let finalSkip = holdValue > 0 ? holdValue : 30
|
||||
currentTimeVal = max(currentTimeVal - finalSkip, 0)
|
||||
player.seek(to: CMTime(seconds: currentTimeVal, preferredTimescale: 600)) { [weak self] finished in
|
||||
guard let self = self else { return }
|
||||
guard self != nil else { return }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1109,7 +1105,7 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele
|
|||
let finalSkip = holdValue > 0 ? holdValue : 30
|
||||
currentTimeVal = min(currentTimeVal + finalSkip, duration)
|
||||
player.seek(to: CMTime(seconds: currentTimeVal, preferredTimescale: 600)) { [weak self] finished in
|
||||
guard let self = self else { return }
|
||||
guard self != nil else { return }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1119,7 +1115,7 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele
|
|||
let finalSkip = skipValue > 0 ? skipValue : 10
|
||||
currentTimeVal = max(currentTimeVal - finalSkip, 0)
|
||||
player.seek(to: CMTime(seconds: currentTimeVal, preferredTimescale: 600)) { [weak self] finished in
|
||||
guard let self = self else { return }
|
||||
guard self != nil else { return }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1128,7 +1124,7 @@ class CustomMediaPlayerViewController: UIViewController, UIGestureRecognizerDele
|
|||
let finalSkip = skipValue > 0 ? skipValue : 10
|
||||
currentTimeVal = min(currentTimeVal + finalSkip, duration)
|
||||
player.seek(to: CMTime(seconds: currentTimeVal, preferredTimescale: 600)) { [weak self] finished in
|
||||
guard let self = self else { return } }
|
||||
guard self != nil else { return } }
|
||||
}
|
||||
|
||||
@objc func handleDoubleTap(_ gesture: UITapGestureRecognizer) {
|
||||
|
|
|
|||
94
Sora/Utils/iCloudSyncManager/iCloudSyncManager.swift
Normal file
94
Sora/Utils/iCloudSyncManager/iCloudSyncManager.swift
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
//
|
||||
// iCloudSyncManager.swift
|
||||
// Sulfur
|
||||
//
|
||||
// Created by Francesco on 17/04/25.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class iCloudSyncManager {
|
||||
static let shared = iCloudSyncManager()
|
||||
|
||||
private let defaultsToSync: [String] = [
|
||||
"externalPlayer",
|
||||
"alwaysLandscape",
|
||||
"rememberPlaySpeed",
|
||||
"holdSpeedPlayer",
|
||||
"skipIncrement",
|
||||
"skipIncrementHold",
|
||||
"holdForPauseEnabled",
|
||||
"skip85Visible",
|
||||
"doubleTapSeekEnabled",
|
||||
"selectedModuleId",
|
||||
"mediaColumnsPortrait",
|
||||
"mediaColumnsLandscape",
|
||||
"sendPushUpdates",
|
||||
"sendTraktUpdates",
|
||||
"bookmarkedItems",
|
||||
"continueWatchingItems"
|
||||
]
|
||||
|
||||
private init() {
|
||||
setupSync()
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(willEnterBackground), name: UIApplication.willResignActiveNotification, object: nil)
|
||||
}
|
||||
|
||||
private func setupSync() {
|
||||
NSUbiquitousKeyValueStore.default.synchronize()
|
||||
|
||||
syncFromiCloud()
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(iCloudDidChangeExternally), name: NSUbiquitousKeyValueStore.didChangeExternallyNotification, object: NSUbiquitousKeyValueStore.default)
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(userDefaultsDidChange), name: UserDefaults.didChangeNotification, object: nil)
|
||||
}
|
||||
|
||||
@objc private func willEnterBackground() {
|
||||
syncToiCloud()
|
||||
}
|
||||
|
||||
private func syncFromiCloud() {
|
||||
let iCloud = NSUbiquitousKeyValueStore.default
|
||||
let defaults = UserDefaults.standard
|
||||
|
||||
for key in defaultsToSync {
|
||||
if let value = iCloud.object(forKey: key) {
|
||||
defaults.set(value, forKey: key)
|
||||
}
|
||||
}
|
||||
|
||||
defaults.synchronize()
|
||||
NotificationCenter.default.post(name: .iCloudSyncDidComplete, object: nil)
|
||||
}
|
||||
|
||||
private func syncToiCloud() {
|
||||
let iCloud = NSUbiquitousKeyValueStore.default
|
||||
let defaults = UserDefaults.standard
|
||||
|
||||
for key in defaultsToSync {
|
||||
if let value = defaults.object(forKey: key) {
|
||||
iCloud.set(value, forKey: key)
|
||||
}
|
||||
}
|
||||
|
||||
iCloud.synchronize()
|
||||
}
|
||||
|
||||
@objc private func iCloudDidChangeExternally(_ notification: Notification) {
|
||||
guard let userInfo = notification.userInfo,
|
||||
let reason = userInfo[NSUbiquitousKeyValueStoreChangeReasonKey] as? Int else {
|
||||
return
|
||||
}
|
||||
|
||||
if reason == NSUbiquitousKeyValueStoreServerChange ||
|
||||
reason == NSUbiquitousKeyValueStoreInitialSyncChange {
|
||||
syncFromiCloud()
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func userDefaultsDidChange(_ notification: Notification) {
|
||||
syncToiCloud()
|
||||
}
|
||||
}
|
||||
|
|
@ -33,6 +33,14 @@ class LibraryManager: ObservableObject {
|
|||
|
||||
init() {
|
||||
loadBookmarks()
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(handleiCloudSync), name: .iCloudSyncDidComplete, object: nil)
|
||||
}
|
||||
|
||||
@objc private func handleiCloudSync() {
|
||||
DispatchQueue.main.async {
|
||||
self.loadBookmarks()
|
||||
}
|
||||
}
|
||||
|
||||
func removeBookmark(item: LibraryItem) {
|
||||
|
|
|
|||
|
|
@ -35,6 +35,8 @@
|
|||
133F55BB2D33B55100E08EEA /* LibraryManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 133F55BA2D33B55100E08EEA /* LibraryManager.swift */; };
|
||||
1359ED142D76F49900C13034 /* finTopView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1359ED132D76F49900C13034 /* finTopView.swift */; };
|
||||
135CCBE22D4D1138008B9C0E /* SettingsViewPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 135CCBE12D4D1138008B9C0E /* SettingsViewPlayer.swift */; };
|
||||
136BBE7E2DB102D600906B5E /* iCloudSyncManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 136BBE7D2DB102D600906B5E /* iCloudSyncManager.swift */; };
|
||||
136BBE802DB1038000906B5E /* Notification+Name.swift in Sources */ = {isa = PBXBuildFile; fileRef = 136BBE7F2DB1038000906B5E /* Notification+Name.swift */; };
|
||||
138AA1B82D2D66FD0021F9DF /* EpisodeCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 138AA1B62D2D66FD0021F9DF /* EpisodeCell.swift */; };
|
||||
138AA1B92D2D66FD0021F9DF /* CircularProgressBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 138AA1B72D2D66FD0021F9DF /* CircularProgressBar.swift */; };
|
||||
139935662D468C450065CEFF /* ModuleManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 139935652D468C450065CEFF /* ModuleManager.swift */; };
|
||||
|
|
@ -96,6 +98,8 @@
|
|||
133F55BA2D33B55100E08EEA /* LibraryManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryManager.swift; sourceTree = "<group>"; };
|
||||
1359ED132D76F49900C13034 /* finTopView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = finTopView.swift; sourceTree = "<group>"; };
|
||||
135CCBE12D4D1138008B9C0E /* SettingsViewPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewPlayer.swift; sourceTree = "<group>"; };
|
||||
136BBE7D2DB102D600906B5E /* iCloudSyncManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iCloudSyncManager.swift; sourceTree = "<group>"; };
|
||||
136BBE7F2DB1038000906B5E /* Notification+Name.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Notification+Name.swift"; sourceTree = "<group>"; };
|
||||
138AA1B62D2D66FD0021F9DF /* EpisodeCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EpisodeCell.swift; sourceTree = "<group>"; };
|
||||
138AA1B72D2D66FD0021F9DF /* CircularProgressBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CircularProgressBar.swift; sourceTree = "<group>"; };
|
||||
139935652D468C450065CEFF /* ModuleManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModuleManager.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -257,6 +261,7 @@
|
|||
133D7C852D2BE2640075467E /* Utils */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
136BBE7C2DB102BE00906B5E /* iCloudSyncManager */,
|
||||
13DB7CEA2D7DED50004371D3 /* DownloadManager */,
|
||||
13C0E5E82D5F85DD00E7F619 /* ContinueWatching */,
|
||||
13103E8C2D58E037000F0673 /* SkeletonCells */,
|
||||
|
|
@ -275,6 +280,7 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
73D164D42D8B5B340011A360 /* JavaScriptCore+Extensions.swift */,
|
||||
136BBE7F2DB1038000906B5E /* Notification+Name.swift */,
|
||||
1327FBA82D758DEA00FC6689 /* UIDevice+Model.swift */,
|
||||
133D7C872D2BE2640075467E /* URLSession.swift */,
|
||||
1359ED132D76F49900C13034 /* finTopView.swift */,
|
||||
|
|
@ -315,6 +321,14 @@
|
|||
path = LibraryView;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
136BBE7C2DB102BE00906B5E /* iCloudSyncManager */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
136BBE7D2DB102D600906B5E /* iCloudSyncManager.swift */,
|
||||
);
|
||||
path = iCloudSyncManager;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
1384DCDF2D89BE870094797A /* Helpers */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
|
@ -539,7 +553,9 @@
|
|||
133D7C902D2BE2640075467E /* SettingsView.swift in Sources */,
|
||||
132AF1252D9995F900A0140B /* JSController-Search.swift in Sources */,
|
||||
13CBEFDA2D5F7D1200D011EE /* String.swift in Sources */,
|
||||
136BBE7E2DB102D600906B5E /* iCloudSyncManager.swift in Sources */,
|
||||
1E26E9E72DA9577900B9DC02 /* VolumeSlider.swift in Sources */,
|
||||
136BBE802DB1038000906B5E /* Notification+Name.swift in Sources */,
|
||||
13DB46902D900A38008CBC03 /* URL.swift in Sources */,
|
||||
130C6BFA2D53AB1F00DC1432 /* SettingsViewData.swift in Sources */,
|
||||
1E9FF1D32D403E49008AC100 /* SettingsViewLoggerFilter.swift in Sources */,
|
||||
|
|
|
|||
Loading…
Reference in a new issue