react-native-compressor 1.7.2 → 1.8.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/README.md +30 -7
- package/android/build.gradle +8 -0
- package/android/src/main/java/com/reactnativecompressor/Audio/AudioCompressor.kt +527 -0
- package/android/src/main/java/com/reactnativecompressor/Audio/AudioMain.kt +31 -0
- package/android/src/main/java/com/reactnativecompressor/CompressorModule.kt +157 -0
- package/android/src/main/java/com/reactnativecompressor/CompressorPackage.kt +34 -0
- package/android/src/main/java/com/reactnativecompressor/Image/ImageCompressor.kt +199 -0
- package/android/src/main/java/com/reactnativecompressor/Image/ImageCompressorOptions.kt +57 -0
- package/android/src/main/java/com/reactnativecompressor/Image/ImageMain.kt +31 -0
- package/android/src/main/java/com/reactnativecompressor/Image/ImageSize.kt +3 -0
- package/android/src/main/java/com/reactnativecompressor/Utils/Downloader.kt +119 -0
- package/android/src/main/java/com/reactnativecompressor/Utils/EventEmitterHandler.kt +66 -0
- package/android/src/main/java/com/reactnativecompressor/Utils/MediaCache.kt +61 -0
- package/android/src/main/java/com/reactnativecompressor/Utils/RealPathUtil.kt +177 -0
- package/android/src/main/java/com/reactnativecompressor/Utils/Uploader.kt +117 -0
- package/android/src/main/java/com/reactnativecompressor/Utils/Utils.kt +162 -0
- package/android/src/main/java/com/reactnativecompressor/Video/AutoVideoCompression.kt +63 -0
- package/android/src/main/java/com/reactnativecompressor/Video/VideoCompressorHelper.kt +125 -0
- package/android/src/main/java/com/reactnativecompressor/Video/VideoMain.kt +86 -0
- package/android/src/newarch/CompressorSpec.kt +5 -0
- package/android/src/oldarch/CompressorSpec.kt +32 -0
- package/ios/Audio/AudioMain.swift +115 -0
- package/ios/Compressor-Bridging-Header.h +1 -2
- package/ios/Compressor.h +0 -8
- package/ios/Compressor.mm +28 -389
- package/ios/CompressorManager.swift +96 -0
- package/ios/Image/ImageCompressor.swift +354 -0
- package/ios/Image/ImageCompressorOptions.swift +93 -0
- package/ios/Image/ImageMain.swift +27 -0
- package/ios/Utils/Downloader.swift +157 -0
- package/ios/Utils/EventEmitterHandler.swift +54 -0
- package/ios/Utils/MediaCache.swift +48 -0
- package/ios/Utils/Uploader.swift +99 -0
- package/ios/Utils/Utils.swift +110 -0
- package/ios/Video/{VideoCompressor.swift → VideoMain.swift} +167 -146
- package/lib/commonjs/Image/index.js +1 -1
- package/lib/commonjs/Image/index.js.map +1 -1
- package/lib/commonjs/Main.js +1 -8
- package/lib/commonjs/Main.js.map +1 -1
- package/lib/commonjs/Spec/NativeCompressor.js.map +1 -1
- package/lib/commonjs/Video/index.js +4 -33
- package/lib/commonjs/Video/index.js.map +1 -1
- package/lib/commonjs/expo-plugin/compressor.js.map +1 -1
- package/lib/commonjs/index.js +11 -6
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/utils/Downloader.js +38 -0
- package/lib/commonjs/utils/Downloader.js.map +1 -0
- package/lib/commonjs/utils/Uploader.js +40 -0
- package/lib/commonjs/utils/Uploader.js.map +1 -0
- package/lib/commonjs/utils/index.js +35 -0
- package/lib/commonjs/utils/index.js.map +1 -1
- package/lib/module/Image/index.js +1 -1
- package/lib/module/Image/index.js.map +1 -1
- package/lib/module/Main.js +1 -7
- package/lib/module/Main.js.map +1 -1
- package/lib/module/Spec/NativeCompressor.js.map +1 -1
- package/lib/module/Video/index.js +5 -33
- package/lib/module/Video/index.js.map +1 -1
- package/lib/module/expo-plugin/compressor.js.map +1 -1
- package/lib/module/index.js +6 -6
- package/lib/module/index.js.map +1 -1
- package/lib/module/utils/Downloader.js +31 -0
- package/lib/module/utils/Downloader.js.map +1 -0
- package/lib/module/utils/Uploader.js +33 -0
- package/lib/module/utils/Uploader.js.map +1 -0
- package/lib/module/utils/index.js +2 -0
- package/lib/module/utils/index.js.map +1 -1
- package/lib/typescript/Image/index.d.ts +4 -0
- package/lib/typescript/Image/index.d.ts.map +1 -1
- package/lib/typescript/Main.d.ts +1 -2
- package/lib/typescript/Main.d.ts.map +1 -1
- package/lib/typescript/Spec/NativeCompressor.d.ts +8 -2
- package/lib/typescript/Spec/NativeCompressor.d.ts.map +1 -1
- package/lib/typescript/Video/index.d.ts +4 -28
- package/lib/typescript/Video/index.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +5 -4
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/utils/Downloader.d.ts +2 -0
- package/lib/typescript/utils/Downloader.d.ts.map +1 -0
- package/lib/typescript/utils/Uploader.d.ts +28 -0
- package/lib/typescript/utils/Uploader.d.ts.map +1 -0
- package/lib/typescript/utils/index.d.ts +2 -1
- package/lib/typescript/utils/index.d.ts.map +1 -1
- package/package.json +6 -4
- package/src/Image/index.tsx +5 -1
- package/src/Main.tsx +1 -15
- package/src/Spec/NativeCompressor.ts +14 -2
- package/src/Video/index.tsx +11 -84
- package/src/expo-plugin/compressor.ts +1 -1
- package/src/index.tsx +6 -3
- package/src/utils/Downloader.tsx +38 -0
- package/src/utils/Uploader.tsx +78 -0
- package/src/utils/index.tsx +2 -0
- package/android/src/main/java/com/reactnativecompressor/Audio/AudioCompressor.java +0 -605
- package/android/src/main/java/com/reactnativecompressor/CompressorModule.java +0 -220
- package/android/src/main/java/com/reactnativecompressor/CompressorPackage.java +0 -58
- package/android/src/main/java/com/reactnativecompressor/Image/ImageCompressor.java +0 -257
- package/android/src/main/java/com/reactnativecompressor/Image/utils/ImageCompressorOptions.java +0 -69
- package/android/src/main/java/com/reactnativecompressor/Image/utils/ImageSize.java +0 -13
- package/android/src/main/java/com/reactnativecompressor/Utils/FileUplaoder/FileUploadHelper.java +0 -39
- package/android/src/main/java/com/reactnativecompressor/Utils/FileUplaoder/FileUploader.java +0 -129
- package/android/src/main/java/com/reactnativecompressor/Utils/MediaCache.java +0 -65
- package/android/src/main/java/com/reactnativecompressor/Utils/RealPathUtil.java +0 -210
- package/android/src/main/java/com/reactnativecompressor/Utils/Utils.java +0 -272
- package/android/src/main/java/com/reactnativecompressor/Video/AutoVideoCompression/AutoVideoCompression.java +0 -87
- package/android/src/main/java/com/reactnativecompressor/Video/VideoCompressorHelper.java +0 -182
- package/android/src/main/java/com/reactnativecompressor/Video/VideoModule.java +0 -112
- package/android/src/newarch/CompressorSpec.java +0 -9
- package/android/src/newarch/VideoCompressorSpec.java +0 -9
- package/android/src/oldarch/CompressorSpec.java +0 -34
- package/android/src/oldarch/VideoCompressorSpec.java +0 -26
- package/ios/Image/ImageCompressor.h +0 -22
- package/ios/Image/ImageCompressor.m +0 -540
- package/ios/Image/ImageCompressorOptions.h +0 -28
- package/ios/Image/ImageCompressorOptions.m +0 -97
- package/ios/Utils/Downloader.h +0 -16
- package/ios/Utils/Downloader.m +0 -150
- package/ios/Utils/MediaCache.h +0 -11
- package/ios/Utils/MediaCache.m +0 -51
- package/ios/Video/VideoCompressor.h +0 -5
- package/ios/Video/VideoCompressor.m +0 -1
- package/lib/commonjs/Spec/NativeVideoCompressor.js +0 -10
- package/lib/commonjs/Spec/NativeVideoCompressor.js.map +0 -1
- package/lib/module/Spec/NativeVideoCompressor.js +0 -3
- package/lib/module/Spec/NativeVideoCompressor.js.map +0 -1
- package/lib/typescript/Spec/NativeVideoCompressor.d.ts +0 -13
- package/lib/typescript/Spec/NativeVideoCompressor.d.ts.map +0 -1
- package/src/Spec/NativeVideoCompressor.ts +0 -14
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
package com.reactnativecompressor.Audio
|
|
2
|
+
|
|
3
|
+
import android.media.MediaMetadataRetriever
|
|
4
|
+
import android.net.Uri
|
|
5
|
+
import android.util.Log
|
|
6
|
+
import com.facebook.react.bridge.Promise
|
|
7
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
8
|
+
import com.facebook.react.bridge.ReadableMap
|
|
9
|
+
import com.reactnativecompressor.Utils.Utils
|
|
10
|
+
import com.reactnativecompressor.Video.VideoCompressorHelper
|
|
11
|
+
|
|
12
|
+
class AudioMain(private val reactContext: ReactApplicationContext) {
|
|
13
|
+
fun compress_audio(
|
|
14
|
+
fileUrl: String,
|
|
15
|
+
optionMap: ReadableMap,
|
|
16
|
+
promise: Promise) {
|
|
17
|
+
try {
|
|
18
|
+
val options = VideoCompressorHelper.fromMap(optionMap)
|
|
19
|
+
val uri = Uri.parse(fileUrl)
|
|
20
|
+
val srcPath = uri.path
|
|
21
|
+
val destinationPath = Utils.generateCacheFilePath("mp3", reactContext)
|
|
22
|
+
val metaRetriever = MediaMetadataRetriever()
|
|
23
|
+
metaRetriever.setDataSource(srcPath)
|
|
24
|
+
val bitrate = options.bitrate
|
|
25
|
+
Log.d("nomi onStart", destinationPath + "onProgress: " + bitrate)
|
|
26
|
+
AudioCompressor().CompressAudio(srcPath, destinationPath, bitrate.toInt() * 1000)
|
|
27
|
+
} catch (ex: Exception) {
|
|
28
|
+
promise.reject(ex)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
package com.reactnativecompressor
|
|
2
|
+
|
|
3
|
+
import android.os.Build
|
|
4
|
+
import androidx.annotation.RequiresApi
|
|
5
|
+
import com.facebook.react.bridge.Promise
|
|
6
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
7
|
+
import com.facebook.react.bridge.ReactMethod
|
|
8
|
+
import com.facebook.react.bridge.ReadableMap
|
|
9
|
+
import com.reactnativecompressor.Audio.AudioMain
|
|
10
|
+
import com.reactnativecompressor.Image.ImageMain
|
|
11
|
+
import com.reactnativecompressor.Utils.Downloader
|
|
12
|
+
import com.reactnativecompressor.Utils.EventEmitterHandler
|
|
13
|
+
import com.reactnativecompressor.Utils.Uploader
|
|
14
|
+
import com.reactnativecompressor.Utils.Utils
|
|
15
|
+
import com.reactnativecompressor.Utils.Utils.generateCacheFilePath
|
|
16
|
+
import com.reactnativecompressor.Utils.Utils.getRealPath
|
|
17
|
+
import com.reactnativecompressor.Video.VideoMain
|
|
18
|
+
|
|
19
|
+
class CompressorModule(private val reactContext: ReactApplicationContext) : CompressorSpec(reactContext) {
|
|
20
|
+
private val imageMain: ImageMain = ImageMain(reactContext)
|
|
21
|
+
private val videoMain: VideoMain = VideoMain(reactContext)
|
|
22
|
+
private val audioMain: AudioMain = AudioMain(reactContext)
|
|
23
|
+
|
|
24
|
+
override fun initialize() {
|
|
25
|
+
super.initialize()
|
|
26
|
+
EventEmitterHandler.reactContext=reactContext;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
companion object {
|
|
31
|
+
const val NAME = "Compressor"
|
|
32
|
+
}
|
|
33
|
+
override fun getName(): String {
|
|
34
|
+
return NAME
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
//Image
|
|
38
|
+
@ReactMethod
|
|
39
|
+
override fun image_compress(
|
|
40
|
+
imagePath: String,
|
|
41
|
+
optionMap: ReadableMap,
|
|
42
|
+
promise: Promise) {
|
|
43
|
+
imageMain.image_compress(imagePath,optionMap,promise)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// VIdeo
|
|
47
|
+
@ReactMethod
|
|
48
|
+
override fun compress(
|
|
49
|
+
fileUrl: String,
|
|
50
|
+
optionMap: ReadableMap,
|
|
51
|
+
promise: Promise) {
|
|
52
|
+
videoMain.compress(fileUrl,optionMap,promise)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
@ReactMethod
|
|
56
|
+
override fun cancelCompression(
|
|
57
|
+
uuid: String) {
|
|
58
|
+
videoMain.cancelCompression(uuid)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
@ReactMethod
|
|
62
|
+
override fun activateBackgroundTask(
|
|
63
|
+
options: ReadableMap,
|
|
64
|
+
promise: Promise) {
|
|
65
|
+
videoMain.activateBackgroundTask(options,promise)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
@ReactMethod
|
|
69
|
+
override fun deactivateBackgroundTask(
|
|
70
|
+
options: ReadableMap,
|
|
71
|
+
promise: Promise) {
|
|
72
|
+
videoMain.deactivateBackgroundTask(options,promise)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Audio
|
|
76
|
+
@ReactMethod
|
|
77
|
+
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
|
|
78
|
+
override fun compress_audio(
|
|
79
|
+
fileUrl: String,
|
|
80
|
+
optionMap: ReadableMap,
|
|
81
|
+
promise: Promise) {
|
|
82
|
+
audioMain.compress_audio(fileUrl,optionMap,promise)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Others
|
|
86
|
+
@ReactMethod
|
|
87
|
+
override fun generateFilePath(_extension: String, promise: Promise) {
|
|
88
|
+
try {
|
|
89
|
+
val outputUri = generateCacheFilePath(_extension, reactContext)
|
|
90
|
+
promise.resolve(outputUri)
|
|
91
|
+
} catch (e: Exception) {
|
|
92
|
+
promise.reject(e)
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
@ReactMethod
|
|
97
|
+
override fun getRealPath(path: String, type: String, promise: Promise) {
|
|
98
|
+
try {
|
|
99
|
+
val realPath = getRealPath(path, reactContext)
|
|
100
|
+
promise.resolve("file://$realPath")
|
|
101
|
+
} catch (e: Exception) {
|
|
102
|
+
promise.reject(e)
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
@ReactMethod
|
|
107
|
+
override fun getVideoMetaData(filePath: String, promise: Promise) {
|
|
108
|
+
videoMain.getVideoMetaData(filePath,promise)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
@ReactMethod
|
|
112
|
+
override fun getFileSize(filePath: String, promise: Promise) {
|
|
113
|
+
Utils.getFileSize(filePath,promise,reactContext)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
@ReactMethod
|
|
117
|
+
override fun upload(
|
|
118
|
+
fileUrl: String,
|
|
119
|
+
options: ReadableMap,
|
|
120
|
+
promise: Promise) {
|
|
121
|
+
Uploader.upload(fileUrl, options, reactContext, promise)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
@ReactMethod
|
|
125
|
+
override fun download(
|
|
126
|
+
fileUrl: String,
|
|
127
|
+
options: ReadableMap,
|
|
128
|
+
promise: Promise) {
|
|
129
|
+
var uuid: String = ""
|
|
130
|
+
var progressDivider=0;
|
|
131
|
+
if(options.hasKey("uuid"))
|
|
132
|
+
{
|
|
133
|
+
uuid= options.getString("uuid") as String
|
|
134
|
+
}
|
|
135
|
+
if(options.hasKey("progressDivider"))
|
|
136
|
+
{
|
|
137
|
+
progressDivider= options.getString("progressDivider") as Int
|
|
138
|
+
}
|
|
139
|
+
val downloadedFilePath:String?=Downloader.downloadMediaWithProgress(fileUrl, uuid,progressDivider,reactContext)
|
|
140
|
+
if(downloadedFilePath!=null)
|
|
141
|
+
{
|
|
142
|
+
promise.resolve(downloadedFilePath)
|
|
143
|
+
}
|
|
144
|
+
else
|
|
145
|
+
{
|
|
146
|
+
promise.reject("Unable to download")
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
@ReactMethod
|
|
151
|
+
override fun addListener(eventName: String) {
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
@ReactMethod
|
|
155
|
+
override fun removeListeners(count: Double) {
|
|
156
|
+
}
|
|
157
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
package com.reactnativecompressor
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.TurboReactPackage
|
|
4
|
+
import com.facebook.react.bridge.NativeModule
|
|
5
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
6
|
+
import com.facebook.react.module.model.ReactModuleInfo
|
|
7
|
+
import com.facebook.react.module.model.ReactModuleInfoProvider
|
|
8
|
+
|
|
9
|
+
class CompressorPackage : TurboReactPackage() {
|
|
10
|
+
override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? {
|
|
11
|
+
return if (name == CompressorModule.NAME) {
|
|
12
|
+
CompressorModule(reactContext)
|
|
13
|
+
} else {
|
|
14
|
+
null
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
override fun getReactModuleInfoProvider(): ReactModuleInfoProvider {
|
|
19
|
+
return ReactModuleInfoProvider {
|
|
20
|
+
val moduleInfos: MutableMap<String, ReactModuleInfo> = HashMap()
|
|
21
|
+
val isTurboModule = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
|
|
22
|
+
moduleInfos[CompressorModule.NAME] = ReactModuleInfo(
|
|
23
|
+
CompressorModule.NAME,
|
|
24
|
+
CompressorModule.NAME,
|
|
25
|
+
false, // canOverrideExistingModule
|
|
26
|
+
false, // needsEagerInit
|
|
27
|
+
true, // hasConstants
|
|
28
|
+
false, // isCxxModule
|
|
29
|
+
isTurboModule // isTurboModule
|
|
30
|
+
)
|
|
31
|
+
moduleInfos
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
package com.reactnativecompressor.Image
|
|
2
|
+
|
|
3
|
+
import android.graphics.Bitmap
|
|
4
|
+
import android.graphics.Bitmap.CompressFormat
|
|
5
|
+
import android.graphics.BitmapFactory
|
|
6
|
+
import android.graphics.Canvas
|
|
7
|
+
import android.graphics.Matrix
|
|
8
|
+
import android.graphics.Paint
|
|
9
|
+
import android.media.ExifInterface
|
|
10
|
+
import android.net.Uri
|
|
11
|
+
import android.util.Base64
|
|
12
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
13
|
+
import com.reactnativecompressor.Utils.Utils.generateCacheFilePath
|
|
14
|
+
import java.io.ByteArrayOutputStream
|
|
15
|
+
import java.io.File
|
|
16
|
+
import java.io.FileOutputStream
|
|
17
|
+
import java.io.IOException
|
|
18
|
+
import java.net.MalformedURLException
|
|
19
|
+
|
|
20
|
+
object ImageCompressor {
|
|
21
|
+
fun getRNFileUrl(filePath: String?): String? {
|
|
22
|
+
var filePath = filePath
|
|
23
|
+
val returnAbleFile = File(filePath)
|
|
24
|
+
try {
|
|
25
|
+
filePath = returnAbleFile.toURL().toString()
|
|
26
|
+
} catch (e: MalformedURLException) {
|
|
27
|
+
e.printStackTrace()
|
|
28
|
+
}
|
|
29
|
+
return filePath
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
fun findActualSize(image: Bitmap, maxWidth: Int, maxHeight: Int): ImageSize {
|
|
33
|
+
val width = image.width.toFloat()
|
|
34
|
+
val height = image.height.toFloat()
|
|
35
|
+
if (width > height) {
|
|
36
|
+
val newHeight = Math.round(height / (width / maxWidth))
|
|
37
|
+
val scale = newHeight / height
|
|
38
|
+
return ImageSize(maxWidth, newHeight, scale)
|
|
39
|
+
}
|
|
40
|
+
val newWidth = Math.round(width / (height / maxHeight))
|
|
41
|
+
val scale = newWidth / width
|
|
42
|
+
return ImageSize(newWidth, maxHeight, scale)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
fun decodeImage(value: String?): Bitmap {
|
|
46
|
+
val data = Base64.decode(value, Base64.DEFAULT)
|
|
47
|
+
return BitmapFactory.decodeByteArray(data, 0, data.size)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
fun loadImage(value: String?): Bitmap {
|
|
51
|
+
val uri = Uri.parse(value)
|
|
52
|
+
val filePath = uri.path
|
|
53
|
+
return BitmapFactory.decodeFile(filePath)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
fun encodeImage(imageDataByteArrayOutputStream: ByteArrayOutputStream, isBase64: Boolean, outputExtension: String?, reactContext: ReactApplicationContext?): String? {
|
|
57
|
+
if (isBase64) {
|
|
58
|
+
val imageData = imageDataByteArrayOutputStream.toByteArray()
|
|
59
|
+
return Base64.encodeToString(imageData, Base64.DEFAULT)
|
|
60
|
+
} else {
|
|
61
|
+
val outputUri = generateCacheFilePath(outputExtension!!, reactContext!!)
|
|
62
|
+
try {
|
|
63
|
+
val fos = FileOutputStream(outputUri)
|
|
64
|
+
imageDataByteArrayOutputStream.writeTo(fos)
|
|
65
|
+
return getRNFileUrl(outputUri)
|
|
66
|
+
} catch (e: Exception) {
|
|
67
|
+
e.printStackTrace()
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return ""
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
fun resize(image: Bitmap, maxWidth: Int, maxHeight: Int): Bitmap {
|
|
74
|
+
val size = findActualSize(image, maxWidth, maxHeight)
|
|
75
|
+
val scaledImage = Bitmap.createBitmap(size.width, size.height, image.config)
|
|
76
|
+
val scaleMatrix = Matrix()
|
|
77
|
+
val canvas = Canvas(scaledImage)
|
|
78
|
+
val paint = Paint(Paint.FILTER_BITMAP_FLAG)
|
|
79
|
+
scaleMatrix.setScale(size.scale, size.scale, 0f, 0f)
|
|
80
|
+
paint.isDither = true
|
|
81
|
+
paint.isAntiAlias = true
|
|
82
|
+
paint.isFilterBitmap = true
|
|
83
|
+
canvas.drawBitmap(image, scaleMatrix, paint)
|
|
84
|
+
return scaledImage
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
fun compress(image: Bitmap?, output: ImageCompressorOptions.OutputType, quality: Float): ByteArrayOutputStream {
|
|
88
|
+
val stream = ByteArrayOutputStream()
|
|
89
|
+
val format = if (output === ImageCompressorOptions.OutputType.jpg) CompressFormat.JPEG else CompressFormat.PNG
|
|
90
|
+
image!!.compress(format, Math.round(100 * quality), stream)
|
|
91
|
+
return stream
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
fun manualCompressImage(imagePath: String?, options: ImageCompressorOptions, reactContext: ReactApplicationContext?): String? {
|
|
95
|
+
val image = if (options.input === ImageCompressorOptions.InputType.base64) decodeImage(imagePath) else loadImage(imagePath)
|
|
96
|
+
val resizedImage = resize(image, options.maxWidth, options.maxHeight)
|
|
97
|
+
val imageDataByteArrayOutputStream = compress(resizedImage, options.output, options.quality)
|
|
98
|
+
val isBase64 = options.returnableOutputType === ImageCompressorOptions.ReturnableOutputType.base64
|
|
99
|
+
return encodeImage(imageDataByteArrayOutputStream, isBase64, options.output.toString(), reactContext)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
fun autoCompressImage(imagePath: String?, compressorOptions: ImageCompressorOptions, reactContext: ReactApplicationContext?): String? {
|
|
103
|
+
var imagePath = imagePath
|
|
104
|
+
val outputExtension = compressorOptions.output.toString()
|
|
105
|
+
val autoCompressMaxHeight = compressorOptions.maxHeight.toFloat()
|
|
106
|
+
val autoCompressMaxWidth = compressorOptions.maxWidth.toFloat()
|
|
107
|
+
val isBase64 = compressorOptions.returnableOutputType === ImageCompressorOptions.ReturnableOutputType.base64
|
|
108
|
+
val uri = Uri.parse(imagePath)
|
|
109
|
+
imagePath = uri.path
|
|
110
|
+
var scaledBitmap: Bitmap? = null
|
|
111
|
+
val options = BitmapFactory.Options()
|
|
112
|
+
options.inJustDecodeBounds = true
|
|
113
|
+
var bmp = BitmapFactory.decodeFile(imagePath, options)
|
|
114
|
+
var actualHeight = options.outHeight
|
|
115
|
+
var actualWidth = options.outWidth
|
|
116
|
+
var imgRatio = actualWidth.toFloat() / actualHeight.toFloat()
|
|
117
|
+
val maxRatio = autoCompressMaxWidth / autoCompressMaxHeight
|
|
118
|
+
if (actualHeight > autoCompressMaxHeight || actualWidth > autoCompressMaxWidth) {
|
|
119
|
+
if (imgRatio < maxRatio) {
|
|
120
|
+
imgRatio = autoCompressMaxHeight / actualHeight
|
|
121
|
+
actualWidth = (imgRatio * actualWidth).toInt()
|
|
122
|
+
actualHeight = autoCompressMaxHeight.toInt()
|
|
123
|
+
} else if (imgRatio > maxRatio) {
|
|
124
|
+
imgRatio = autoCompressMaxWidth / actualWidth
|
|
125
|
+
actualHeight = (imgRatio * actualHeight).toInt()
|
|
126
|
+
actualWidth = autoCompressMaxWidth.toInt()
|
|
127
|
+
} else {
|
|
128
|
+
actualHeight = autoCompressMaxHeight.toInt()
|
|
129
|
+
actualWidth = autoCompressMaxWidth.toInt()
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
options.inSampleSize = calculateInSampleSize(options, actualWidth, actualHeight)
|
|
133
|
+
options.inJustDecodeBounds = false
|
|
134
|
+
options.inDither = false
|
|
135
|
+
options.inPurgeable = true
|
|
136
|
+
options.inInputShareable = true
|
|
137
|
+
options.inTempStorage = ByteArray(16 * 1024)
|
|
138
|
+
try {
|
|
139
|
+
bmp = BitmapFactory.decodeFile(imagePath, options)
|
|
140
|
+
} catch (exception: OutOfMemoryError) {
|
|
141
|
+
exception.printStackTrace()
|
|
142
|
+
}
|
|
143
|
+
try {
|
|
144
|
+
scaledBitmap = Bitmap.createBitmap(actualWidth, actualHeight, Bitmap.Config.RGB_565)
|
|
145
|
+
} catch (exception: OutOfMemoryError) {
|
|
146
|
+
exception.printStackTrace()
|
|
147
|
+
}
|
|
148
|
+
val ratioX = actualWidth / options.outWidth.toFloat()
|
|
149
|
+
val ratioY = actualHeight / options.outHeight.toFloat()
|
|
150
|
+
val middleX = actualWidth / 2.0f
|
|
151
|
+
val middleY = actualHeight / 2.0f
|
|
152
|
+
val scaleMatrix = Matrix()
|
|
153
|
+
scaleMatrix.setScale(ratioX, ratioY, middleX, middleY)
|
|
154
|
+
val canvas = Canvas(scaledBitmap!!)
|
|
155
|
+
canvas.setMatrix(scaleMatrix)
|
|
156
|
+
canvas.drawBitmap(bmp!!, middleX - bmp.width / 2, middleY - bmp.height / 2, Paint(Paint.FILTER_BITMAP_FLAG))
|
|
157
|
+
if (bmp != null) {
|
|
158
|
+
bmp.recycle()
|
|
159
|
+
}
|
|
160
|
+
scaledBitmap = correctImageOrientation(scaledBitmap, imagePath)
|
|
161
|
+
val imageDataByteArrayOutputStream = compress(scaledBitmap, compressorOptions.output, compressorOptions.quality)
|
|
162
|
+
return encodeImage(imageDataByteArrayOutputStream, isBase64, compressorOptions.output.toString(), reactContext)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
fun calculateInSampleSize(options: BitmapFactory.Options, reqWidth: Int, reqHeight: Int): Int {
|
|
166
|
+
val height = options.outHeight
|
|
167
|
+
val width = options.outWidth
|
|
168
|
+
var inSampleSize = 1
|
|
169
|
+
if (height > reqHeight || width > reqWidth) {
|
|
170
|
+
val heightRatio = Math.round(height.toFloat() / reqHeight.toFloat())
|
|
171
|
+
val widthRatio = Math.round(width.toFloat() / reqWidth.toFloat())
|
|
172
|
+
inSampleSize = if (heightRatio < widthRatio) heightRatio else widthRatio
|
|
173
|
+
}
|
|
174
|
+
val totalPixels = (width * height).toFloat()
|
|
175
|
+
val totalReqPixelsCap = (reqWidth * reqHeight * 2).toFloat()
|
|
176
|
+
while (totalPixels / (inSampleSize * inSampleSize) > totalReqPixelsCap) {
|
|
177
|
+
inSampleSize++
|
|
178
|
+
}
|
|
179
|
+
return inSampleSize
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
fun correctImageOrientation(bitmap: Bitmap?, imagePath: String?): Bitmap? {
|
|
183
|
+
return try {
|
|
184
|
+
val exif = ExifInterface(imagePath!!)
|
|
185
|
+
val orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL)
|
|
186
|
+
val matrix = Matrix()
|
|
187
|
+
when (orientation) {
|
|
188
|
+
ExifInterface.ORIENTATION_ROTATE_90 -> matrix.postRotate(90f)
|
|
189
|
+
ExifInterface.ORIENTATION_ROTATE_180 -> matrix.postRotate(180f)
|
|
190
|
+
ExifInterface.ORIENTATION_ROTATE_270 -> matrix.postRotate(270f)
|
|
191
|
+
else -> return bitmap // No rotation needed
|
|
192
|
+
}
|
|
193
|
+
Bitmap.createBitmap(bitmap!!, 0, 0, bitmap.width, bitmap.height, matrix, true)
|
|
194
|
+
} catch (e: IOException) {
|
|
195
|
+
e.printStackTrace()
|
|
196
|
+
bitmap // Return original bitmap if an error occurs
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
package com.reactnativecompressor.Image
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.ReadableMap
|
|
4
|
+
|
|
5
|
+
class ImageCompressorOptions {
|
|
6
|
+
enum class InputType {
|
|
7
|
+
base64,
|
|
8
|
+
uri
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
enum class OutputType {
|
|
12
|
+
png,
|
|
13
|
+
jpg
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
enum class ReturnableOutputType {
|
|
17
|
+
base64,
|
|
18
|
+
uri
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
enum class CompressionMethod {
|
|
22
|
+
auto,
|
|
23
|
+
manual
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
var compressionMethod = CompressionMethod.auto
|
|
27
|
+
var maxWidth = 1280
|
|
28
|
+
var maxHeight = 1280
|
|
29
|
+
var progressDivider: Int? = 0
|
|
30
|
+
var quality = 0.8f
|
|
31
|
+
var input = InputType.uri
|
|
32
|
+
var output = OutputType.jpg
|
|
33
|
+
var uuid: String? = ""
|
|
34
|
+
var returnableOutputType = ReturnableOutputType.uri
|
|
35
|
+
|
|
36
|
+
companion object {
|
|
37
|
+
fun fromMap(map: ReadableMap): ImageCompressorOptions {
|
|
38
|
+
val options = ImageCompressorOptions()
|
|
39
|
+
val iterator = map.keySetIterator()
|
|
40
|
+
while (iterator.hasNextKey()) {
|
|
41
|
+
val key = iterator.nextKey()
|
|
42
|
+
when (key) {
|
|
43
|
+
"compressionMethod" -> options.compressionMethod = CompressionMethod.valueOf(map.getString(key)!!)
|
|
44
|
+
"maxWidth" -> options.maxWidth = map.getInt(key)
|
|
45
|
+
"maxHeight" -> options.maxHeight = map.getInt(key)
|
|
46
|
+
"progressDivider" -> options.progressDivider = map.getInt(key)
|
|
47
|
+
"quality" -> options.quality = map.getDouble(key).toFloat()
|
|
48
|
+
"input" -> options.input = InputType.valueOf(map.getString(key)!!)
|
|
49
|
+
"output" -> options.output = OutputType.valueOf(map.getString(key)!!)
|
|
50
|
+
"returnableOutputType" -> options.returnableOutputType = ReturnableOutputType.valueOf(map.getString(key)!!)
|
|
51
|
+
"uuid" -> options.uuid = map.getString(key)
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return options
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
package com.reactnativecompressor.Image
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.Promise
|
|
4
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
5
|
+
import com.facebook.react.bridge.ReadableMap
|
|
6
|
+
import com.reactnativecompressor.Utils.MediaCache
|
|
7
|
+
import com.reactnativecompressor.Utils.Utils
|
|
8
|
+
|
|
9
|
+
class ImageMain(private val reactContext: ReactApplicationContext) {
|
|
10
|
+
fun image_compress(
|
|
11
|
+
imagePath: String,
|
|
12
|
+
optionMap: ReadableMap,
|
|
13
|
+
promise: Promise
|
|
14
|
+
) {
|
|
15
|
+
var imagePath: String? = imagePath
|
|
16
|
+
try {
|
|
17
|
+
val options = ImageCompressorOptions.fromMap(optionMap)
|
|
18
|
+
imagePath = Utils.getRealPath(imagePath, reactContext, options.uuid, options.progressDivider)
|
|
19
|
+
if (options.compressionMethod === ImageCompressorOptions.CompressionMethod.auto) {
|
|
20
|
+
val returnableResult = ImageCompressor.autoCompressImage(imagePath, options, reactContext)
|
|
21
|
+
promise.resolve(returnableResult)
|
|
22
|
+
} else {
|
|
23
|
+
val returnableResult = ImageCompressor.manualCompressImage(imagePath, options, reactContext)
|
|
24
|
+
promise.resolve(returnableResult)
|
|
25
|
+
}
|
|
26
|
+
MediaCache.removeCompletedImagePath(imagePath)
|
|
27
|
+
} catch (ex: Exception) {
|
|
28
|
+
promise.reject(ex)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
package com.reactnativecompressor.Utils
|
|
2
|
+
|
|
3
|
+
import android.util.Log
|
|
4
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
5
|
+
import okhttp3.Call
|
|
6
|
+
import okhttp3.Callback
|
|
7
|
+
import okhttp3.OkHttpClient
|
|
8
|
+
import okhttp3.Request
|
|
9
|
+
import okhttp3.Response
|
|
10
|
+
import java.io.BufferedInputStream
|
|
11
|
+
import java.io.File
|
|
12
|
+
import java.io.FileOutputStream
|
|
13
|
+
import java.io.IOException
|
|
14
|
+
import java.util.UUID
|
|
15
|
+
import java.util.concurrent.Semaphore
|
|
16
|
+
import java.util.concurrent.atomic.AtomicReference
|
|
17
|
+
|
|
18
|
+
class Downloader {
|
|
19
|
+
companion object {
|
|
20
|
+
private const val TAG = "react-native-compessor"
|
|
21
|
+
|
|
22
|
+
fun downloadMediaWithProgress(mediaUrl: String?, uuid: String,progressDivider:Int, reactContext: ReactApplicationContext): String? {
|
|
23
|
+
downloadCompression[0] = 0
|
|
24
|
+
val client = OkHttpClient()
|
|
25
|
+
val request: Request = Request.Builder()
|
|
26
|
+
.url(mediaUrl!!)
|
|
27
|
+
.build()
|
|
28
|
+
val semaphore = Semaphore(0) // Semaphore to wait for the download to complete
|
|
29
|
+
val filePathRef = AtomicReference<String?>(null) // To store the file path
|
|
30
|
+
|
|
31
|
+
// Perform the request asynchronously
|
|
32
|
+
client.newCall(request).enqueue(object : Callback {
|
|
33
|
+
@Throws(IOException::class)
|
|
34
|
+
override fun onResponse(call: Call, response: Response) {
|
|
35
|
+
if (response.isSuccessful) {
|
|
36
|
+
val responseBody = response.body
|
|
37
|
+
if (responseBody != null) {
|
|
38
|
+
var fileExtension = "unknown" // Default extension
|
|
39
|
+
|
|
40
|
+
// Detect the file extension based on the Content-Type header
|
|
41
|
+
val contentType = response.header("Content-Type")
|
|
42
|
+
if (contentType != null) {
|
|
43
|
+
if (contentType == "image/jpeg") {
|
|
44
|
+
fileExtension = "jpg"
|
|
45
|
+
} else if (contentType == "image/png") {
|
|
46
|
+
fileExtension = "png"
|
|
47
|
+
} else if (contentType == "video/mp4") {
|
|
48
|
+
fileExtension = "mp4"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
val cacheDir = reactContext.cacheDir
|
|
52
|
+
val randomFileName = UUID.randomUUID().toString() + "." + fileExtension
|
|
53
|
+
val mediaFile = File(cacheDir, randomFileName)
|
|
54
|
+
try {
|
|
55
|
+
FileOutputStream(mediaFile).use { fos ->
|
|
56
|
+
val inputStream = BufferedInputStream(responseBody.byteStream())
|
|
57
|
+
val buffer = ByteArray(4096)
|
|
58
|
+
var bytesRead: Int
|
|
59
|
+
var totalBytesRead: Long = 0
|
|
60
|
+
var totalBytes = responseBody.contentLength()
|
|
61
|
+
if (totalBytes <= 0) {
|
|
62
|
+
totalBytes = 31457280
|
|
63
|
+
}
|
|
64
|
+
Log.d(TAG, "$totalBytesRead totalBytesRead $totalBytes")
|
|
65
|
+
Log.d(TAG, "$response responseBody $responseBody")
|
|
66
|
+
while (inputStream.read(buffer).also { bytesRead = it } != -1) {
|
|
67
|
+
fos.write(buffer, 0, bytesRead)
|
|
68
|
+
totalBytesRead += bytesRead.toLong()
|
|
69
|
+
val progressRatio = totalBytesRead.toDouble() / totalBytes
|
|
70
|
+
val progress = (progressRatio * 100).toInt()
|
|
71
|
+
sendProgressUpdate(progress, uuid, progressDivider)
|
|
72
|
+
}
|
|
73
|
+
fos.flush()
|
|
74
|
+
val filePath = "file://" + mediaFile.absolutePath
|
|
75
|
+
MediaCache.addCompletedImagePath(filePath)
|
|
76
|
+
filePathRef.set(filePath) // Set the file path
|
|
77
|
+
}
|
|
78
|
+
} catch (e: IOException) {
|
|
79
|
+
e.printStackTrace()
|
|
80
|
+
sendErrorResult(e.message, uuid)
|
|
81
|
+
} finally {
|
|
82
|
+
semaphore.release() // Release the semaphore when download is complete
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
} else {
|
|
86
|
+
sendErrorResult("Failed to download media: " + response.message, uuid)
|
|
87
|
+
semaphore.release() // Release the semaphore in case of error
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
override fun onFailure(call: Call, e: IOException) {
|
|
92
|
+
e.printStackTrace()
|
|
93
|
+
sendErrorResult(e.message, uuid)
|
|
94
|
+
semaphore.release() // Release the semaphore in case of failure
|
|
95
|
+
}
|
|
96
|
+
})
|
|
97
|
+
try {
|
|
98
|
+
semaphore.acquire() // Wait for the download to complete
|
|
99
|
+
} catch (e: InterruptedException) {
|
|
100
|
+
e.printStackTrace()
|
|
101
|
+
}
|
|
102
|
+
return filePathRef.get() // Return the file path
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
val downloadCompression = intArrayOf(0)
|
|
106
|
+
private fun sendProgressUpdate(progress: Int, uuid: String, progressDivider:Int=0) {
|
|
107
|
+
val roundProgress = Math.round(progress.toFloat())
|
|
108
|
+
if (progressDivider==0||(roundProgress % progressDivider == 0 && roundProgress > downloadCompression[0])) {
|
|
109
|
+
EventEmitterHandler.emitDownloadProgress(progress / 100.0,uuid)
|
|
110
|
+
Log.d(TAG, "downloadProgress: " + progress / 100.0)
|
|
111
|
+
downloadCompression[0] = roundProgress
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
private fun sendErrorResult(error: String?, uuid: String?) {
|
|
116
|
+
EventEmitterHandler.emitDownloadProgressError(uuid,error)
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|