mirror of
https://github.com/cranci1/Sora.git
synced 2026-03-11 17:45:37 +00:00
test cuz my mac doesnt build
This commit is contained in:
parent
be3a968f3e
commit
4f30dfb588
4 changed files with 202 additions and 21 deletions
|
|
@ -15,6 +15,10 @@ struct ContentView: View {
|
|||
.tabItem {
|
||||
Label("Library", systemImage: "books.vertical")
|
||||
}
|
||||
DownloadsView()
|
||||
.tabItem {
|
||||
Label("Downloads", systemImage: "arrow.down.circle.fill")
|
||||
}
|
||||
SearchView()
|
||||
.tabItem {
|
||||
Label("Search", systemImage: "magnifyingglass")
|
||||
|
|
|
|||
|
|
@ -38,31 +38,15 @@ class DownloadManager {
|
|||
|
||||
let outputFileName = "\(title)_Episode\(episode).mp4"
|
||||
let outputFileURL = folderURL.appendingPathComponent(outputFileName)
|
||||
let downloadID = UUID()
|
||||
NotificationCenter.default.post(name: .downloadStarted, object: nil, userInfo: ["fileName": outputFileName, "id": downloadID])
|
||||
|
||||
let fileExtension = url.pathExtension.lowercased()
|
||||
|
||||
if fileExtension == "mp4" {
|
||||
let task = URLSession.shared.downloadTask(with: url) { tempLocalURL, response, error in
|
||||
if let tempLocalURL = tempLocalURL {
|
||||
do {
|
||||
try FileManager.default.moveItem(at: tempLocalURL, to: outputFileURL)
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
let delegate = DownloadTaskDelegate(downloadID: downloadID, fileName: outputFileName, outputFileURL: outputFileURL, completion: completion)
|
||||
let session = URLSession(configuration: .default, delegate: delegate, delegateQueue: nil)
|
||||
let task = session.downloadTask(with: url)
|
||||
task.resume()
|
||||
} else if fileExtension == "m3u8" {
|
||||
DispatchQueue.global(qos: .background).async {
|
||||
|
|
@ -92,9 +76,11 @@ class DownloadManager {
|
|||
DispatchQueue.main.async {
|
||||
if success == 0 {
|
||||
Logger.shared.log("✅ Conversion successful: \(outputFileURL)")
|
||||
NotificationCenter.default.post(name: .downloadCompleted, object: nil, userInfo: ["id": downloadID, "success": true])
|
||||
completion(true, outputFileURL)
|
||||
} else {
|
||||
Logger.shared.log("❌ Conversion failed")
|
||||
NotificationCenter.default.post(name: .downloadCompleted, object: nil, userInfo: ["id": downloadID, "success": false])
|
||||
completion(false, nil)
|
||||
}
|
||||
}
|
||||
|
|
@ -105,3 +91,71 @@ class DownloadManager {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
class DownloadTaskDelegate: NSObject, URLSessionDownloadDelegate {
|
||||
let downloadID: UUID
|
||||
let outputFileURL: URL
|
||||
let completion: (Bool, URL?) -> Void
|
||||
let fileName: String
|
||||
let startTime: Date
|
||||
var lastTime: Date
|
||||
|
||||
init(downloadID: UUID, fileName: String, outputFileURL: URL, completion: @escaping (Bool, URL?) -> Void) {
|
||||
self.downloadID = downloadID
|
||||
self.fileName = fileName
|
||||
self.outputFileURL = outputFileURL
|
||||
self.completion = completion
|
||||
self.startTime = Date()
|
||||
self.lastTime = Date()
|
||||
}
|
||||
|
||||
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
|
||||
let progress = Double(totalBytesWritten) / Double(totalBytesExpectedToWrite)
|
||||
let now = Date()
|
||||
let timeInterval = now.timeIntervalSince(lastTime)
|
||||
var speed: Double = 0.0
|
||||
if timeInterval > 0 {
|
||||
speed = Double(bytesWritten) / timeInterval
|
||||
}
|
||||
lastTime = now
|
||||
|
||||
let downloadedMB = Double(totalBytesWritten) / 1024.0 / 1024.0
|
||||
let speedMB = speed / 1024.0 / 1024.0
|
||||
|
||||
DispatchQueue.main.async {
|
||||
NotificationCenter.default.post(name: .downloadProgressUpdate, object: nil, userInfo: [
|
||||
"id": self.downloadID,
|
||||
"progress": progress,
|
||||
"downloadedSize": String(format: "%.2f MB", downloadedMB),
|
||||
"downloadSpeed": String(format: "%.2f MB/s", speedMB)
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
|
||||
do {
|
||||
try FileManager.default.moveItem(at: location, to: outputFileURL)
|
||||
DispatchQueue.main.async {
|
||||
Logger.shared.log("✅ Download successful: \(self.outputFileURL)")
|
||||
NotificationCenter.default.post(name: .downloadCompleted, object: nil, userInfo: ["id": self.downloadID, "success": true])
|
||||
self.completion(true, self.outputFileURL)
|
||||
}
|
||||
} catch {
|
||||
DispatchQueue.main.async {
|
||||
Logger.shared.log("❌ Download failed: \(error)")
|
||||
NotificationCenter.default.post(name: .downloadCompleted, object: nil, userInfo: ["id": self.downloadID, "success": false])
|
||||
self.completion(false, nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
|
||||
if let error = error {
|
||||
DispatchQueue.main.async {
|
||||
Logger.shared.log("❌ Download failed: \(error.localizedDescription)")
|
||||
NotificationCenter.default.post(name: .downloadCompleted, object: nil, userInfo: ["id": self.downloadID, "success": false])
|
||||
self.completion(false, nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
117
Sora/Views/DownloadsView.swift
Normal file
117
Sora/Views/DownloadsView.swift
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
//
|
||||
// DownloadsView.swift
|
||||
// Sulfur
|
||||
//
|
||||
// Created by Francesco on 10/03/25.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Combine
|
||||
|
||||
extension Notification.Name {
|
||||
static let downloadStarted = Notification.Name("downloadStarted")
|
||||
static let downloadProgressUpdate = Notification.Name("downloadProgressUpdate")
|
||||
static let downloadCompleted = Notification.Name("downloadCompleted")
|
||||
}
|
||||
|
||||
struct DownloadItem: Identifiable {
|
||||
let id = UUID()
|
||||
let fileName: String
|
||||
var status: String = "Downloading"
|
||||
var progress: Double = 0.0
|
||||
var downloadedSize: String = "0 MB"
|
||||
var downloadSpeed: String = "0 MB/s"
|
||||
}
|
||||
|
||||
class DownloadsViewModel: ObservableObject {
|
||||
@Published var downloads: [DownloadItem] = []
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
init() {
|
||||
NotificationCenter.default.publisher(for: .downloadStarted)
|
||||
.sink { [weak self] notification in
|
||||
if let fileName = notification.userInfo?["fileName"] as? String {
|
||||
let newDownload = DownloadItem(fileName: fileName)
|
||||
self?.downloads.append(newDownload)
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
NotificationCenter.default.publisher(for: .downloadProgressUpdate)
|
||||
.sink { [weak self] notification in
|
||||
guard let id = notification.userInfo?["id"] as? UUID,
|
||||
let progress = notification.userInfo?["progress"] as? Double,
|
||||
let downloadedSize = notification.userInfo?["downloadedSize"] as? String,
|
||||
let downloadSpeed = notification.userInfo?["downloadSpeed"] as? String else {
|
||||
return
|
||||
}
|
||||
if let index = self?.downloads.firstIndex(where: { $0.id == id }) {
|
||||
self?.downloads[index].progress = progress
|
||||
self?.downloads[index].downloadedSize = downloadedSize
|
||||
self?.downloads[index].downloadSpeed = downloadSpeed
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
NotificationCenter.default.publisher(for: .downloadCompleted)
|
||||
.sink { [weak self] notification in
|
||||
guard let id = notification.userInfo?["id"] as? UUID,
|
||||
let success = notification.userInfo?["success"] as? Bool else {
|
||||
return
|
||||
}
|
||||
if let index = self?.downloads.firstIndex(where: { $0.id == id }) {
|
||||
self?.downloads[index].status = success ? "Completed" : "Failed"
|
||||
self?.downloads[index].progress = success ? 1.0 : self?.downloads[index].progress ?? 0.0
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
}
|
||||
|
||||
struct DownloadsView: View {
|
||||
@StateObject var viewModel = DownloadsViewModel()
|
||||
|
||||
var body: some View {
|
||||
NavigationView {
|
||||
List(viewModel.downloads) { download in
|
||||
DownloadRow(download: download)
|
||||
}
|
||||
.navigationTitle("Downloads")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct DownloadRow: View {
|
||||
let download: DownloadItem
|
||||
|
||||
var iconName: String {
|
||||
switch download.status {
|
||||
case "Downloading":
|
||||
return "arrow.down.circle"
|
||||
case "Completed":
|
||||
return "checkmark.circle"
|
||||
default:
|
||||
return "exclamationmark.triangle"
|
||||
}
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading) {
|
||||
HStack {
|
||||
Image(systemName: iconName)
|
||||
.foregroundColor(.blue)
|
||||
Text(download.fileName)
|
||||
.font(.headline)
|
||||
}
|
||||
ProgressView(value: download.progress)
|
||||
.progressViewStyle(LinearProgressViewStyle())
|
||||
HStack {
|
||||
Text("Speed: \(download.downloadSpeed)")
|
||||
Spacer()
|
||||
Text("Size: \(download.downloadedSize)")
|
||||
}
|
||||
.font(.subheadline)
|
||||
}
|
||||
.padding(.vertical, 8)
|
||||
}
|
||||
}
|
||||
|
|
@ -50,6 +50,7 @@
|
|||
13CBA0882D60F19C00EFE70A /* VTTSubtitlesLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13CBA0872D60F19C00EFE70A /* VTTSubtitlesLoader.swift */; };
|
||||
13CBEFDA2D5F7D1200D011EE /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13CBEFD92D5F7D1200D011EE /* String.swift */; };
|
||||
13D842552D45267500EBBFA6 /* DropManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13D842542D45267500EBBFA6 /* DropManager.swift */; };
|
||||
13D949EB2D7F412B00C92959 /* DownloadsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13D949EA2D7F412B00C92959 /* DownloadsView.swift */; };
|
||||
13D99CF72D4E73C300250A86 /* ModuleAdditionSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13D99CF62D4E73C300250A86 /* ModuleAdditionSettingsView.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 */; };
|
||||
|
|
@ -106,6 +107,7 @@
|
|||
13CBA0872D60F19C00EFE70A /* VTTSubtitlesLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VTTSubtitlesLoader.swift; sourceTree = "<group>"; };
|
||||
13CBEFD92D5F7D1200D011EE /* String.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = String.swift; sourceTree = "<group>"; };
|
||||
13D842542D45267500EBBFA6 /* DropManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropManager.swift; sourceTree = "<group>"; };
|
||||
13D949EA2D7F412B00C92959 /* DownloadsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DownloadsView.swift; sourceTree = "<group>"; };
|
||||
13D99CF62D4E73C300250A86 /* ModuleAdditionSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModuleAdditionSettingsView.swift; sourceTree = "<group>"; };
|
||||
13DB7CC22D7D99C0004371D3 /* SubtitleSettingsManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubtitleSettingsManager.swift; sourceTree = "<group>"; };
|
||||
13DB7CEB2D7DED5D004371D3 /* DownloadManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DownloadManager.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -261,6 +263,7 @@
|
|||
1399FAD22D3AB34F00E97C31 /* SettingsView */,
|
||||
133F55B92D33B53E00E08EEA /* LibraryView */,
|
||||
133D7C7C2D2BE2630075467E /* SearchView.swift */,
|
||||
13D949EA2D7F412B00C92959 /* DownloadsView.swift */,
|
||||
);
|
||||
path = Views;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -557,6 +560,7 @@
|
|||
133D7C922D2BE2640075467E /* URLSession.swift in Sources */,
|
||||
133D7C912D2BE2640075467E /* SettingsViewModule.swift in Sources */,
|
||||
136F21BC2D5B8F29006409AC /* AniList-DetailsView.swift in Sources */,
|
||||
13D949EB2D7F412B00C92959 /* DownloadsView.swift in Sources */,
|
||||
133F55BB2D33B55100E08EEA /* LibraryManager.swift in Sources */,
|
||||
13103E842D589D8B000F0673 /* AniList-Seasonal.swift in Sources */,
|
||||
133D7C8E2D2BE2640075467E /* LibraryView.swift in Sources */,
|
||||
|
|
@ -607,6 +611,7 @@
|
|||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_BITCODE = YES;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
|
|
@ -669,6 +674,7 @@
|
|||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_BITCODE = YES;
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
|
|
|
|||
Loading…
Reference in a new issue