yes -DownloadManager
Some checks are pending
Build and Release IPA / Build IPA (push) Waiting to run

This commit is contained in:
cranci1 2025-03-09 16:57:05 +01:00
parent 020c753eb8
commit 7e611ce77f
3 changed files with 106 additions and 4 deletions

View file

@ -0,0 +1,93 @@
//
// DownloadManager.swift
// Sulfur
//
// Created by Francesco on 09/03/25.
//
import Foundation
import FFmpegSupport
class DownloadManager {
static let shared = DownloadManager()
private init() {}
/// Downloads and converts an HLS stream to MP4, or downloads an MP4 stream normally.
/// - Parameters:
/// - url: The stream URL (either .m3u8 or .mp4).
/// - title: The title used for creating the folder.
/// - episode: The episode number used for naming the output file.
/// - completion: Completion handler with a Bool indicating success and the URL of the output file.
func downloadAndConvertHLS(from url: URL, title: String, episode: Int, 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)
if !FileManager.default.fileExists(atPath: folderURL.path) {
do {
try FileManager.default.createDirectory(at: folderURL, withIntermediateDirectories: true, attributes: nil)
} catch {
print("Error creating folder: \(error)")
completion(false, nil)
return
}
}
let outputFileName = "\(title)_Episode\(episode).mp4"
let outputFileURL = folderURL.appendingPathComponent(outputFileName)
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)
}
}
}
task.resume()
} else if fileExtension == "m3u8" {
let ffmpegCommand = [
"ffmpeg",
"-threads", "0",
"-i", url.absoluteString,
"-c", "copy",
outputFileURL.path
]
DispatchQueue.global(qos: .background).async {
let success = ffmpeg(ffmpegCommand)
DispatchQueue.main.async {
if (success == 0) {
Logger.shared.log("✅ Conversion successful: \(outputFileURL)")
completion(true, outputFileURL)
} else {
Logger.shared.log("❌ Conversion failed")
completion(false, nil)
}
}
}
} else {
Logger.shared.log("❌ Unsupported file type: \(fileExtension)")
completion(false, nil)
}
}
}

View file

@ -21,7 +21,7 @@ struct LibraryView: View {
var body: some View {
NavigationView {
ScrollView {
VStack(alignment: .leading, spacing: 32) {
VStack(alignment: .leading, spacing: 16) {
Group {
Text("Continue Watching")
.font(.title2)
@ -92,7 +92,6 @@ struct LibraryView: View {
.aspectRatio(contentMode: .fit)
.frame(width: 35, height: 35)
.clipShape(Circle())
.padding(5)
}
Text(item.title)
@ -100,7 +99,6 @@ struct LibraryView: View {
.foregroundColor(.primary)
.lineLimit(2)
.multilineTextAlignment(.leading)
.padding(.horizontal, 8)
}
}
}
@ -139,7 +137,6 @@ struct LibraryView: View {
}
}
// ContinueWatchingSection and ContinueWatchingCell remain unchanged
struct ContinueWatchingSection: View {
@Binding var items: [ContinueWatchingItem]
var markAsWatched: (ContinueWatchingItem) -> Void

View file

@ -53,6 +53,7 @@
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 */; };
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 */; };
@ -107,6 +108,7 @@
13D842542D45267500EBBFA6 /* DropManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropManager.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>"; };
13DC0C412D2EC9BA00D0F966 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
13DC0C452D302C7500D0F966 /* VideoPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoPlayer.swift; sourceTree = "<group>"; };
13EA2BD12D32D97400C1EBD7 /* CustomPlayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomPlayer.swift; sourceTree = "<group>"; };
@ -230,6 +232,7 @@
133D7C6C2D2BE2500075467E /* Sora */ = {
isa = PBXGroup;
children = (
13DB7CEA2D7DED50004371D3 /* DownloadManager */,
130C6BF82D53A4C200DC1432 /* Sora.entitlements */,
13DC0C412D2EC9BA00D0F966 /* Info.plist */,
13103E802D589D6C000F0673 /* Tracking Services */,
@ -398,6 +401,14 @@
path = Drops;
sourceTree = "<group>";
};
13DB7CEA2D7DED50004371D3 /* DownloadManager */ = {
isa = PBXGroup;
children = (
13DB7CEB2D7DED5D004371D3 /* DownloadManager.swift */,
);
path = DownloadManager;
sourceTree = "<group>";
};
13DC0C442D302C6A00D0F966 /* MediaPlayer */ = {
isa = PBXGroup;
children = (
@ -526,6 +537,7 @@
136F21B92D5B8DD8006409AC /* AniList-MediaInfo.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 */,