rns-mediapicker 0.1.1 → 0.1.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.
package/README.md
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
# rns-mediapicker
|
|
1
|
+
# 🎵 / 🖼️ / 🎥 rns-mediapicker
|
|
2
2
|
|
|
3
3
|
A **high-performance native media picker** for **React Native** and **Expo**.
|
|
4
|
-
Supports **audio, image, and video** selection from **camera or library
|
|
4
|
+
Supports **audio, image, and video** selection from **camera or library**, automatic JPEG compression, EXIF-safe dimensions, and transparent-image flattening.
|
|
5
5
|
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
## 🚀 Installation
|
|
9
9
|
|
|
10
|
-
Choose
|
|
10
|
+
Choose any method:
|
|
11
11
|
|
|
12
12
|
```bash
|
|
13
13
|
yarn add rns-mediapicker
|
|
@@ -123,7 +123,8 @@ class FastMediaPickerModule(
|
|
|
123
123
|
val fallback =
|
|
124
124
|
Intent(Intent.ACTION_GET_CONTENT).apply {
|
|
125
125
|
addCategory(Intent.CATEGORY_OPENABLE)
|
|
126
|
-
type
|
|
126
|
+
// FIXED: Explicitly use this.type to refer to Intent property
|
|
127
|
+
this.type =
|
|
127
128
|
when (type) {
|
|
128
129
|
"video" -> "video/*"
|
|
129
130
|
"image" -> "image/*"
|
|
@@ -190,7 +191,8 @@ class FastMediaPickerModule(
|
|
|
190
191
|
processMedia(uri)
|
|
191
192
|
}
|
|
192
193
|
|
|
193
|
-
override
|
|
194
|
+
// FIXED: Removed 'override' as this signature is no longer in ActivityEventListener
|
|
195
|
+
fun onActivityResult(
|
|
194
196
|
requestCode: Int,
|
|
195
197
|
resultCode: Int,
|
|
196
198
|
data: Intent?,
|
|
@@ -33,7 +33,6 @@ class FastMediaPicker: NSObject,
|
|
|
33
33
|
let typeLower = mediaType.lowercased()
|
|
34
34
|
|
|
35
35
|
DispatchQueue.main.async {
|
|
36
|
-
|
|
37
36
|
// 1. AUDIO (Document Picker)
|
|
38
37
|
if typeLower == "audio" {
|
|
39
38
|
let picker = UIDocumentPickerViewController(forOpeningContentTypes: [.audio])
|
|
@@ -89,7 +88,9 @@ class FastMediaPicker: NSObject,
|
|
|
89
88
|
|
|
90
89
|
// MARK: - PHPicker
|
|
91
90
|
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
|
|
92
|
-
|
|
91
|
+
DispatchQueue.main.async {
|
|
92
|
+
picker.dismiss(animated: true)
|
|
93
|
+
}
|
|
93
94
|
|
|
94
95
|
guard let result = results.first else {
|
|
95
96
|
safeReject("E_CANCELLED", "User cancelled")
|
|
@@ -98,24 +99,31 @@ class FastMediaPicker: NSObject,
|
|
|
98
99
|
|
|
99
100
|
let provider = result.itemProvider
|
|
100
101
|
|
|
102
|
+
// Check for Video
|
|
101
103
|
if provider.hasItemConformingToTypeIdentifier(UTType.movie.identifier) {
|
|
102
104
|
provider.loadFileRepresentation(forTypeIdentifier: UTType.movie.identifier) { [weak self] url, error in
|
|
103
|
-
guard let self = self else { return }
|
|
104
105
|
if let url = url {
|
|
105
|
-
self
|
|
106
|
+
self?.handlePickedVideo(url: url)
|
|
106
107
|
} else {
|
|
107
|
-
self
|
|
108
|
+
self?.safeReject("E_LOAD", error?.localizedDescription ?? "Video load failed")
|
|
108
109
|
}
|
|
109
110
|
}
|
|
110
111
|
return
|
|
111
112
|
}
|
|
112
113
|
|
|
114
|
+
// Check for Image
|
|
113
115
|
if provider.canLoadObject(ofClass: UIImage.self) {
|
|
114
|
-
provider.loadObject(ofClass: UIImage.self) { [weak self] image,
|
|
115
|
-
|
|
116
|
-
|
|
116
|
+
provider.loadObject(ofClass: UIImage.self) { [weak self] image, error in
|
|
117
|
+
if let img = image as? UIImage {
|
|
118
|
+
self?.processPickedImage(img)
|
|
119
|
+
} else {
|
|
120
|
+
self?.safeReject("E_LOAD", error?.localizedDescription ?? "Image load failed")
|
|
121
|
+
}
|
|
117
122
|
}
|
|
123
|
+
return
|
|
118
124
|
}
|
|
125
|
+
|
|
126
|
+
safeReject("E_NO_MEDIA", "Unsupported media type")
|
|
119
127
|
}
|
|
120
128
|
|
|
121
129
|
// MARK: - Document Picker (Audio)
|
|
@@ -137,15 +145,12 @@ class FastMediaPicker: NSObject,
|
|
|
137
145
|
}
|
|
138
146
|
try FileManager.default.copyItem(at: url, to: fileURL)
|
|
139
147
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
])
|
|
147
|
-
self.cleanupPromise()
|
|
148
|
-
}
|
|
148
|
+
self.safeResolve([
|
|
149
|
+
"uri": fileURL.absoluteString,
|
|
150
|
+
"type": "audio",
|
|
151
|
+
"isAudio": true,
|
|
152
|
+
"isVideo": false
|
|
153
|
+
])
|
|
149
154
|
} catch {
|
|
150
155
|
safeReject("E_AUDIO_COPY", error.localizedDescription)
|
|
151
156
|
}
|
|
@@ -183,34 +188,22 @@ class FastMediaPicker: NSObject,
|
|
|
183
188
|
private func processPickedImage(_ image: UIImage) {
|
|
184
189
|
let fileURL = makeTempURL(ext: "jpg")
|
|
185
190
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
format.opaque = true
|
|
189
|
-
|
|
190
|
-
let renderer = UIGraphicsImageRenderer(size: image.size, format: format)
|
|
191
|
-
let output = renderer.image { ctx in
|
|
192
|
-
UIColor.white.setFill()
|
|
193
|
-
ctx.fill(CGRect(origin: .zero, size: image.size))
|
|
194
|
-
image.draw(at: .zero)
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
guard let data = output.jpegData(compressionQuality: 0.8) else {
|
|
191
|
+
// Standardize image orientation and convert to Data
|
|
192
|
+
guard let data = image.jpegData(compressionQuality: 0.8) else {
|
|
198
193
|
safeReject("E_IMAGE_WRITE", "Image encoding failed")
|
|
199
194
|
return
|
|
200
195
|
}
|
|
201
196
|
|
|
202
197
|
do {
|
|
203
198
|
try data.write(to: fileURL)
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
self.cleanupPromise()
|
|
213
|
-
}
|
|
199
|
+
self.safeResolve([
|
|
200
|
+
"uri": fileURL.absoluteString,
|
|
201
|
+
"width": image.size.width,
|
|
202
|
+
"height": image.size.height,
|
|
203
|
+
"type": "image",
|
|
204
|
+
"isAudio": false,
|
|
205
|
+
"isVideo": false
|
|
206
|
+
])
|
|
214
207
|
} catch {
|
|
215
208
|
safeReject("E_IMAGE_WRITE", error.localizedDescription)
|
|
216
209
|
}
|
|
@@ -224,19 +217,18 @@ class FastMediaPicker: NSObject,
|
|
|
224
217
|
if FileManager.default.fileExists(atPath: fileURL.path) {
|
|
225
218
|
try FileManager.default.removeItem(at: fileURL)
|
|
226
219
|
}
|
|
220
|
+
// PHPicker provides a temporary URL that may be deleted; we MUST copy it
|
|
227
221
|
try FileManager.default.copyItem(at: url, to: fileURL)
|
|
228
222
|
|
|
229
223
|
let dims = videoDimensions(for: fileURL)
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
self.cleanupPromise()
|
|
239
|
-
}
|
|
224
|
+
self.safeResolve([
|
|
225
|
+
"uri": fileURL.absoluteString,
|
|
226
|
+
"width": dims.width,
|
|
227
|
+
"height": dims.height,
|
|
228
|
+
"type": "video",
|
|
229
|
+
"isAudio": false,
|
|
230
|
+
"isVideo": true
|
|
231
|
+
])
|
|
240
232
|
} catch {
|
|
241
233
|
safeReject("E_VIDEO_COPY", error.localizedDescription)
|
|
242
234
|
}
|
|
@@ -247,6 +239,13 @@ class FastMediaPicker: NSObject,
|
|
|
247
239
|
safeReject("E_CANCELLED", "User cancelled via swipe")
|
|
248
240
|
}
|
|
249
241
|
|
|
242
|
+
private func safeResolve(_ result: [String: Any]) {
|
|
243
|
+
DispatchQueue.main.async {
|
|
244
|
+
self.resolve?(result)
|
|
245
|
+
self.cleanupPromise()
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
250
249
|
private func safeReject(_ code: String, _ message: String) {
|
|
251
250
|
DispatchQueue.main.async {
|
|
252
251
|
self.reject?(code, message, nil)
|
|
@@ -285,4 +284,4 @@ class FastMediaPicker: NSObject,
|
|
|
285
284
|
let size = track.naturalSize.applying(track.preferredTransform)
|
|
286
285
|
return (abs(size.width), abs(size.height))
|
|
287
286
|
}
|
|
288
|
-
}
|
|
287
|
+
}
|