stream-chat-expo 9.5.1-beta.4 → 9.5.1-beta.6
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.
|
@@ -113,20 +113,13 @@ public final class StreamVideoThumbnailGenerator: NSObject {
|
|
|
113
113
|
return outputURL.absoluteString
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
-
let
|
|
117
|
-
let generator = AVAssetImageGenerator(asset: asset)
|
|
118
|
-
generator.appliesPreferredTrackTransform = true
|
|
119
|
-
generator.maximumSize = CGSize(width: maxDimension, height: maxDimension)
|
|
116
|
+
let image = try await resolveThumbnailImage(url: url)
|
|
120
117
|
|
|
121
|
-
let
|
|
118
|
+
guard let data = image.jpegData(compressionQuality: compressionQuality) else {
|
|
119
|
+
throw thumbnailError(code: 2, message: "Failed to encode JPEG thumbnail for \(url)")
|
|
120
|
+
}
|
|
122
121
|
|
|
123
122
|
do {
|
|
124
|
-
let cgImage = try generator.copyCGImage(at: requestedTime, actualTime: nil)
|
|
125
|
-
let image = UIImage(cgImage: cgImage)
|
|
126
|
-
guard let data = image.jpegData(compressionQuality: compressionQuality) else {
|
|
127
|
-
throw thumbnailError(code: 2, message: "Failed to encode JPEG thumbnail for \(url)")
|
|
128
|
-
}
|
|
129
|
-
|
|
130
123
|
try data.write(to: outputURL, options: .atomic)
|
|
131
124
|
return outputURL.absoluteString
|
|
132
125
|
} catch {
|
|
@@ -134,6 +127,32 @@ public final class StreamVideoThumbnailGenerator: NSObject {
|
|
|
134
127
|
}
|
|
135
128
|
}
|
|
136
129
|
|
|
130
|
+
private static func resolveThumbnailImage(url: String) async throws -> UIImage {
|
|
131
|
+
if isPhotoLibraryURL(url) {
|
|
132
|
+
return try await resolvePhotoLibraryThumbnail(url: url)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if let normalizedURL = normalizeLocalURL(url) {
|
|
136
|
+
return try generateLocalVideoThumbnail(url: normalizedURL)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
throw thumbnailError(code: 5, message: "Unsupported video URL for thumbnail generation: \(url)")
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
private static func generateLocalVideoThumbnail(url: URL) throws -> UIImage {
|
|
143
|
+
let asset = AVURLAsset(url: url)
|
|
144
|
+
let generator = AVAssetImageGenerator(asset: asset)
|
|
145
|
+
generator.appliesPreferredTrackTransform = true
|
|
146
|
+
generator.maximumSize = CGSize(width: maxDimension, height: maxDimension)
|
|
147
|
+
|
|
148
|
+
do {
|
|
149
|
+
let cgImage = try generator.copyCGImage(at: thumbnailTime(for: asset), actualTime: nil)
|
|
150
|
+
return UIImage(cgImage: cgImage)
|
|
151
|
+
} catch {
|
|
152
|
+
throw thumbnailError(error, code: 3, message: "Thumbnail generation failed for \(url.absoluteString)")
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
137
156
|
private static func thumbnailCacheDirectory() throws -> URL {
|
|
138
157
|
let outputDirectory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0]
|
|
139
158
|
.appendingPathComponent(cacheDirectoryName, isDirectory: true)
|
|
@@ -170,23 +189,11 @@ public final class StreamVideoThumbnailGenerator: NSObject {
|
|
|
170
189
|
return .zero
|
|
171
190
|
}
|
|
172
191
|
|
|
173
|
-
private static func resolveAsset(url: String) async throws -> AVAsset {
|
|
174
|
-
if isPhotoLibraryURL(url) {
|
|
175
|
-
return try await resolvePhotoLibraryAsset(url: url)
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
if let normalizedURL = normalizeLocalURL(url) {
|
|
179
|
-
return AVURLAsset(url: normalizedURL)
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
throw thumbnailError(code: 5, message: "Unsupported video URL for thumbnail generation: \(url)")
|
|
183
|
-
}
|
|
184
|
-
|
|
185
192
|
private static func isPhotoLibraryURL(_ url: String) -> Bool {
|
|
186
193
|
url.lowercased().hasPrefix("ph://")
|
|
187
194
|
}
|
|
188
195
|
|
|
189
|
-
private static func
|
|
196
|
+
private static func resolvePhotoLibraryThumbnail(url: String) async throws -> UIImage {
|
|
190
197
|
let identifier = photoLibraryIdentifier(from: url)
|
|
191
198
|
|
|
192
199
|
guard !identifier.isEmpty else {
|
|
@@ -198,47 +205,65 @@ public final class StreamVideoThumbnailGenerator: NSObject {
|
|
|
198
205
|
throw thumbnailError(code: 7, message: "Failed to find photo library asset for \(url)")
|
|
199
206
|
}
|
|
200
207
|
|
|
201
|
-
|
|
202
|
-
|
|
208
|
+
// Request a thumbnail sized image instead of the full AVAsset: PhotoKit
|
|
209
|
+
// serves a locally cached thumbnail even for iCloud only assets, so this
|
|
210
|
+
// avoids downloading the entire video the way requestAVAsset(forVideo:) would.
|
|
211
|
+
let options = PHImageRequestOptions()
|
|
212
|
+
options.deliveryMode = .fastFormat
|
|
213
|
+
options.resizeMode = .fast
|
|
203
214
|
options.isNetworkAccessAllowed = true
|
|
204
|
-
options.
|
|
215
|
+
options.isSynchronous = false
|
|
205
216
|
|
|
206
|
-
|
|
217
|
+
let targetSize = CGSize(width: maxDimension, height: maxDimension)
|
|
218
|
+
|
|
219
|
+
return try await withThrowingTaskGroup(of: UIImage.self) { group in
|
|
207
220
|
group.addTask {
|
|
208
|
-
try await
|
|
221
|
+
try await requestPhotoLibraryImage(
|
|
222
|
+
url: url,
|
|
223
|
+
asset: asset,
|
|
224
|
+
targetSize: targetSize,
|
|
225
|
+
options: options
|
|
226
|
+
)
|
|
209
227
|
}
|
|
210
228
|
group.addTask {
|
|
211
229
|
try await Task.sleep(nanoseconds: UInt64(photoLibraryAssetResolutionTimeout * 1_000_000_000))
|
|
212
230
|
throw thumbnailError(
|
|
213
231
|
code: 11,
|
|
214
|
-
message: "Timed out resolving photo library
|
|
232
|
+
message: "Timed out resolving photo library thumbnail for \(url)"
|
|
215
233
|
)
|
|
216
234
|
}
|
|
217
235
|
|
|
218
|
-
guard let
|
|
236
|
+
guard let image = try await group.next() else {
|
|
219
237
|
throw thumbnailError(
|
|
220
238
|
code: 10,
|
|
221
|
-
message: "Failed to resolve photo library
|
|
239
|
+
message: "Failed to resolve photo library thumbnail for \(url)"
|
|
222
240
|
)
|
|
223
241
|
}
|
|
224
242
|
|
|
225
243
|
group.cancelAll()
|
|
226
|
-
return
|
|
244
|
+
return image
|
|
227
245
|
}
|
|
228
246
|
}
|
|
229
247
|
|
|
230
|
-
private static func
|
|
248
|
+
private static func requestPhotoLibraryImage(
|
|
231
249
|
url: String,
|
|
232
250
|
asset: PHAsset,
|
|
233
|
-
|
|
234
|
-
|
|
251
|
+
targetSize: CGSize,
|
|
252
|
+
options: PHImageRequestOptions
|
|
253
|
+
) async throws -> UIImage {
|
|
235
254
|
let imageManager = PHImageManager.default()
|
|
236
255
|
let state = StreamPhotoLibraryAssetRequestState()
|
|
237
256
|
|
|
238
257
|
return try await withTaskCancellationHandler(operation: {
|
|
239
258
|
try await withCheckedThrowingContinuation { continuation in
|
|
240
|
-
let requestID = imageManager.
|
|
241
|
-
|
|
259
|
+
let requestID = imageManager.requestImage(
|
|
260
|
+
for: asset,
|
|
261
|
+
targetSize: targetSize,
|
|
262
|
+
contentMode: .aspectFit,
|
|
263
|
+
options: options
|
|
264
|
+
) { image, info in
|
|
265
|
+
// Accept the first delivered image (.fastFormat sends exactly one,
|
|
266
|
+
// thumbnail quality, possibly flagged degraded and that's what we want).
|
|
242
267
|
state.lock.lock()
|
|
243
268
|
if state.didResume {
|
|
244
269
|
state.lock.unlock()
|
|
@@ -251,7 +276,7 @@ public final class StreamVideoThumbnailGenerator: NSObject {
|
|
|
251
276
|
continuation.resume(
|
|
252
277
|
throwing: thumbnailError(
|
|
253
278
|
code: 8,
|
|
254
|
-
message: "Photo library
|
|
279
|
+
message: "Photo library image request was cancelled for \(url)"
|
|
255
280
|
)
|
|
256
281
|
)
|
|
257
282
|
return
|
|
@@ -262,23 +287,23 @@ public final class StreamVideoThumbnailGenerator: NSObject {
|
|
|
262
287
|
throwing: thumbnailError(
|
|
263
288
|
error,
|
|
264
289
|
code: 9,
|
|
265
|
-
message: "Photo library
|
|
290
|
+
message: "Photo library image request failed for \(url)"
|
|
266
291
|
)
|
|
267
292
|
)
|
|
268
293
|
return
|
|
269
294
|
}
|
|
270
295
|
|
|
271
|
-
guard let
|
|
296
|
+
guard let image else {
|
|
272
297
|
continuation.resume(
|
|
273
298
|
throwing: thumbnailError(
|
|
274
299
|
code: 10,
|
|
275
|
-
message: "Failed to resolve photo library
|
|
300
|
+
message: "Failed to resolve photo library thumbnail for \(url)"
|
|
276
301
|
)
|
|
277
302
|
)
|
|
278
303
|
return
|
|
279
304
|
}
|
|
280
305
|
|
|
281
|
-
continuation.resume(returning:
|
|
306
|
+
continuation.resume(returning: image)
|
|
282
307
|
}
|
|
283
308
|
|
|
284
309
|
state.lock.lock()
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stream-chat-expo",
|
|
3
3
|
"description": "The official Expo SDK for Stream Chat, a service for building chat applications",
|
|
4
|
-
"version": "9.5.1-beta.
|
|
4
|
+
"version": "9.5.1-beta.6",
|
|
5
5
|
"author": {
|
|
6
6
|
"company": "Stream.io Inc",
|
|
7
7
|
"name": "Stream.io Inc"
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"types": "types/index.d.ts",
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"mime": "^4.0.7",
|
|
30
|
-
"stream-chat-react-native-core": "9.5.1-beta.
|
|
30
|
+
"stream-chat-react-native-core": "9.5.1-beta.6"
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
|
33
33
|
"expo": ">=52.0.0",
|