From 794c4993d4f575f4eb804cf715b397629209df1d Mon Sep 17 00:00:00 2001 From: Francesco <100066266+cranci1@users.noreply.github.com> Date: Mon, 24 Mar 2025 18:13:04 +0100 Subject: [PATCH] macos support --- .../DownloadManager/DownloadManager.swift | 215 ------------------ Sora/Views/DownloadView.swift | 83 ------- Sulfur.xcodeproj/project.pbxproj | 33 --- .../xcshareddata/swiftpm/Package.resolved | 18 -- 4 files changed, 349 deletions(-) delete mode 100644 Sora/Utils/DownloadManager/DownloadManager.swift delete mode 100644 Sora/Views/DownloadView.swift diff --git a/Sora/Utils/DownloadManager/DownloadManager.swift b/Sora/Utils/DownloadManager/DownloadManager.swift deleted file mode 100644 index d86e355..0000000 --- a/Sora/Utils/DownloadManager/DownloadManager.swift +++ /dev/null @@ -1,215 +0,0 @@ -// -// DownloadManager.swift -// Sulfur -// -// Created by Francesco on 09/03/25. -// - -import Foundation -import FFmpegSupport -import UIKit - -extension Notification.Name { - static let DownloadManagerStatusUpdate = Notification.Name("DownloadManagerStatusUpdate") -} - -class DownloadManager { - static let shared = DownloadManager() - - private var backgroundTaskIdentifier: UIBackgroundTaskIdentifier = .invalid - private var activeConversions = [String: Bool]() - - private init() { - NotificationCenter.default.addObserver(self, selector: #selector(applicationWillResignActive), name: UIApplication.willResignActiveNotification, object: nil) - } - - @objc private func applicationWillResignActive() { - if !activeConversions.isEmpty { - backgroundTaskIdentifier = UIApplication.shared.beginBackgroundTask { [weak self] in - self?.endBackgroundTask() - } - } - } - - private func endBackgroundTask() { - if backgroundTaskIdentifier != .invalid { - UIApplication.shared.endBackgroundTask(backgroundTaskIdentifier) - backgroundTaskIdentifier = .invalid - } - } - - func downloadAndConvertHLS(from url: URL, title: String, episode: Int, subtitleURL: URL? = nil, module: ScrapingModule, completion: @escaping (Bool, URL?) -> Void) { - guard let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { - completion(false, nil) - return - } - - let folderURL = documentsDirectory.appendingPathComponent(title + "-" + module.metadata.sourceName) - if (!FileManager.default.fileExists(atPath: folderURL.path)) { - do { - try FileManager.default.createDirectory(at: folderURL, withIntermediateDirectories: true, attributes: nil) - } catch { - Logger.shared.log("Error creating folder: \(error)") - completion(false, nil) - return - } - } - - let outputFileName = "\(title)_Episode\(episode)_\(module.metadata.sourceName).mp4" - let outputFileURL = folderURL.appendingPathComponent(outputFileName) - - let fileExtension = url.pathExtension.lowercased() - - if fileExtension == "mp4" { - NotificationCenter.default.post(name: .DownloadManagerStatusUpdate, object: nil, userInfo: [ - "title": title, - "episode": episode, - "type": "mp4", - "status": "Downloading", - "progress": 0.0 - ]) - - let task = URLSession.custom.downloadTask(with: url) { tempLocalURL, response, error in - if let tempLocalURL = tempLocalURL { - do { - try FileManager.default.moveItem(at: tempLocalURL, to: outputFileURL) - NotificationCenter.default.post(name: .DownloadManagerStatusUpdate, object: nil, userInfo: [ - "title": title, - "episode": episode, - "type": "mp4", - "status": "Completed", - "progress": 1.0 - ]) - DispatchQueue.main.async { - Logger.shared.log("Download successful: \(outputFileURL)") - completion(true, outputFileURL) - } - } catch { - DispatchQueue.main.async { - Logger.shared.log("Download failed: \(error)") - completion(false, nil) - } - } - } else { - DispatchQueue.main.async { - Logger.shared.log("Download failed: \(error?.localizedDescription ?? "Unknown error")") - completion(false, nil) - } - } - } - task.resume() - } else if fileExtension == "m3u8" { - let conversionKey = "\(title)_\(episode)_\(module.metadata.sourceName)" - activeConversions[conversionKey] = true - - if UIApplication.shared.applicationState != .active && backgroundTaskIdentifier == .invalid { - backgroundTaskIdentifier = UIApplication.shared.beginBackgroundTask { [weak self] in - self?.endBackgroundTask() - } - } - - DispatchQueue.global(qos: .background).async { - NotificationCenter.default.post(name: .DownloadManagerStatusUpdate, object: nil, userInfo: [ - "title": title, - "episode": episode, - "type": "hls", - "status": "Converting", - "progress": 0.0 - ]) - - let processorCount = ProcessInfo.processInfo.processorCount - let physicalMemory = ProcessInfo.processInfo.physicalMemory / (1024 * 1024) - - var ffmpegCommand = ["ffmpeg", "-y"] - - ffmpegCommand.append(contentsOf: ["-protocol_whitelist", "file,http,https,tcp,tls"]) - - ffmpegCommand.append(contentsOf: ["-fflags", "+genpts"]) - ffmpegCommand.append(contentsOf: ["-reconnect", "1", "-reconnect_streamed", "1", "-reconnect_delay_max", "5"]) - ffmpegCommand.append(contentsOf: ["-headers", "Referer: \(module.metadata.baseUrl)"]) - - let multiThreads = UserDefaults.standard.bool(forKey: "multiThreads") - if multiThreads { - let threadCount = max(2, processorCount - 1) - ffmpegCommand.append(contentsOf: ["-threads", "\(threadCount)"]) - } else { - ffmpegCommand.append(contentsOf: ["-threads", "2"]) - } - - let bufferSize = min(32, max(8, Int(physicalMemory) / 256)) - ffmpegCommand.append(contentsOf: ["-bufsize", "\(bufferSize)M"]) - ffmpegCommand.append(contentsOf: ["-i", url.absoluteString]) - - if let subtitleURL = subtitleURL { - do { - let subtitleData = try Data(contentsOf: subtitleURL) - let subtitleFileExtension = subtitleURL.pathExtension.lowercased() - if subtitleFileExtension != "srt" && subtitleFileExtension != "vtt" { - Logger.shared.log("Unsupported subtitle format: \(subtitleFileExtension)") - } - let subtitleFileName = "\(title)_Episode\(episode).\(subtitleFileExtension)" - let subtitleLocalURL = folderURL.appendingPathComponent(subtitleFileName) - try subtitleData.write(to: subtitleLocalURL) - ffmpegCommand.append(contentsOf: ["-i", subtitleLocalURL.path]) - - ffmpegCommand.append(contentsOf: [ - "-c:v", "copy", - "-c:a", "copy", - "-c:s", "mov_text", - "-disposition:s:0", "default+forced", - "-metadata:s:s:0", "handler_name=English", - "-metadata:s:s:0", "language=eng" - ]) - - ffmpegCommand.append(outputFileURL.path) - } catch { - Logger.shared.log("Subtitle download failed: \(error)") - ffmpegCommand.append(contentsOf: ["-c:v", "copy", "-c:a", "copy"]) - ffmpegCommand.append(contentsOf: ["-movflags", "+faststart"]) - ffmpegCommand.append(outputFileURL.path) - } - } else { - ffmpegCommand.append(contentsOf: ["-c:v", "copy", "-c:a", "copy"]) - ffmpegCommand.append(contentsOf: ["-movflags", "+faststart"]) - ffmpegCommand.append(outputFileURL.path) - } - Logger.shared.log("FFmpeg command: \(ffmpegCommand.joined(separator: " "))", type: "Debug") - - NotificationCenter.default.post(name: .DownloadManagerStatusUpdate, object: nil, userInfo: [ - "title": title, - "episode": episode, - "type": "hls", - "status": "Converting", - "progress": 0.5 - ]) - - let success = ffmpeg(ffmpegCommand) - DispatchQueue.main.async { [weak self] in - if success == 0 { - NotificationCenter.default.post(name: .DownloadManagerStatusUpdate, object: nil, userInfo: [ - "title": title, - "episode": episode, - "type": "hls", - "status": "Completed", - "progress": 1.0 - ]) - Logger.shared.log("Conversion successful: \(outputFileURL)") - completion(true, outputFileURL) - } else { - Logger.shared.log("Conversion failed") - completion(false, nil) - } - - self?.activeConversions[conversionKey] = nil - - if self?.activeConversions.isEmpty ?? true { - self?.endBackgroundTask() - } - } - } - } else { - Logger.shared.log("Unsupported file type: \(fileExtension)") - completion(false, nil) - } - } -} diff --git a/Sora/Views/DownloadView.swift b/Sora/Views/DownloadView.swift deleted file mode 100644 index 8717e2a..0000000 --- a/Sora/Views/DownloadView.swift +++ /dev/null @@ -1,83 +0,0 @@ -// -// DownloadView.swift -// Sulfur -// -// Created by Francesco on 12/03/25. -// - -import SwiftUI - -struct DownloadItem: Identifiable { - let id = UUID() - let title: String - let episode: Int - let type: String - var progress: Double - var status: String -} - -class DownloadViewModel: ObservableObject { - @Published var downloads: [DownloadItem] = [] - - init() { - NotificationCenter.default.addObserver(self, selector: #selector(updateStatus(_:)), name: .DownloadManagerStatusUpdate, object: nil) - } - - @objc func updateStatus(_ notification: Notification) { - guard let info = notification.userInfo, - let title = info["title"] as? String, - let episode = info["episode"] as? Int, - let type = info["type"] as? String, - let status = info["status"] as? String, - let progress = info["progress"] as? Double else { return } - - if let index = downloads.firstIndex(where: { $0.title == title && $0.episode == episode }) { - downloads[index] = DownloadItem(title: title, episode: episode, type: type, progress: progress, status: status) - } else { - let newDownload = DownloadItem(title: title, episode: episode, type: type, progress: progress, status: status) - downloads.append(newDownload) - } - } -} - -struct DownloadView: View { - @StateObject var viewModel = DownloadViewModel() - - var body: some View { - NavigationView { - List(viewModel.downloads) { download in - HStack(spacing: 16) { - Image(systemName: iconName(for: download)) - .resizable() - .frame(width: 30, height: 30) - .foregroundColor(.accentColor) - - VStack(alignment: .leading, spacing: 4) { - Text("\(download.title) - Episode \(download.episode)") - .font(.headline) - - ProgressView(value: download.progress) - .progressViewStyle(LinearProgressViewStyle(tint: .accentColor)) - .frame(height: 8) - - Text(download.status) - .font(.subheadline) - .foregroundColor(.secondary) - } - Spacer() - } - .padding(.vertical, 8) - } - .navigationTitle("Downloads") - } - .navigationViewStyle(StackNavigationViewStyle()) - } - - func iconName(for download: DownloadItem) -> String { - if download.type == "hls" { - return download.status.lowercased().contains("converting") ? "arrow.triangle.2.circlepath.circle.fill" : "checkmark.circle.fill" - } else { - return download.progress >= 1.0 ? "checkmark.circle.fill" : "arrow.down.circle.fill" - } - } -} diff --git a/Sulfur.xcodeproj/project.pbxproj b/Sulfur.xcodeproj/project.pbxproj index c38b27b..e72ea8f 100644 --- a/Sulfur.xcodeproj/project.pbxproj +++ b/Sulfur.xcodeproj/project.pbxproj @@ -7,7 +7,6 @@ objects = { /* Begin PBXBuildFile section */ - 130217CC2D81C55E0011EFF5 /* DownloadView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 130217CB2D81C55E0011EFF5 /* DownloadView.swift */; }; 130C6BFA2D53AB1F00DC1432 /* SettingsViewData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 130C6BF92D53AB1F00DC1432 /* SettingsViewData.swift */; }; 13103E892D58A39A000F0673 /* AniListItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13103E882D58A39A000F0673 /* AniListItem.swift */; }; 13103E8B2D58E028000F0673 /* View.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13103E8A2D58E028000F0673 /* View.swift */; }; @@ -53,8 +52,6 @@ 13DB46902D900A38008CBC03 /* URL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13DB468F2D900A38008CBC03 /* URL.swift */; }; 13DB46922D900BCE008CBC03 /* SettingsViewTrackers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13DB46912D900BCE008CBC03 /* SettingsViewTrackers.swift */; }; 13DB7CC32D7D99C0004371D3 /* SubtitleSettingsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13DB7CC22D7D99C0004371D3 /* SubtitleSettingsManager.swift */; }; - 13DB7CC62D7DC7D2004371D3 /* FFmpeg-iOS-Lame in Frameworks */ = {isa = PBXBuildFile; productRef = 13DB7CC52D7DC7D2004371D3 /* FFmpeg-iOS-Lame */; }; - 13DB7CEC2D7DED5D004371D3 /* DownloadManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13DB7CEB2D7DED5D004371D3 /* DownloadManager.swift */; }; 13DC0C462D302C7500D0F966 /* VideoPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13DC0C452D302C7500D0F966 /* VideoPlayer.swift */; }; 13EA2BD52D32D97400C1EBD7 /* CustomPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13EA2BD12D32D97400C1EBD7 /* CustomPlayer.swift */; }; 13EA2BD62D32D97400C1EBD7 /* Double+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13EA2BD32D32D97400C1EBD7 /* Double+Extension.swift */; }; @@ -65,7 +62,6 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ - 130217CB2D81C55E0011EFF5 /* DownloadView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DownloadView.swift; sourceTree = ""; }; 130C6BF82D53A4C200DC1432 /* Sora.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Sora.entitlements; sourceTree = ""; }; 130C6BF92D53AB1F00DC1432 /* SettingsViewData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewData.swift; sourceTree = ""; }; 13103E882D58A39A000F0673 /* AniListItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AniListItem.swift; sourceTree = ""; }; @@ -111,7 +107,6 @@ 13DB468F2D900A38008CBC03 /* URL.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URL.swift; sourceTree = ""; }; 13DB46912D900BCE008CBC03 /* SettingsViewTrackers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewTrackers.swift; sourceTree = ""; }; 13DB7CC22D7D99C0004371D3 /* SubtitleSettingsManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubtitleSettingsManager.swift; sourceTree = ""; }; - 13DB7CEB2D7DED5D004371D3 /* DownloadManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DownloadManager.swift; sourceTree = ""; }; 13DC0C412D2EC9BA00D0F966 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; 13DC0C452D302C7500D0F966 /* VideoPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoPlayer.swift; sourceTree = ""; }; 13EA2BD12D32D97400C1EBD7 /* CustomPlayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomPlayer.swift; sourceTree = ""; }; @@ -127,7 +122,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 13DB7CC62D7DC7D2004371D3 /* FFmpeg-iOS-Lame in Frameworks */, 1359ED1A2D76FA7D00C13034 /* Drops in Frameworks */, 133D7C972D2BE2AF0075467E /* Kingfisher in Frameworks */, ); @@ -253,7 +247,6 @@ 1399FAD22D3AB34F00E97C31 /* SettingsView */, 133F55B92D33B53E00E08EEA /* LibraryView */, 133D7C7C2D2BE2630075467E /* SearchView.swift */, - 130217CB2D81C55E0011EFF5 /* DownloadView.swift */, ); path = Views; sourceTree = ""; @@ -284,7 +277,6 @@ 133D7C852D2BE2640075467E /* Utils */ = { isa = PBXGroup; children = ( - 13DB7CEA2D7DED50004371D3 /* DownloadManager */, 13C0E5E82D5F85DD00E7F619 /* ContinueWatching */, 13103E8C2D58E037000F0673 /* SkeletonCells */, 13DC0C442D302C6A00D0F966 /* MediaPlayer */, @@ -400,14 +392,6 @@ path = Auth; sourceTree = ""; }; - 13DB7CEA2D7DED50004371D3 /* DownloadManager */ = { - isa = PBXGroup; - children = ( - 13DB7CEB2D7DED5D004371D3 /* DownloadManager.swift */, - ); - path = DownloadManager; - sourceTree = ""; - }; 13DC0C442D302C6A00D0F966 /* MediaPlayer */ = { isa = PBXGroup; children = ( @@ -456,7 +440,6 @@ packageProductDependencies = ( 133D7C962D2BE2AF0075467E /* Kingfisher */, 1359ED192D76FA7D00C13034 /* Drops */, - 13DB7CC52D7DC7D2004371D3 /* FFmpeg-iOS-Lame */, ); productName = Sora; productReference = 133D7C6A2D2BE2500075467E /* Sulfur.app */; @@ -488,7 +471,6 @@ packageReferences = ( 133D7C952D2BE2AF0075467E /* XCRemoteSwiftPackageReference "Kingfisher" */, 1359ED182D76FA7D00C13034 /* XCRemoteSwiftPackageReference "Drops" */, - 13DB7CC42D7DC7D2004371D3 /* XCRemoteSwiftPackageReference "FFmpeg-iOS-Lame" */, ); productRefGroup = 133D7C6B2D2BE2500075467E /* Products */; projectDirPath = ""; @@ -534,7 +516,6 @@ 133D7C932D2BE2640075467E /* Modules.swift in Sources */, 13DB7CC32D7D99C0004371D3 /* SubtitleSettingsManager.swift in Sources */, 133D7C702D2BE2500075467E /* ContentView.swift in Sources */, - 13DB7CEC2D7DED5D004371D3 /* DownloadManager.swift in Sources */, 1334FF522D7871B7007E289F /* TMDBItem.swift in Sources */, 13D99CF72D4E73C300250A86 /* ModuleAdditionSettingsView.swift in Sources */, 13C0E5EC2D5F85F800E7F619 /* ContinueWatchingItem.swift in Sources */, @@ -554,7 +535,6 @@ 133D7C942D2BE2640075467E /* JSController.swift in Sources */, 133D7C922D2BE2640075467E /* URLSession.swift in Sources */, 133D7C912D2BE2640075467E /* SettingsViewModule.swift in Sources */, - 130217CC2D81C55E0011EFF5 /* DownloadView.swift in Sources */, 133F55BB2D33B55100E08EEA /* LibraryManager.swift in Sources */, 133D7C8E2D2BE2640075467E /* LibraryView.swift in Sources */, 133D7C6E2D2BE2500075467E /* SoraApp.swift in Sources */, @@ -814,14 +794,6 @@ kind = branch; }; }; - 13DB7CC42D7DC7D2004371D3 /* XCRemoteSwiftPackageReference "FFmpeg-iOS-Lame" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/kewlbear/FFmpeg-iOS-Lame"; - requirement = { - branch = main; - kind = branch; - }; - }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ @@ -835,11 +807,6 @@ package = 1359ED182D76FA7D00C13034 /* XCRemoteSwiftPackageReference "Drops" */; productName = Drops; }; - 13DB7CC52D7DC7D2004371D3 /* FFmpeg-iOS-Lame */ = { - isa = XCSwiftPackageProductDependency; - package = 13DB7CC42D7DC7D2004371D3 /* XCRemoteSwiftPackageReference "FFmpeg-iOS-Lame" */; - productName = "FFmpeg-iOS-Lame"; - }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 133D7C622D2BE2500075467E /* Project object */; diff --git a/Sulfur.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Sulfur.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 9784b35..48e92a9 100644 --- a/Sulfur.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Sulfur.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -10,24 +10,6 @@ "version": null } }, - { - "package": "FFmpeg-iOS-Lame", - "repositoryURL": "https://github.com/kewlbear/FFmpeg-iOS-Lame", - "state": { - "branch": "main", - "revision": "1808fa5a1263c5e216646cd8421fc7dcb70520cc", - "version": null - } - }, - { - "package": "FFmpeg-iOS-Support", - "repositoryURL": "https://github.com/kewlbear/FFmpeg-iOS-Support", - "state": { - "branch": null, - "revision": "be3bd9149ac53760e8725652eee99c405b2be47a", - "version": "0.0.2" - } - }, { "package": "Kingfisher", "repositoryURL": "https://github.com/onevcat/Kingfisher.git",