react-native-rn-media-compressor 0.1.0

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/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Siddesh7972
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the "Software"), to deal
6
+ in the Software without restriction, including without limitation the rights
7
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is
9
+ furnished to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all
12
+ copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,37 @@
1
+ # react-native-rn-media-compressor
2
+
3
+ This package will reduce the size of media file before uplaoding it to server
4
+
5
+ ## Installation
6
+
7
+
8
+ ```sh
9
+ npm install react-native-rn-media-compressor
10
+ ```
11
+
12
+
13
+ ## Usage
14
+
15
+
16
+ ```js
17
+ import { multiply } from 'react-native-rn-media-compressor';
18
+
19
+ // ...
20
+
21
+ const result = await multiply(3, 7);
22
+ ```
23
+
24
+
25
+ ## Contributing
26
+
27
+ - [Development workflow](CONTRIBUTING.md#development-workflow)
28
+ - [Sending a pull request](CONTRIBUTING.md#sending-a-pull-request)
29
+ - [Code of conduct](CODE_OF_CONDUCT.md)
30
+
31
+ ## License
32
+
33
+ MIT
34
+
35
+ ---
36
+
37
+ Made with [create-react-native-library](https://github.com/callstack/react-native-builder-bob)
@@ -0,0 +1,42 @@
1
+ buildscript {
2
+ ext {
3
+ buildToolsVersion = "34.0.0"
4
+ minSdkVersion = 21
5
+ compileSdkVersion = 34
6
+ targetSdkVersion = 34
7
+ ndkVersion = "25.1.8937393"
8
+ kotlinVersion = "1.8.0"
9
+ }
10
+ repositories {
11
+ google()
12
+ mavenCentral()
13
+ }
14
+ dependencies {
15
+ classpath("com.android.tools.build:gradle")
16
+ classpath("com.facebook.react:react-native-gradle-plugin")
17
+ }
18
+ }
19
+
20
+ apply plugin: "com.android.library"
21
+ apply plugin: "com.facebook.react-native"
22
+
23
+ android {
24
+ ndkVersion rootProject.ext.ndkVersion
25
+ compileSdkVersion rootProject.ext.compileSdkVersion
26
+
27
+ namespace "com.rnmediacompressor"
28
+ defaultConfig {
29
+ minSdkVersion rootProject.ext.minSdkVersion
30
+ targetSdkVersion rootProject.ext.targetSdkVersion
31
+ }
32
+ }
33
+
34
+ repositories {
35
+ mavenCentral()
36
+ google()
37
+ maven { url "https://www.jitpack.io" }
38
+ }
39
+
40
+ dependencies {
41
+ implementation "com.facebook.react:react-native"
42
+ }
@@ -0,0 +1,2 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2
+ </manifest>
@@ -0,0 +1,206 @@
1
+ package com.rnmediacompressor;
2
+
3
+ import android.graphics.Bitmap;
4
+ import android.graphics.BitmapFactory;
5
+ import android.media.MediaMetadataRetriever;
6
+ import android.util.Log;
7
+
8
+ import androidx.annotation.NonNull;
9
+
10
+ import com.facebook.react.bridge.Arguments;
11
+ import com.facebook.react.bridge.Promise;
12
+ import com.facebook.react.bridge.ReactApplicationContext;
13
+ import com.facebook.react.bridge.ReactContextBaseJavaModule;
14
+ import com.facebook.react.bridge.ReactMethod;
15
+ import com.facebook.react.bridge.ReadableMap;
16
+ import com.facebook.react.bridge.WritableMap;
17
+
18
+ import java.io.File;
19
+ import java.io.FileOutputStream;
20
+ import java.util.concurrent.ExecutorService;
21
+ import java.util.concurrent.Executors;
22
+
23
+ public class RnMediaCompressorModule extends ReactContextBaseJavaModule {
24
+ private static final String MODULE_NAME = "RnMediaCompressor";
25
+ private static final String TAG = "RnMediaCompressor";
26
+ private final ExecutorService executorService = Executors.newSingleThreadExecutor();
27
+
28
+ public RnMediaCompressorModule(ReactApplicationContext reactContext) {
29
+ super(reactContext);
30
+ }
31
+
32
+ @Override
33
+ public void onCatalystInstanceDestroy() {
34
+ super.onCatalystInstanceDestroy();
35
+ executorService.shutdown();
36
+ }
37
+
38
+ @NonNull
39
+ @Override
40
+ public String getName() {
41
+ return MODULE_NAME;
42
+ }
43
+
44
+ @ReactMethod
45
+ public void compressImage(String filePath, ReadableMap options, Promise promise) {
46
+ executorService.execute(() -> {
47
+ compressImageAsync(filePath, options, promise);
48
+ });
49
+ }
50
+
51
+ @ReactMethod
52
+ public void compressVideo(String filePath, ReadableMap options, Promise promise) {
53
+ promise.reject("NOT_IMPLEMENTED", "Video compression is not yet implemented for Android");
54
+ }
55
+
56
+ @ReactMethod
57
+ public void getMediaInfo(String filePath, Promise promise) {
58
+ try {
59
+ File file = new File(filePath);
60
+ if (!file.exists()) {
61
+ promise.reject("FILE_NOT_FOUND", "File does not exist: " + filePath);
62
+ return;
63
+ }
64
+
65
+ WritableMap info = Arguments.createMap();
66
+ info.putDouble("size", file.length());
67
+
68
+ // Try to get image dimensions
69
+ BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
70
+ bitmapOptions.inJustDecodeBounds = true;
71
+ BitmapFactory.decodeFile(filePath, bitmapOptions);
72
+
73
+ if (bitmapOptions.outWidth > 0 && bitmapOptions.outHeight > 0) {
74
+ info.putInt("width", bitmapOptions.outWidth);
75
+ info.putInt("height", bitmapOptions.outHeight);
76
+ info.putString("mimeType", bitmapOptions.outMimeType);
77
+ } else {
78
+ // Try video
79
+ MediaMetadataRetriever retriever = new MediaMetadataRetriever();
80
+ try {
81
+ retriever.setDataSource(filePath);
82
+ String widthStr = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH);
83
+ String heightStr = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT);
84
+ String durationStr = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
85
+ String mimeType = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_MIMETYPE);
86
+
87
+ if (widthStr != null) {
88
+ info.putInt("width", Integer.parseInt(widthStr));
89
+ }
90
+ if (heightStr != null) {
91
+ info.putInt("height", Integer.parseInt(heightStr));
92
+ }
93
+ if (durationStr != null) {
94
+ info.putDouble("duration", Double.parseDouble(durationStr) / 1000.0);
95
+ }
96
+ if (mimeType != null) {
97
+ info.putString("mimeType", mimeType);
98
+ }
99
+ } catch (Exception e) {
100
+ Log.e(TAG, "Error getting video metadata", e);
101
+ } finally {
102
+ retriever.release();
103
+ }
104
+ }
105
+
106
+ promise.resolve(info);
107
+ } catch (Exception e) {
108
+ promise.reject("ERROR", "Failed to get media info: " + e.getMessage(), e);
109
+ }
110
+ }
111
+
112
+ private void compressImageAsync(String filePath, ReadableMap options, Promise promise) {
113
+ try {
114
+ File inputFile = new File(filePath);
115
+ if (!inputFile.exists()) {
116
+ promise.reject("FILE_NOT_FOUND", "File does not exist: " + filePath);
117
+ return;
118
+ }
119
+
120
+ long originalSize = inputFile.length();
121
+
122
+ // Read options
123
+ int maxWidth = options.hasKey("maxWidth") ? options.getInt("maxWidth") : 0;
124
+ int maxHeight = options.hasKey("maxHeight") ? options.getInt("maxHeight") : 0;
125
+ float quality = options.hasKey("quality") ? (float) options.getDouble("quality") : 0.8f;
126
+ String outputFormat = options.hasKey("outputFormat") ? options.getString("outputFormat") : "jpeg";
127
+
128
+ // Load bitmap
129
+ Bitmap bitmap = BitmapFactory.decodeFile(filePath);
130
+ if (bitmap == null) {
131
+ promise.reject("DECODE_ERROR", "Failed to decode image file");
132
+ return;
133
+ }
134
+
135
+ int originalWidth = bitmap.getWidth();
136
+ int originalHeight = bitmap.getHeight();
137
+
138
+ // Calculate new dimensions
139
+ int newWidth = originalWidth;
140
+ int newHeight = originalHeight;
141
+
142
+ if (maxWidth > 0 || maxHeight > 0) {
143
+ float widthRatio = maxWidth > 0 ? (float) maxWidth / originalWidth : Float.MAX_VALUE;
144
+ float heightRatio = maxHeight > 0 ? (float) maxHeight / originalHeight : Float.MAX_VALUE;
145
+ float ratio = Math.min(widthRatio, heightRatio);
146
+
147
+ if (ratio < 1.0f) {
148
+ newWidth = Math.round(originalWidth * ratio);
149
+ newHeight = Math.round(originalHeight * ratio);
150
+ }
151
+ }
152
+
153
+ // Resize if needed
154
+ Bitmap resizedBitmap = bitmap;
155
+ if (newWidth != originalWidth || newHeight != originalHeight) {
156
+ resizedBitmap = Bitmap.createScaledBitmap(bitmap, newWidth, newHeight, true);
157
+ if (resizedBitmap != bitmap) {
158
+ bitmap.recycle();
159
+ }
160
+ }
161
+
162
+ // Create output file
163
+ String outputPath = filePath;
164
+ if (filePath.contains(".")) {
165
+ String extension = outputFormat.equals("png") ? ".png" : ".jpg";
166
+ outputPath = filePath.substring(0, filePath.lastIndexOf(".")) + "_compressed" + extension;
167
+ } else {
168
+ outputPath = filePath + "_compressed." + (outputFormat.equals("png") ? "png" : "jpg");
169
+ }
170
+
171
+ File outputFile = new File(outputPath);
172
+
173
+ // Compress and save
174
+ FileOutputStream fos = new FileOutputStream(outputFile);
175
+ Bitmap.CompressFormat format = outputFormat.equals("png")
176
+ ? Bitmap.CompressFormat.PNG
177
+ : Bitmap.CompressFormat.JPEG;
178
+
179
+ resizedBitmap.compress(format, Math.round(quality * 100), fos);
180
+ fos.flush();
181
+ fos.close();
182
+
183
+ if (resizedBitmap != bitmap) {
184
+ resizedBitmap.recycle();
185
+ }
186
+
187
+ long compressedSize = outputFile.length();
188
+ double compressionRatio = originalSize > 0
189
+ ? (double) compressedSize / originalSize
190
+ : 1.0;
191
+
192
+ WritableMap result = Arguments.createMap();
193
+ result.putString("path", outputFile.getAbsolutePath());
194
+ result.putDouble("size", compressedSize);
195
+ result.putDouble("originalSize", originalSize);
196
+ result.putDouble("compressionRatio", compressionRatio);
197
+ result.putInt("width", newWidth);
198
+ result.putInt("height", newHeight);
199
+
200
+ promise.resolve(result);
201
+ } catch (Exception e) {
202
+ promise.reject("COMPRESSION_ERROR", "Failed to compress image: " + e.getMessage(), e);
203
+ }
204
+ }
205
+ }
206
+
@@ -0,0 +1,29 @@
1
+ package com.rnmediacompressor;
2
+
3
+ import androidx.annotation.NonNull;
4
+
5
+ import com.facebook.react.ReactPackage;
6
+ import com.facebook.react.bridge.NativeModule;
7
+ import com.facebook.react.bridge.ReactApplicationContext;
8
+ import com.facebook.react.uimanager.ViewManager;
9
+
10
+ import java.util.ArrayList;
11
+ import java.util.Collections;
12
+ import java.util.List;
13
+
14
+ public class RnMediaCompressorPackage implements ReactPackage {
15
+ @NonNull
16
+ @Override
17
+ public List<NativeModule> createNativeModules(@NonNull ReactApplicationContext reactContext) {
18
+ List<NativeModule> modules = new ArrayList<>();
19
+ modules.add(new RnMediaCompressorModule(reactContext));
20
+ return modules;
21
+ }
22
+
23
+ @NonNull
24
+ @Override
25
+ public List<ViewManager> createViewManagers(@NonNull ReactApplicationContext reactContext) {
26
+ return Collections.emptyList();
27
+ }
28
+ }
29
+
@@ -0,0 +1,6 @@
1
+ #import <React/RCTBridgeModule.h>
2
+ #import <React/RCTEventEmitter.h>
3
+
4
+ @interface RnMediaCompressor : NSObject <RCTBridgeModule>
5
+
6
+ @end
@@ -0,0 +1,343 @@
1
+ #import "RnMediaCompressor.h"
2
+ #import <UIKit/UIKit.h>
3
+ #import <AVFoundation/AVFoundation.h>
4
+ #import <ImageIO/ImageIO.h>
5
+ #import <MobileCoreServices/MobileCoreServices.h>
6
+
7
+ @implementation RnMediaCompressor
8
+
9
+ RCT_EXPORT_MODULE(RnMediaCompressor)
10
+
11
+ RCT_EXPORT_METHOD(compressImage:(NSString *)filePath
12
+ options:(NSDictionary *)options
13
+ resolver:(RCTPromiseResolveBlock)resolve
14
+ rejecter:(RCTPromiseRejectBlock)reject)
15
+ {
16
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
17
+ @try {
18
+ NSFileManager *fileManager = [NSFileManager defaultManager];
19
+ if (![fileManager fileExistsAtPath:filePath]) {
20
+ reject(@"FILE_NOT_FOUND", [NSString stringWithFormat:@"File does not exist: %@", filePath], nil);
21
+ return;
22
+ }
23
+
24
+ // Get original file size
25
+ NSDictionary *fileAttributes = [fileManager attributesOfItemAtPath:filePath error:nil];
26
+ NSNumber *originalSize = [fileAttributes objectForKey:NSFileSize];
27
+
28
+ // Read options
29
+ NSNumber *maxWidth = options[@"maxWidth"] ? options[@"maxWidth"] : nil;
30
+ NSNumber *maxHeight = options[@"maxHeight"] ? options[@"maxHeight"] : nil;
31
+ NSNumber *quality = options[@"quality"] ? options[@"quality"] : @0.8;
32
+ NSString *outputFormat = options[@"outputFormat"] ? options[@"outputFormat"] : @"jpeg";
33
+
34
+ // Load image
35
+ UIImage *image = [UIImage imageWithContentsOfFile:filePath];
36
+ if (!image) {
37
+ reject(@"DECODE_ERROR", @"Failed to decode image file", nil);
38
+ return;
39
+ }
40
+
41
+ CGFloat originalWidth = image.size.width;
42
+ CGFloat originalHeight = image.size.height;
43
+
44
+ // Calculate new dimensions
45
+ CGFloat newWidth = originalWidth;
46
+ CGFloat newHeight = originalHeight;
47
+
48
+ if (maxWidth || maxHeight) {
49
+ CGFloat widthRatio = maxWidth ? [maxWidth floatValue] / originalWidth : CGFLOAT_MAX;
50
+ CGFloat heightRatio = maxHeight ? [maxHeight floatValue] / originalHeight : CGFLOAT_MAX;
51
+ CGFloat ratio = MIN(widthRatio, heightRatio);
52
+
53
+ if (ratio < 1.0) {
54
+ newWidth = originalWidth * ratio;
55
+ newHeight = originalHeight * ratio;
56
+ }
57
+ }
58
+
59
+ // Resize image
60
+ UIImage *resizedImage = image;
61
+ if (newWidth != originalWidth || newHeight != originalHeight) {
62
+ CGSize newSize = CGSizeMake(newWidth, newHeight);
63
+ UIGraphicsBeginImageContextWithOptions(newSize, NO, 1.0);
64
+ [image drawInRect:CGRectMake(0, 0, newWidth, newHeight)];
65
+ resizedImage = UIGraphicsGetImageFromCurrentImageContext();
66
+ UIGraphicsEndImageContext();
67
+ }
68
+
69
+ // Create output path
70
+ NSString *outputPath = filePath;
71
+ if ([filePath containsString:@"."]) {
72
+ NSString *extension = [outputFormat isEqualToString:@"png"] ? @".png" : @".jpg";
73
+ NSRange range = [filePath rangeOfString:@"." options:NSBackwardsSearch];
74
+ outputPath = [[filePath substringToIndex:range.location] stringByAppendingString:@"_compressed"];
75
+ outputPath = [outputPath stringByAppendingString:extension];
76
+ } else {
77
+ NSString *extension = [outputFormat isEqualToString:@"png"] ? @".png" : @".jpg";
78
+ outputPath = [filePath stringByAppendingString:@"_compressed"];
79
+ outputPath = [outputPath stringByAppendingString:extension];
80
+ }
81
+
82
+ // Compress and save
83
+ NSData *imageData;
84
+ if ([outputFormat isEqualToString:@"png"]) {
85
+ imageData = UIImagePNGRepresentation(resizedImage);
86
+ } else {
87
+ imageData = UIImageJPEGRepresentation(resizedImage, [quality floatValue]);
88
+ }
89
+
90
+ if (!imageData) {
91
+ reject(@"COMPRESSION_ERROR", @"Failed to compress image", nil);
92
+ return;
93
+ }
94
+
95
+ BOOL success = [imageData writeToFile:outputPath atomically:YES];
96
+ if (!success) {
97
+ reject(@"WRITE_ERROR", @"Failed to write compressed image to file", nil);
98
+ return;
99
+ }
100
+
101
+ // Get compressed file size
102
+ NSDictionary *compressedAttributes = [fileManager attributesOfItemAtPath:outputPath error:nil];
103
+ NSNumber *compressedSize = [compressedAttributes objectForKey:NSFileSize];
104
+
105
+ double compressionRatio = [originalSize doubleValue] > 0
106
+ ? [compressedSize doubleValue] / [originalSize doubleValue]
107
+ : 1.0;
108
+
109
+ NSDictionary *result = @{
110
+ @"path": outputPath,
111
+ @"size": compressedSize,
112
+ @"originalSize": originalSize,
113
+ @"compressionRatio": @(compressionRatio),
114
+ @"width": @(newWidth),
115
+ @"height": @(newHeight)
116
+ };
117
+
118
+ resolve(result);
119
+ } @catch (NSException *exception) {
120
+ reject(@"COMPRESSION_ERROR", exception.reason, nil);
121
+ }
122
+ });
123
+ }
124
+
125
+ RCT_EXPORT_METHOD(compressVideo:(NSString *)filePath
126
+ options:(NSDictionary *)options
127
+ resolver:(RCTPromiseResolveBlock)resolve
128
+ rejecter:(RCTPromiseRejectBlock)reject)
129
+ {
130
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
131
+ @try {
132
+ NSFileManager *fileManager = [NSFileManager defaultManager];
133
+ if (![fileManager fileExistsAtPath:filePath]) {
134
+ reject(@"FILE_NOT_FOUND", [NSString stringWithFormat:@"File does not exist: %@", filePath], nil);
135
+ return;
136
+ }
137
+
138
+ // Get original file size
139
+ NSDictionary *fileAttributes = [fileManager attributesOfItemAtPath:filePath error:nil];
140
+ NSNumber *originalSize = [fileAttributes objectForKey:NSFileSize];
141
+
142
+ AVAsset *asset = [AVAsset assetWithURL:[NSURL fileURLWithPath:filePath]];
143
+ if (!asset) {
144
+ reject(@"DECODE_ERROR", @"Failed to load video file", nil);
145
+ return;
146
+ }
147
+
148
+ // Read options
149
+ NSNumber *maxWidth = options[@"maxWidth"];
150
+ NSNumber *maxHeight = options[@"maxHeight"];
151
+ NSNumber *bitrate = options[@"bitrate"];
152
+ NSNumber *quality = options[@"quality"]; // 0.0 - 1.0
153
+
154
+ AVAssetTrack *videoTrack = [[asset tracksWithMediaType:AVMediaTypeVideo] firstObject];
155
+ if (!videoTrack) {
156
+ reject(@"DECODE_ERROR", @"No video track found", nil);
157
+ return;
158
+ }
159
+
160
+ CGSize originalSize_video = videoTrack.naturalSize;
161
+ CGFloat originalWidth = originalSize_video.width;
162
+ CGFloat originalHeight = originalSize_video.height;
163
+
164
+ // Calculate new dimensions
165
+ CGFloat newWidth = originalWidth;
166
+ CGFloat newHeight = originalHeight;
167
+
168
+ if (maxWidth || maxHeight) {
169
+ CGFloat widthRatio = maxWidth ? [maxWidth floatValue] / originalWidth : CGFLOAT_MAX;
170
+ CGFloat heightRatio = maxHeight ? [maxHeight floatValue] / originalHeight : CGFLOAT_MAX;
171
+ CGFloat ratio = MIN(widthRatio, heightRatio);
172
+
173
+ if (ratio < 1.0) {
174
+ newWidth = originalWidth * ratio;
175
+ newHeight = originalHeight * ratio;
176
+ }
177
+ }
178
+
179
+ // Create output path
180
+ NSString *outputPath = filePath;
181
+ if ([filePath containsString:@"."]) {
182
+ NSRange range = [filePath rangeOfString:@"." options:NSBackwardsSearch];
183
+ outputPath = [[filePath substringToIndex:range.location] stringByAppendingString:@"_compressed"];
184
+ outputPath = [outputPath stringByAppendingString:@".mp4"];
185
+ } else {
186
+ outputPath = [filePath stringByAppendingString:@"_compressed.mp4"];
187
+ }
188
+
189
+ // Remove existing file if it exists
190
+ if ([fileManager fileExistsAtPath:outputPath]) {
191
+ [fileManager removeItemAtPath:outputPath error:nil];
192
+ }
193
+
194
+ // Choose export preset based on quality / bitrate
195
+ NSString *presetName = AVAssetExportPresetMediumQuality;
196
+
197
+ if (bitrate != nil) {
198
+ // Rough mapping of target bitrate to presets
199
+ double targetBitrate = [bitrate doubleValue];
200
+ // These thresholds are approximate and device dependent
201
+ if (targetBitrate <= 1000000.0) { // <= 1 Mbps
202
+ presetName = AVAssetExportPresetLowQuality;
203
+ } else if (targetBitrate >= 5000000.0) { // >= 5 Mbps
204
+ presetName = AVAssetExportPresetHighestQuality;
205
+ } else {
206
+ presetName = AVAssetExportPresetMediumQuality;
207
+ }
208
+ } else if (quality != nil) {
209
+ CGFloat q = [quality floatValue];
210
+ if (q <= 0.4f) {
211
+ presetName = AVAssetExportPresetLowQuality;
212
+ } else if (q >= 0.85f) {
213
+ presetName = AVAssetExportPresetHighestQuality;
214
+ } else {
215
+ presetName = AVAssetExportPresetMediumQuality;
216
+ }
217
+ }
218
+
219
+ // Create export session
220
+ AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:asset presetName:presetName];
221
+ exportSession.outputURL = [NSURL fileURLWithPath:outputPath];
222
+ exportSession.outputFileType = AVFileTypeMPEG4;
223
+ exportSession.shouldOptimizeForNetworkUse = YES;
224
+
225
+ // Set video composition for resizing
226
+ if (newWidth != originalWidth || newHeight != originalHeight) {
227
+ AVMutableVideoComposition *videoComposition = [AVMutableVideoComposition videoComposition];
228
+ videoComposition.renderSize = CGSizeMake(newWidth, newHeight);
229
+ videoComposition.frameDuration = CMTimeMake(1, 30);
230
+
231
+ AVMutableVideoCompositionInstruction *instruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
232
+ instruction.timeRange = CMTimeRangeMake(kCMTimeZero, asset.duration);
233
+
234
+ AVMutableVideoCompositionLayerInstruction *layerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:videoTrack];
235
+ CGAffineTransform transform = CGAffineTransformMakeScale(newWidth / originalWidth, newHeight / originalHeight);
236
+ [layerInstruction setTransform:transform atTime:kCMTimeZero];
237
+
238
+ instruction.layerInstructions = @[layerInstruction];
239
+ videoComposition.instructions = @[instruction];
240
+
241
+ exportSession.videoComposition = videoComposition;
242
+ }
243
+
244
+ // Export video
245
+ dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
246
+ __block NSError *exportError = nil;
247
+
248
+ [exportSession exportAsynchronouslyWithCompletionHandler:^{
249
+ exportError = exportSession.error;
250
+ dispatch_semaphore_signal(semaphore);
251
+ }];
252
+
253
+ dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
254
+
255
+ if (exportError) {
256
+ reject(@"COMPRESSION_ERROR", [NSString stringWithFormat:@"Video compression failed: %@", exportError.localizedDescription], exportError);
257
+ return;
258
+ }
259
+
260
+ // Get compressed file size
261
+ NSDictionary *compressedAttributes = [fileManager attributesOfItemAtPath:outputPath error:nil];
262
+ NSNumber *compressedSize = [compressedAttributes objectForKey:NSFileSize];
263
+
264
+ double compressionRatio = [originalSize doubleValue] > 0
265
+ ? [compressedSize doubleValue] / [originalSize doubleValue]
266
+ : 1.0;
267
+
268
+ NSDictionary *result = @{
269
+ @"path": outputPath,
270
+ @"size": compressedSize,
271
+ @"originalSize": originalSize,
272
+ @"compressionRatio": @(compressionRatio),
273
+ @"width": @(newWidth),
274
+ @"height": @(newHeight)
275
+ };
276
+
277
+ resolve(result);
278
+ } @catch (NSException *exception) {
279
+ reject(@"COMPRESSION_ERROR", exception.reason, nil);
280
+ }
281
+ });
282
+ }
283
+
284
+ RCT_EXPORT_METHOD(getMediaInfo:(NSString *)filePath
285
+ resolver:(RCTPromiseResolveBlock)resolve
286
+ rejecter:(RCTPromiseRejectBlock)reject)
287
+ {
288
+ @try {
289
+ NSFileManager *fileManager = [NSFileManager defaultManager];
290
+ if (![fileManager fileExistsAtPath:filePath]) {
291
+ reject(@"FILE_NOT_FOUND", [NSString stringWithFormat:@"File does not exist: %@", filePath], nil);
292
+ return;
293
+ }
294
+
295
+ NSDictionary *fileAttributes = [fileManager attributesOfItemAtPath:filePath error:nil];
296
+ NSNumber *fileSize = [fileAttributes objectForKey:NSFileSize];
297
+
298
+ // Try to get image info
299
+ UIImage *image = [UIImage imageWithContentsOfFile:filePath];
300
+ if (image) {
301
+ NSDictionary *result = @{
302
+ @"width": @(image.size.width),
303
+ @"height": @(image.size.height),
304
+ @"size": fileSize
305
+ };
306
+ resolve(result);
307
+ return;
308
+ }
309
+
310
+ // Try to get video info
311
+ AVAsset *asset = [AVAsset assetWithURL:[NSURL fileURLWithPath:filePath]];
312
+ if (asset) {
313
+ AVAssetTrack *videoTrack = [[asset tracksWithMediaType:AVMediaTypeVideo] firstObject];
314
+ if (videoTrack) {
315
+ CGSize size = videoTrack.naturalSize;
316
+ CMTime duration = asset.duration;
317
+ double durationSeconds = CMTimeGetSeconds(duration);
318
+
319
+ NSMutableDictionary *result = [NSMutableDictionary dictionaryWithDictionary:@{
320
+ @"width": @(size.width),
321
+ @"height": @(size.height),
322
+ @"size": fileSize,
323
+ @"duration": @(durationSeconds)
324
+ }];
325
+
326
+ // Try to get MIME type
327
+ NSArray *formats = [asset availableMediaCharacteristicsWithMediaSelectionOptions];
328
+ if (formats.count > 0) {
329
+ result[@"mimeType"] = @"video/mp4";
330
+ }
331
+
332
+ resolve(result);
333
+ return;
334
+ }
335
+ }
336
+
337
+ reject(@"UNSUPPORTED_FORMAT", @"Unable to determine media type", nil);
338
+ } @catch (NSException *exception) {
339
+ reject(@"ERROR", exception.reason, nil);
340
+ }
341
+ }
342
+
343
+ @end
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+
3
+ import { NativeModules, Platform } from 'react-native';
4
+
5
+ // Lazy access to native module - check when function is called, not at module load
6
+ const getNativeModule = () => {
7
+ try {
8
+ // Try direct access
9
+ if (NativeModules.RnMediaCompressor) {
10
+ return NativeModules.RnMediaCompressor;
11
+ }
12
+ // Try accessing via the module name
13
+ const module = NativeModules['RnMediaCompressor'];
14
+ if (module) {
15
+ return module;
16
+ }
17
+ // Log available modules for debugging
18
+ if (__DEV__) {
19
+ console.log('Available native modules:', Object.keys(NativeModules));
20
+ }
21
+ return null;
22
+ } catch (error) {
23
+ if (__DEV__) {
24
+ console.error('Error accessing native module:', error);
25
+ }
26
+ return null;
27
+ }
28
+ };
29
+ /**
30
+ * Compress an image file
31
+ * @param filePath - Path to the image file
32
+ * @param options - Compression options
33
+ * @returns Promise with compression result
34
+ */
35
+ export async function compressImage(filePath, options = {}) {
36
+ const RnMediaCompressor = getNativeModule();
37
+ if (!RnMediaCompressor) {
38
+ const errorMessage = 'RnMediaCompressor native module is not available. ' + 'Make sure you have:\n' + '1. Run "yarn prepare" or "npm run prepare" to build the library\n' + '2. For iOS: Run "cd ios && pod install"\n' + '3. Rebuild your app (not just reload)\n' + '4. If using Expo: Use a development build (not Expo Go)';
39
+ throw new Error(errorMessage);
40
+ }
41
+ if (!filePath) {
42
+ throw new Error('File path is required');
43
+ }
44
+ return RnMediaCompressor.compressImage(filePath, options);
45
+ }
46
+
47
+ /**
48
+ * Compress a video file
49
+ * @param filePath - Path to the video file
50
+ * @param options - Compression options
51
+ * @returns Promise with compression result
52
+ */
53
+ export async function compressVideo(filePath, options = {}) {
54
+ const RnMediaCompressor = getNativeModule();
55
+ if (!RnMediaCompressor) {
56
+ const errorMessage = 'RnMediaCompressor native module is not available. ' + 'Make sure you have:\n' + '1. Run "yarn prepare" or "npm run prepare" to build the library\n' + '2. For iOS: Run "cd ios && pod install"\n' + '3. Rebuild your app (not just reload)\n' + '4. If using Expo: Use a development build (not Expo Go)';
57
+ throw new Error(errorMessage);
58
+ }
59
+ if (!filePath) {
60
+ throw new Error('File path is required');
61
+ }
62
+ if (Platform.OS === 'android') {
63
+ throw new Error('Video compression is not yet implemented for Android');
64
+ }
65
+ return RnMediaCompressor.compressVideo(filePath, options);
66
+ }
67
+
68
+ /**
69
+ * Get media file information
70
+ * @param filePath - Path to the media file
71
+ * @returns Promise with media information
72
+ */
73
+ export async function getMediaInfo(filePath) {
74
+ const RnMediaCompressor = getNativeModule();
75
+ if (!RnMediaCompressor) {
76
+ const errorMessage = 'RnMediaCompressor native module is not available. ' + 'Make sure you have:\n' + '1. Run "yarn prepare" or "npm run prepare" to build the library\n' + '2. For iOS: Run "cd ios && pod install"\n' + '3. Rebuild your app (not just reload)\n' + '4. If using Expo: Use a development build (not Expo Go)';
77
+ throw new Error(errorMessage);
78
+ }
79
+ if (!filePath) {
80
+ throw new Error('File path is required');
81
+ }
82
+ return RnMediaCompressor.getMediaInfo(filePath);
83
+ }
84
+ export default {
85
+ compressImage,
86
+ compressVideo,
87
+ getMediaInfo
88
+ };
89
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["NativeModules","Platform","getNativeModule","RnMediaCompressor","module","__DEV__","console","log","Object","keys","error","compressImage","filePath","options","errorMessage","Error","compressVideo","OS","getMediaInfo"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,SAASA,aAAa,EAAEC,QAAQ,QAAQ,cAAc;;AAEtD;AACA,MAAMC,eAAe,GAAGA,CAAA,KAAM;EAC5B,IAAI;IACF;IACA,IAAIF,aAAa,CAACG,iBAAiB,EAAE;MACnC,OAAOH,aAAa,CAACG,iBAAiB;IACxC;IACA;IACA,MAAMC,MAAM,GAAGJ,aAAa,CAAC,mBAAmB,CAAC;IACjD,IAAII,MAAM,EAAE;MACV,OAAOA,MAAM;IACf;IACA;IACA,IAAIC,OAAO,EAAE;MACXC,OAAO,CAACC,GAAG,CAAC,2BAA2B,EAAEC,MAAM,CAACC,IAAI,CAACT,aAAa,CAAC,CAAC;IACtE;IACA,OAAO,IAAI;EACb,CAAC,CAAC,OAAOU,KAAK,EAAE;IACd,IAAIL,OAAO,EAAE;MACXC,OAAO,CAACI,KAAK,CAAC,gCAAgC,EAAEA,KAAK,CAAC;IACxD;IACA,OAAO,IAAI;EACb;AACF,CAAC;AAiCD;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,eAAeC,aAAaA,CACjCC,QAAgB,EAChBC,OAA6B,GAAG,CAAC,CAAC,EACN;EAC5B,MAAMV,iBAAiB,GAAGD,eAAe,CAAC,CAAC;EAE3C,IAAI,CAACC,iBAAiB,EAAE;IACtB,MAAMW,YAAY,GAChB,oDAAoD,GACpD,uBAAuB,GACvB,mEAAmE,GACnE,2CAA2C,GAC3C,yCAAyC,GACzC,yDAAyD;IAC3D,MAAM,IAAIC,KAAK,CAACD,YAAY,CAAC;EAC/B;EAEA,IAAI,CAACF,QAAQ,EAAE;IACb,MAAM,IAAIG,KAAK,CAAC,uBAAuB,CAAC;EAC1C;EAEA,OAAOZ,iBAAiB,CAACQ,aAAa,CAACC,QAAQ,EAAEC,OAAO,CAAC;AAC3D;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,eAAeG,aAAaA,CACjCJ,QAAgB,EAChBC,OAA6B,GAAG,CAAC,CAAC,EACN;EAC5B,MAAMV,iBAAiB,GAAGD,eAAe,CAAC,CAAC;EAE3C,IAAI,CAACC,iBAAiB,EAAE;IACtB,MAAMW,YAAY,GAChB,oDAAoD,GACpD,uBAAuB,GACvB,mEAAmE,GACnE,2CAA2C,GAC3C,yCAAyC,GACzC,yDAAyD;IAC3D,MAAM,IAAIC,KAAK,CAACD,YAAY,CAAC;EAC/B;EAEA,IAAI,CAACF,QAAQ,EAAE;IACb,MAAM,IAAIG,KAAK,CAAC,uBAAuB,CAAC;EAC1C;EAEA,IAAId,QAAQ,CAACgB,EAAE,KAAK,SAAS,EAAE;IAC7B,MAAM,IAAIF,KAAK,CAAC,sDAAsD,CAAC;EACzE;EAEA,OAAOZ,iBAAiB,CAACa,aAAa,CAACJ,QAAQ,EAAEC,OAAO,CAAC;AAC3D;;AAEA;AACA;AACA;AACA;AACA;AACA,OAAO,eAAeK,YAAYA,CAACN,QAAgB,EAAsB;EACvE,MAAMT,iBAAiB,GAAGD,eAAe,CAAC,CAAC;EAE3C,IAAI,CAACC,iBAAiB,EAAE;IACtB,MAAMW,YAAY,GAChB,oDAAoD,GACpD,uBAAuB,GACvB,mEAAmE,GACnE,2CAA2C,GAC3C,yCAAyC,GACzC,yDAAyD;IAC3D,MAAM,IAAIC,KAAK,CAACD,YAAY,CAAC;EAC/B;EAEA,IAAI,CAACF,QAAQ,EAAE;IACb,MAAM,IAAIG,KAAK,CAAC,uBAAuB,CAAC;EAC1C;EAEA,OAAOZ,iBAAiB,CAACe,YAAY,CAACN,QAAQ,CAAC;AACjD;AAEA,eAAe;EACbD,aAAa;EACbK,aAAa;EACbE;AACF,CAAC","ignoreList":[]}
@@ -0,0 +1 @@
1
+ {"type":"module"}
@@ -0,0 +1 @@
1
+ {"type":"module"}
@@ -0,0 +1,54 @@
1
+ export interface CompressImageOptions {
2
+ maxWidth?: number;
3
+ maxHeight?: number;
4
+ quality?: number;
5
+ outputFormat?: 'jpeg' | 'png';
6
+ }
7
+ export interface CompressVideoOptions {
8
+ maxWidth?: number;
9
+ maxHeight?: number;
10
+ quality?: number;
11
+ bitrate?: number;
12
+ }
13
+ export interface CompressionResult {
14
+ path: string;
15
+ size: number;
16
+ originalSize: number;
17
+ compressionRatio: number;
18
+ width: number;
19
+ height: number;
20
+ }
21
+ export interface MediaInfo {
22
+ size: number;
23
+ width?: number;
24
+ height?: number;
25
+ duration?: number;
26
+ mimeType?: string;
27
+ }
28
+ /**
29
+ * Compress an image file
30
+ * @param filePath - Path to the image file
31
+ * @param options - Compression options
32
+ * @returns Promise with compression result
33
+ */
34
+ export declare function compressImage(filePath: string, options?: CompressImageOptions): Promise<CompressionResult>;
35
+ /**
36
+ * Compress a video file
37
+ * @param filePath - Path to the video file
38
+ * @param options - Compression options
39
+ * @returns Promise with compression result
40
+ */
41
+ export declare function compressVideo(filePath: string, options?: CompressVideoOptions): Promise<CompressionResult>;
42
+ /**
43
+ * Get media file information
44
+ * @param filePath - Path to the media file
45
+ * @returns Promise with media information
46
+ */
47
+ export declare function getMediaInfo(filePath: string): Promise<MediaInfo>;
48
+ declare const _default: {
49
+ compressImage: typeof compressImage;
50
+ compressVideo: typeof compressVideo;
51
+ getMediaInfo: typeof getMediaInfo;
52
+ };
53
+ export default _default;
54
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AA2BA,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;CAC/B;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;GAKG;AACH,wBAAsB,aAAa,CACjC,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,oBAAyB,GACjC,OAAO,CAAC,iBAAiB,CAAC,CAmB5B;AAED;;;;;GAKG;AACH,wBAAsB,aAAa,CACjC,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,oBAAyB,GACjC,OAAO,CAAC,iBAAiB,CAAC,CAuB5B;AAED;;;;GAIG;AACH,wBAAsB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAmBvE;;;;;;AAED,wBAIE"}
package/package.json ADDED
@@ -0,0 +1,160 @@
1
+ {
2
+ "name": "react-native-rn-media-compressor",
3
+ "version": "0.1.0",
4
+ "description": "This package will reduce the size of media file before uplaoding it to server",
5
+ "main": "./lib/module/index.js",
6
+ "types": "./lib/typescript/src/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "source": "./src/index.tsx",
10
+ "types": "./lib/typescript/src/index.d.ts",
11
+ "default": "./lib/module/index.js"
12
+ },
13
+ "./package.json": "./package.json"
14
+ },
15
+ "files": [
16
+ "src",
17
+ "lib",
18
+ "android",
19
+ "ios",
20
+ "cpp",
21
+ "*.podspec",
22
+ "react-native.config.js",
23
+ "!ios/build",
24
+ "!android/build",
25
+ "!android/gradle",
26
+ "!android/gradlew",
27
+ "!android/gradlew.bat",
28
+ "!android/local.properties",
29
+ "!**/__tests__",
30
+ "!**/__fixtures__",
31
+ "!**/__mocks__",
32
+ "!**/.*"
33
+ ],
34
+ "scripts": {
35
+ "example": "yarn workspace react-native-rn-media-compressor-example",
36
+ "clean": "del-cli lib",
37
+ "prepare": "bob build",
38
+ "typecheck": "tsc",
39
+ "lint": "eslint \"**/*.{js,ts,tsx}\"",
40
+ "test": "jest",
41
+ "release": "release-it --only-version"
42
+ },
43
+ "keywords": [
44
+ "react-native",
45
+ "ios",
46
+ "android"
47
+ ],
48
+ "repository": {
49
+ "type": "git",
50
+ "url": "git+https://github.com/Siddesh7972/react-native-rn-media-compressor.git"
51
+ },
52
+ "author": "Siddesh7972 <siddesh.gawande@mindbowser.com> (https://github.com/Siddesh7972)",
53
+ "license": "MIT",
54
+ "bugs": {
55
+ "url": "https://github.com/Siddesh7972/react-native-rn-media-compressor/issues"
56
+ },
57
+ "homepage": "https://github.com/Siddesh7972/react-native-rn-media-compressor#readme",
58
+ "publishConfig": {
59
+ "registry": "https://registry.npmjs.org/"
60
+ },
61
+ "devDependencies": {
62
+ "@commitlint/config-conventional": "^19.8.1",
63
+ "@eslint/compat": "^1.3.2",
64
+ "@eslint/eslintrc": "^3.3.1",
65
+ "@eslint/js": "^9.35.0",
66
+ "@react-native/babel-preset": "0.83.0",
67
+ "@react-native/eslint-config": "0.83.0",
68
+ "@release-it/conventional-changelog": "^10.0.1",
69
+ "@types/jest": "^29.5.14",
70
+ "@types/react": "^19.1.12",
71
+ "commitlint": "^19.8.1",
72
+ "del-cli": "^6.0.0",
73
+ "eslint": "^9.35.0",
74
+ "eslint-config-prettier": "^10.1.8",
75
+ "eslint-plugin-prettier": "^5.5.4",
76
+ "jest": "^29.7.0",
77
+ "lefthook": "^2.0.3",
78
+ "prettier": "^2.8.8",
79
+ "react": "19.1.0",
80
+ "react-native": "0.81.5",
81
+ "react-native-builder-bob": "^0.40.17",
82
+ "release-it": "^19.0.4",
83
+ "typescript": "^5.9.2"
84
+ },
85
+ "peerDependencies": {
86
+ "react": "*",
87
+ "react-native": "*"
88
+ },
89
+ "workspaces": [
90
+ "example"
91
+ ],
92
+ "packageManager": "yarn@4.11.0",
93
+ "react-native-builder-bob": {
94
+ "source": "src",
95
+ "output": "lib",
96
+ "targets": [
97
+ [
98
+ "module",
99
+ {
100
+ "esm": true
101
+ }
102
+ ],
103
+ [
104
+ "typescript",
105
+ {
106
+ "project": "tsconfig.build.json"
107
+ }
108
+ ]
109
+ ]
110
+ },
111
+ "prettier": {
112
+ "quoteProps": "consistent",
113
+ "singleQuote": true,
114
+ "tabWidth": 2,
115
+ "trailingComma": "es5",
116
+ "useTabs": false
117
+ },
118
+ "jest": {
119
+ "preset": "react-native",
120
+ "modulePathIgnorePatterns": [
121
+ "<rootDir>/example/node_modules",
122
+ "<rootDir>/lib/"
123
+ ]
124
+ },
125
+ "commitlint": {
126
+ "extends": [
127
+ "@commitlint/config-conventional"
128
+ ]
129
+ },
130
+ "release-it": {
131
+ "git": {
132
+ "commitMessage": "chore: release ${version}",
133
+ "tagName": "v${version}"
134
+ },
135
+ "npm": {
136
+ "publish": true
137
+ },
138
+ "github": {
139
+ "release": true
140
+ },
141
+ "plugins": {
142
+ "@release-it/conventional-changelog": {
143
+ "preset": {
144
+ "name": "angular"
145
+ }
146
+ }
147
+ }
148
+ },
149
+ "create-react-native-library": {
150
+ "type": "library",
151
+ "languages": "js",
152
+ "tools": [
153
+ "eslint",
154
+ "jest",
155
+ "lefthook",
156
+ "release-it"
157
+ ],
158
+ "version": "0.56.0"
159
+ }
160
+ }
@@ -0,0 +1,22 @@
1
+ require "json"
2
+
3
+ package = JSON.parse(File.read(File.join(__dir__, "package.json")))
4
+
5
+ Pod::Spec.new do |s|
6
+ s.name = "react-native-rn-media-compressor"
7
+ s.version = package["version"]
8
+ s.summary = package["description"]
9
+ s.description = <<-DESC
10
+ This package will reduce the size of media file before uploading it to server
11
+ DESC
12
+ s.homepage = package["homepage"]
13
+ s.license = package["license"]
14
+ s.authors = package["author"]
15
+
16
+ s.platforms = { :ios => "13.4" }
17
+ s.source = { :git => package["repository"]["url"], :tag => "#{s.version}" }
18
+
19
+ s.source_files = "ios/**/*.{h,m,mm,swift}"
20
+
21
+ s.dependency "React-Core"
22
+ end
@@ -0,0 +1,13 @@
1
+ module.exports = {
2
+ dependency: {
3
+ platforms: {
4
+ android: {
5
+ sourceDir: '../android/',
6
+ packageImportPath: 'import com.rnmediacompressor.RnMediaCompressorPackage;',
7
+ },
8
+ ios: {
9
+ podspecPath: '../react-native-rn-media-compressor.podspec',
10
+ },
11
+ },
12
+ },
13
+ };
package/src/index.tsx ADDED
@@ -0,0 +1,153 @@
1
+ import { NativeModules, Platform } from 'react-native';
2
+
3
+ // Lazy access to native module - check when function is called, not at module load
4
+ const getNativeModule = () => {
5
+ try {
6
+ // Try direct access
7
+ if (NativeModules.RnMediaCompressor) {
8
+ return NativeModules.RnMediaCompressor;
9
+ }
10
+ // Try accessing via the module name
11
+ const module = NativeModules['RnMediaCompressor'];
12
+ if (module) {
13
+ return module;
14
+ }
15
+ // Log available modules for debugging
16
+ if (__DEV__) {
17
+ console.log('Available native modules:', Object.keys(NativeModules));
18
+ }
19
+ return null;
20
+ } catch (error) {
21
+ if (__DEV__) {
22
+ console.error('Error accessing native module:', error);
23
+ }
24
+ return null;
25
+ }
26
+ };
27
+
28
+ export interface CompressImageOptions {
29
+ maxWidth?: number;
30
+ maxHeight?: number;
31
+ quality?: number; // 0.0 - 1.0
32
+ outputFormat?: 'jpeg' | 'png';
33
+ }
34
+
35
+ export interface CompressVideoOptions {
36
+ maxWidth?: number;
37
+ maxHeight?: number;
38
+ quality?: number; // 0.0 - 1.0
39
+ bitrate?: number; // bits per second
40
+ }
41
+
42
+ export interface CompressionResult {
43
+ path: string;
44
+ size: number;
45
+ originalSize: number;
46
+ compressionRatio: number;
47
+ width: number;
48
+ height: number;
49
+ }
50
+
51
+ export interface MediaInfo {
52
+ size: number;
53
+ width?: number;
54
+ height?: number;
55
+ duration?: number; // in seconds
56
+ mimeType?: string;
57
+ }
58
+
59
+ /**
60
+ * Compress an image file
61
+ * @param filePath - Path to the image file
62
+ * @param options - Compression options
63
+ * @returns Promise with compression result
64
+ */
65
+ export async function compressImage(
66
+ filePath: string,
67
+ options: CompressImageOptions = {}
68
+ ): Promise<CompressionResult> {
69
+ const RnMediaCompressor = getNativeModule();
70
+
71
+ if (!RnMediaCompressor) {
72
+ const errorMessage =
73
+ 'RnMediaCompressor native module is not available. ' +
74
+ 'Make sure you have:\n' +
75
+ '1. Run "yarn prepare" or "npm run prepare" to build the library\n' +
76
+ '2. For iOS: Run "cd ios && pod install"\n' +
77
+ '3. Rebuild your app (not just reload)\n' +
78
+ '4. If using Expo: Use a development build (not Expo Go)';
79
+ throw new Error(errorMessage);
80
+ }
81
+
82
+ if (!filePath) {
83
+ throw new Error('File path is required');
84
+ }
85
+
86
+ return RnMediaCompressor.compressImage(filePath, options);
87
+ }
88
+
89
+ /**
90
+ * Compress a video file
91
+ * @param filePath - Path to the video file
92
+ * @param options - Compression options
93
+ * @returns Promise with compression result
94
+ */
95
+ export async function compressVideo(
96
+ filePath: string,
97
+ options: CompressVideoOptions = {}
98
+ ): Promise<CompressionResult> {
99
+ const RnMediaCompressor = getNativeModule();
100
+
101
+ if (!RnMediaCompressor) {
102
+ const errorMessage =
103
+ 'RnMediaCompressor native module is not available. ' +
104
+ 'Make sure you have:\n' +
105
+ '1. Run "yarn prepare" or "npm run prepare" to build the library\n' +
106
+ '2. For iOS: Run "cd ios && pod install"\n' +
107
+ '3. Rebuild your app (not just reload)\n' +
108
+ '4. If using Expo: Use a development build (not Expo Go)';
109
+ throw new Error(errorMessage);
110
+ }
111
+
112
+ if (!filePath) {
113
+ throw new Error('File path is required');
114
+ }
115
+
116
+ if (Platform.OS === 'android') {
117
+ throw new Error('Video compression is not yet implemented for Android');
118
+ }
119
+
120
+ return RnMediaCompressor.compressVideo(filePath, options);
121
+ }
122
+
123
+ /**
124
+ * Get media file information
125
+ * @param filePath - Path to the media file
126
+ * @returns Promise with media information
127
+ */
128
+ export async function getMediaInfo(filePath: string): Promise<MediaInfo> {
129
+ const RnMediaCompressor = getNativeModule();
130
+
131
+ if (!RnMediaCompressor) {
132
+ const errorMessage =
133
+ 'RnMediaCompressor native module is not available. ' +
134
+ 'Make sure you have:\n' +
135
+ '1. Run "yarn prepare" or "npm run prepare" to build the library\n' +
136
+ '2. For iOS: Run "cd ios && pod install"\n' +
137
+ '3. Rebuild your app (not just reload)\n' +
138
+ '4. If using Expo: Use a development build (not Expo Go)';
139
+ throw new Error(errorMessage);
140
+ }
141
+
142
+ if (!filePath) {
143
+ throw new Error('File path is required');
144
+ }
145
+
146
+ return RnMediaCompressor.getMediaInfo(filePath);
147
+ }
148
+
149
+ export default {
150
+ compressImage,
151
+ compressVideo,
152
+ getMediaInfo,
153
+ };