@stepincto/expo-video 1.0.1 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -17,6 +17,8 @@ class CachableRequest: Equatable, Hashable {
|
|
|
17
17
|
var response: URLResponse?
|
|
18
18
|
private(set) var receivedData = Data()
|
|
19
19
|
private let dataOffset: Int64
|
|
20
|
+
private static var fileLocks: [String: NSLock] = [:]
|
|
21
|
+
private static let fileLocksQueue = DispatchQueue(label: "expo-video-cache-file-locks")
|
|
20
22
|
|
|
21
23
|
init(loadingRequest: AVAssetResourceLoadingRequest, dataTask: URLSessionDataTask, dataRequest: AVAssetResourceLoadingDataRequest) {
|
|
22
24
|
self.loadingRequest = loadingRequest
|
|
@@ -30,8 +32,45 @@ class CachableRequest: Equatable, Hashable {
|
|
|
30
32
|
}
|
|
31
33
|
|
|
32
34
|
func saveData(to cachedResource: CachedResource) {
|
|
33
|
-
//
|
|
34
|
-
|
|
35
|
+
// 1. Check if file is fully cached
|
|
36
|
+
if let mediaInfo = cachedResource.mediaInfo {
|
|
37
|
+
let expectedLength = Int(mediaInfo.expectedContentLength)
|
|
38
|
+
let ranges = mediaInfo.loadedDataRanges
|
|
39
|
+
if ranges.count == 1, ranges[0].0 == 0, ranges[0].1 == expectedLength - 1 {
|
|
40
|
+
// Already fully cached, do not write
|
|
41
|
+
return
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// 2. Acquire per-file lock (non-blocking)
|
|
46
|
+
let filePath = cachedResource.dataPath
|
|
47
|
+
let lock: NSLock = Self.fileLocksQueue.sync {
|
|
48
|
+
if let l = Self.fileLocks[filePath] { return l }
|
|
49
|
+
let l = NSLock()
|
|
50
|
+
Self.fileLocks[filePath] = l
|
|
51
|
+
return l
|
|
52
|
+
}
|
|
53
|
+
if !lock.try() {
|
|
54
|
+
// Another writer is active, skip this write
|
|
55
|
+
return
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// 3. Perform async write with final range check inside the task
|
|
59
|
+
let offset = dataOffset
|
|
60
|
+
let length = receivedData.count
|
|
61
|
+
let end = offset + Int64(length) - 1
|
|
62
|
+
|
|
63
|
+
Task.detached(priority: .userInitiated) { [receivedData] in
|
|
64
|
+
// Final check inside the async task to prevent race conditions
|
|
65
|
+
if cachedResource.canRespondWithData(from: offset, to: end) {
|
|
66
|
+
// This range is already cached, skip writing
|
|
67
|
+
lock.unlock()
|
|
68
|
+
return
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
await cachedResource.writeData(data: receivedData, offset: offset)
|
|
72
|
+
lock.unlock()
|
|
73
|
+
}
|
|
35
74
|
}
|
|
36
75
|
|
|
37
76
|
static func == (lhs: CachableRequest, rhs: CachableRequest) -> Bool {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stepincto/expo-video",
|
|
3
3
|
"title": "Expo Video",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.4",
|
|
5
5
|
"originalUpstreamVersion": "2.2.2",
|
|
6
6
|
"description": "A cross-platform, performant video component for React Native and Expo with Web support",
|
|
7
7
|
"main": "build/index.js",
|