@stepincto/expo-video 1.0.0 → 1.0.3

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,41 @@ class CachableRequest: Equatable, Hashable {
30
32
  }
31
33
 
32
34
  func saveData(to cachedResource: CachedResource) {
33
- // Disabled: Player is now read-only with respect to the cache.
34
- // Do nothing.
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
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
+ defer { lock.unlock() }
58
+
59
+ // 3. Write only missing data
60
+ let offset = dataOffset
61
+ let length = receivedData.count
62
+ let end = offset + Int64(length) - 1
63
+ if cachedResource.canRespondWithData(from: offset, to: end) {
64
+ // This range is already cached, skip
65
+ return
66
+ }
67
+ Task {
68
+ await cachedResource.writeData(data: receivedData, offset: offset)
69
+ }
35
70
  }
36
71
 
37
72
  static func == (lhs: CachableRequest, rhs: CachableRequest) -> Bool {
@@ -10,7 +10,7 @@ import ExpoModulesCore
10
10
  */
11
11
  class CachedResource {
12
12
  private let url: URL
13
- private let dataPath: String
13
+ internal let dataPath: String
14
14
  private let fileHandle: MediaFileHandle
15
15
  private(set) var mediaInfo: MediaInfo?
16
16
 
@@ -210,6 +210,15 @@ extension VideoCacheManager {
210
210
  let fileUrl = URL(fileURLWithPath: saveFilePath)
211
211
  let mediaInfoPath = saveFilePath + VideoCacheManager.mediaInfoSuffix
212
212
 
213
+ // Ensure parent directory exists before writing file
214
+ let parentDir = fileUrl.deletingLastPathComponent()
215
+ do {
216
+ try FileManager.default.createDirectory(at: parentDir, withIntermediateDirectories: true)
217
+ } catch {
218
+ print("[expo-video] Failed to create parent directory for cache file: \(error)")
219
+ throw error
220
+ }
221
+
213
222
  let (data, response) = try await URLSession.shared.data(from: url)
214
223
  try data.write(to: fileUrl, options: .atomic)
215
224
 
@@ -253,10 +262,13 @@ extension VideoCacheManager {
253
262
  let fileUrl = URL(fileURLWithPath: saveFilePath)
254
263
  let mediaInfoPath = saveFilePath + VideoCacheManager.mediaInfoSuffix
255
264
 
256
- // Ensure the cache directory exists (same as VideoAsset)
257
- if let cacheDir = try? FileManager.default.url(for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: true) {
258
- let videoCacheDir = cacheDir.appendingPathComponent(VideoCacheManager.expoVideoCacheScheme, isDirectory: true)
259
- try? FileManager.default.createDirectory(at: videoCacheDir, withIntermediateDirectories: true)
265
+ // Ensure parent directory exists before writing file
266
+ let parentDir = fileUrl.deletingLastPathComponent()
267
+ do {
268
+ try FileManager.default.createDirectory(at: parentDir, withIntermediateDirectories: true)
269
+ } catch {
270
+ print("[expo-video] Failed to create parent directory for cache file: \(error)")
271
+ throw error
260
272
  }
261
273
 
262
274
  // Ensure the file exists before opening for writing
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.0",
4
+ "version": "1.0.3",
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",