@@ -11,7 +11,6 @@ import Foundation
11
11
12
12
class Downloader : NSObject , ObservableObject {
13
13
private( set) var destination : URL
14
- private( set) var sourceURL : URL
15
14
16
15
private let chunkSize = 10 * 1024 * 1024 // 10MB
17
16
@@ -31,28 +30,18 @@ class Downloader: NSObject, ObservableObject {
31
30
private( set) lazy var downloadState : CurrentValueSubject < DownloadState , Never > = CurrentValueSubject ( . notStarted)
32
31
private var stateSubscriber : Cancellable ?
33
32
34
- private( set) var tempFilePath : URL ?
33
+ private( set) var tempFilePath : URL
35
34
private( set) var expectedSize : Int ?
36
35
private( set) var downloadedSize : Int = 0
37
36
38
37
private var urlSession : URLSession ? = nil
39
38
40
- /// Creates the incomplete file path for a given destination URL
41
- /// This is similar to the Hugging Face Hub approach of using .incomplete files
42
- static func incompletePath( for destination: URL ) -> URL {
43
- destination. appendingPathExtension ( " incomplete " )
44
- }
45
-
46
39
/// Check if an incomplete file exists for the destination and returns its size
47
40
/// - Parameter destination: The destination URL for the download
48
41
/// - Returns: Size of the incomplete file if it exists, otherwise 0
49
- static func checkForIncompleteFile( at destination: URL ) -> Int {
50
- let incompletePath = Self . incompletePath ( for: destination)
51
-
42
+ static func incompleteFileSize( at incompletePath: URL ) -> Int {
52
43
if FileManager . default. fileExists ( atPath: incompletePath. path) {
53
- if let attributes = try ? FileManager . default. attributesOfItem ( atPath: incompletePath. path) ,
54
- let fileSize = attributes [ . size] as? Int
55
- {
44
+ if let attributes = try ? FileManager . default. attributesOfItem ( atPath: incompletePath. path) , let fileSize = attributes [ . size] as? Int {
56
45
return fileSize
57
46
}
58
47
}
@@ -63,29 +52,22 @@ class Downloader: NSObject, ObservableObject {
63
52
init (
64
53
from url: URL ,
65
54
to destination: URL ,
55
+ incompleteDestination: URL ,
66
56
using authToken: String ? = nil ,
67
57
inBackground: Bool = false ,
68
- resumeSize: Int = 0 , // Can be specified manually, but will also check for incomplete files
69
58
headers: [ String : String ] ? = nil ,
70
59
expectedSize: Int ? = nil ,
71
60
timeout: TimeInterval = 10 ,
72
61
numRetries: Int = 5
73
62
) {
74
63
self . destination = destination
75
- sourceURL = url
76
64
self . expectedSize = expectedSize
77
65
78
66
// Create incomplete file path based on destination
79
- tempFilePath = Downloader . incompletePath ( for : destination )
67
+ self . tempFilePath = incompleteDestination
80
68
81
69
// If resume size wasn't specified, check for an existing incomplete file
82
- let actualResumeSize : Int = if resumeSize > 0 {
83
- resumeSize
84
- } else {
85
- Downloader . checkForIncompleteFile ( at: destination)
86
- }
87
-
88
- downloadedSize = actualResumeSize
70
+ let resumeSize = Self . incompleteFileSize ( at: incompleteDestination)
89
71
90
72
super. init ( )
91
73
let sessionIdentifier = " swift-transformers.hub.downloader "
@@ -99,7 +81,7 @@ class Downloader: NSObject, ObservableObject {
99
81
100
82
urlSession = URLSession ( configuration: config, delegate: self , delegateQueue: nil )
101
83
102
- setUpDownload ( from: url, with: authToken, resumeSize: actualResumeSize , headers: headers, expectedSize: expectedSize, timeout: timeout, numRetries: numRetries)
84
+ setUpDownload ( from: url, with: authToken, resumeSize: resumeSize , headers: headers, expectedSize: expectedSize, timeout: timeout, numRetries: numRetries)
103
85
}
104
86
105
87
/// Sets up and initiates a file download operation
@@ -139,25 +121,6 @@ class Downloader: NSObject, ObservableObject {
139
121
140
122
Task {
141
123
do {
142
- // Check if incomplete file exists and get its size
143
- var existingSize = 0
144
- guard let incompleteFilePath = self . tempFilePath else {
145
- throw DownloadError . unexpectedError
146
- }
147
-
148
- let fileManager = FileManager . default
149
- if fileManager. fileExists ( atPath: incompleteFilePath. path) {
150
- let attributes = try fileManager. attributesOfItem ( atPath: incompleteFilePath. path)
151
- existingSize = attributes [ . size] as? Int ?? 0
152
- self . downloadedSize = existingSize
153
- } else {
154
- // Create parent directory if needed
155
- try fileManager. createDirectory ( at: incompleteFilePath. deletingLastPathComponent ( ) , withIntermediateDirectories: true )
156
-
157
- // Create empty incomplete file
158
- fileManager. createFile ( atPath: incompleteFilePath. path, contents: nil )
159
- }
160
-
161
124
// Set up the request with appropriate headers
162
125
var request = URLRequest ( url: url)
163
126
var requestHeaders = headers ?? [ : ]
@@ -167,12 +130,12 @@ class Downloader: NSObject, ObservableObject {
167
130
}
168
131
169
132
// Set Range header if we're resuming
170
- if existingSize > 0 {
171
- requestHeaders [ " Range " ] = " bytes= \( existingSize ) - "
133
+ if resumeSize > 0 {
134
+ requestHeaders [ " Range " ] = " bytes= \( resumeSize ) - "
172
135
173
136
// Calculate and show initial progress
174
137
if let expectedSize, expectedSize > 0 {
175
- let initialProgress = Double ( existingSize ) / Double( expectedSize)
138
+ let initialProgress = Double ( resumeSize ) / Double( expectedSize)
176
139
self . downloadState. value = . downloading( initialProgress)
177
140
} else {
178
141
self . downloadState. value = . downloading( 0 )
@@ -185,10 +148,10 @@ class Downloader: NSObject, ObservableObject {
185
148
request. allHTTPHeaderFields = requestHeaders
186
149
187
150
// Open the incomplete file for writing
188
- let tempFile = try FileHandle ( forWritingTo: incompleteFilePath )
151
+ let tempFile = try FileHandle ( forWritingTo: self . tempFilePath )
189
152
190
153
// If resuming, seek to end of file
191
- if existingSize > 0 {
154
+ if resumeSize > 0 {
192
155
try tempFile. seekToEnd ( )
193
156
}
194
157
@@ -197,7 +160,7 @@ class Downloader: NSObject, ObservableObject {
197
160
198
161
// Clean up and move the completed download to its final destination
199
162
tempFile. closeFile ( )
200
- try fileManager . moveDownloadedFile ( from: incompleteFilePath , to: self . destination)
163
+ try FileManager . default . moveDownloadedFile ( from: self . tempFilePath , to: self . destination)
201
164
self . downloadState. value = . completed( self . destination)
202
165
} catch {
203
166
self . downloadState. value = . failed( error)
0 commit comments