react-native-compressor 1.7.2 → 1.8.1

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.
Files changed (130) hide show
  1. package/README.md +51 -8
  2. package/android/build.gradle +9 -0
  3. package/android/src/main/java/com/reactnativecompressor/Audio/AudioCompressor.kt +527 -0
  4. package/android/src/main/java/com/reactnativecompressor/Audio/AudioMain.kt +31 -0
  5. package/android/src/main/java/com/reactnativecompressor/CompressorModule.kt +169 -0
  6. package/android/src/main/java/com/reactnativecompressor/CompressorPackage.kt +34 -0
  7. package/android/src/main/java/com/reactnativecompressor/Image/ImageCompressor.kt +199 -0
  8. package/android/src/main/java/com/reactnativecompressor/Image/ImageCompressorOptions.kt +57 -0
  9. package/android/src/main/java/com/reactnativecompressor/Image/ImageMain.kt +31 -0
  10. package/android/src/main/java/com/reactnativecompressor/Image/ImageSize.kt +3 -0
  11. package/android/src/main/java/com/reactnativecompressor/Utils/Downloader.kt +119 -0
  12. package/android/src/main/java/com/reactnativecompressor/Utils/EventEmitterHandler.kt +66 -0
  13. package/android/src/main/java/com/reactnativecompressor/Utils/MediaCache.kt +61 -0
  14. package/android/src/main/java/com/reactnativecompressor/Utils/RealPathUtil.kt +177 -0
  15. package/android/src/main/java/com/reactnativecompressor/Utils/Uploader.kt +117 -0
  16. package/android/src/main/java/com/reactnativecompressor/Utils/Utils.kt +162 -0
  17. package/android/src/main/java/com/reactnativecompressor/Utils/createVideoThumbnail.kt +158 -0
  18. package/android/src/main/java/com/reactnativecompressor/Video/AutoVideoCompression.kt +63 -0
  19. package/android/src/main/java/com/reactnativecompressor/Video/VideoCompressorHelper.kt +125 -0
  20. package/android/src/main/java/com/reactnativecompressor/Video/VideoMain.kt +86 -0
  21. package/android/src/newarch/CompressorSpec.kt +5 -0
  22. package/android/src/oldarch/CompressorSpec.kt +34 -0
  23. package/ios/Audio/AudioMain.swift +115 -0
  24. package/ios/Compressor-Bridging-Header.h +1 -2
  25. package/ios/Compressor.h +0 -8
  26. package/ios/Compressor.mm +37 -389
  27. package/ios/CompressorManager.swift +107 -0
  28. package/ios/Image/ImageCompressor.swift +354 -0
  29. package/ios/Image/ImageCompressorOptions.swift +93 -0
  30. package/ios/Image/ImageMain.swift +27 -0
  31. package/ios/Utils/CreateVideoThumbnail.swift +111 -0
  32. package/ios/Utils/Downloader.swift +157 -0
  33. package/ios/Utils/EventEmitterHandler.swift +54 -0
  34. package/ios/Utils/MediaCache.swift +48 -0
  35. package/ios/Utils/Uploader.swift +99 -0
  36. package/ios/Utils/Utils.swift +110 -0
  37. package/ios/Video/{VideoCompressor.swift → VideoMain.swift} +167 -146
  38. package/lib/commonjs/Image/index.js +1 -1
  39. package/lib/commonjs/Image/index.js.map +1 -1
  40. package/lib/commonjs/Main.js +1 -8
  41. package/lib/commonjs/Main.js.map +1 -1
  42. package/lib/commonjs/Spec/NativeCompressor.js.map +1 -1
  43. package/lib/commonjs/Video/index.js +4 -33
  44. package/lib/commonjs/Video/index.js.map +1 -1
  45. package/lib/commonjs/expo-plugin/compressor.js.map +1 -1
  46. package/lib/commonjs/index.js +25 -6
  47. package/lib/commonjs/index.js.map +1 -1
  48. package/lib/commonjs/utils/Downloader.js +38 -0
  49. package/lib/commonjs/utils/Downloader.js.map +1 -0
  50. package/lib/commonjs/utils/Uploader.js +40 -0
  51. package/lib/commonjs/utils/Uploader.js.map +1 -0
  52. package/lib/commonjs/utils/index.js +47 -1
  53. package/lib/commonjs/utils/index.js.map +1 -1
  54. package/lib/module/Image/index.js +1 -1
  55. package/lib/module/Image/index.js.map +1 -1
  56. package/lib/module/Main.js +1 -7
  57. package/lib/module/Main.js.map +1 -1
  58. package/lib/module/Spec/NativeCompressor.js.map +1 -1
  59. package/lib/module/Video/index.js +5 -33
  60. package/lib/module/Video/index.js.map +1 -1
  61. package/lib/module/expo-plugin/compressor.js.map +1 -1
  62. package/lib/module/index.js +9 -7
  63. package/lib/module/index.js.map +1 -1
  64. package/lib/module/utils/Downloader.js +31 -0
  65. package/lib/module/utils/Downloader.js.map +1 -0
  66. package/lib/module/utils/Uploader.js +33 -0
  67. package/lib/module/utils/Uploader.js.map +1 -0
  68. package/lib/module/utils/index.js +9 -0
  69. package/lib/module/utils/index.js.map +1 -1
  70. package/lib/typescript/Image/index.d.ts +4 -0
  71. package/lib/typescript/Image/index.d.ts.map +1 -1
  72. package/lib/typescript/Main.d.ts +1 -2
  73. package/lib/typescript/Main.d.ts.map +1 -1
  74. package/lib/typescript/Spec/NativeCompressor.d.ts +16 -2
  75. package/lib/typescript/Spec/NativeCompressor.d.ts.map +1 -1
  76. package/lib/typescript/Video/index.d.ts +4 -28
  77. package/lib/typescript/Video/index.d.ts.map +1 -1
  78. package/lib/typescript/index.d.ts +7 -4
  79. package/lib/typescript/index.d.ts.map +1 -1
  80. package/lib/typescript/utils/Downloader.d.ts +2 -0
  81. package/lib/typescript/utils/Downloader.d.ts.map +1 -0
  82. package/lib/typescript/utils/Uploader.d.ts +28 -0
  83. package/lib/typescript/utils/Uploader.d.ts.map +1 -0
  84. package/lib/typescript/utils/index.d.ts +4 -1
  85. package/lib/typescript/utils/index.d.ts.map +1 -1
  86. package/package.json +6 -4
  87. package/src/Image/index.tsx +5 -1
  88. package/src/Main.tsx +1 -15
  89. package/src/Spec/NativeCompressor.ts +25 -2
  90. package/src/Video/index.tsx +11 -84
  91. package/src/expo-plugin/compressor.ts +1 -1
  92. package/src/index.tsx +12 -3
  93. package/src/utils/Downloader.tsx +38 -0
  94. package/src/utils/Uploader.tsx +78 -0
  95. package/src/utils/index.tsx +15 -0
  96. package/android/src/main/java/com/reactnativecompressor/Audio/AudioCompressor.java +0 -605
  97. package/android/src/main/java/com/reactnativecompressor/CompressorModule.java +0 -220
  98. package/android/src/main/java/com/reactnativecompressor/CompressorPackage.java +0 -58
  99. package/android/src/main/java/com/reactnativecompressor/Image/ImageCompressor.java +0 -257
  100. package/android/src/main/java/com/reactnativecompressor/Image/utils/ImageCompressorOptions.java +0 -69
  101. package/android/src/main/java/com/reactnativecompressor/Image/utils/ImageSize.java +0 -13
  102. package/android/src/main/java/com/reactnativecompressor/Utils/FileUplaoder/FileUploadHelper.java +0 -39
  103. package/android/src/main/java/com/reactnativecompressor/Utils/FileUplaoder/FileUploader.java +0 -129
  104. package/android/src/main/java/com/reactnativecompressor/Utils/MediaCache.java +0 -65
  105. package/android/src/main/java/com/reactnativecompressor/Utils/RealPathUtil.java +0 -210
  106. package/android/src/main/java/com/reactnativecompressor/Utils/Utils.java +0 -272
  107. package/android/src/main/java/com/reactnativecompressor/Video/AutoVideoCompression/AutoVideoCompression.java +0 -87
  108. package/android/src/main/java/com/reactnativecompressor/Video/VideoCompressorHelper.java +0 -182
  109. package/android/src/main/java/com/reactnativecompressor/Video/VideoModule.java +0 -112
  110. package/android/src/newarch/CompressorSpec.java +0 -9
  111. package/android/src/newarch/VideoCompressorSpec.java +0 -9
  112. package/android/src/oldarch/CompressorSpec.java +0 -34
  113. package/android/src/oldarch/VideoCompressorSpec.java +0 -26
  114. package/ios/Image/ImageCompressor.h +0 -22
  115. package/ios/Image/ImageCompressor.m +0 -540
  116. package/ios/Image/ImageCompressorOptions.h +0 -28
  117. package/ios/Image/ImageCompressorOptions.m +0 -97
  118. package/ios/Utils/Downloader.h +0 -16
  119. package/ios/Utils/Downloader.m +0 -150
  120. package/ios/Utils/MediaCache.h +0 -11
  121. package/ios/Utils/MediaCache.m +0 -51
  122. package/ios/Video/VideoCompressor.h +0 -5
  123. package/ios/Video/VideoCompressor.m +0 -1
  124. package/lib/commonjs/Spec/NativeVideoCompressor.js +0 -10
  125. package/lib/commonjs/Spec/NativeVideoCompressor.js.map +0 -1
  126. package/lib/module/Spec/NativeVideoCompressor.js +0 -3
  127. package/lib/module/Spec/NativeVideoCompressor.js.map +0 -1
  128. package/lib/typescript/Spec/NativeVideoCompressor.d.ts +0 -13
  129. package/lib/typescript/Spec/NativeVideoCompressor.d.ts.map +0 -1
  130. package/src/Spec/NativeVideoCompressor.ts +0 -14
@@ -0,0 +1,61 @@
1
+ package com.reactnativecompressor.Utils
2
+
3
+ import android.util.Log
4
+ import java.io.File
5
+
6
+ object MediaCache {
7
+ private const val TAG = "MediaCache"
8
+ private val completedImagePaths: MutableList<String> = ArrayList()
9
+ fun addCompletedImagePath(imagePath: String?) {
10
+ if (imagePath != null) {
11
+ // Your code to add the imagePath to a list or perform other actions if needed.
12
+ completedImagePaths.add(imagePath)
13
+ Log.d(TAG, "Successfully added image path: $imagePath")
14
+ }
15
+ }
16
+
17
+ @JvmStatic
18
+ fun removeCompletedImagePath(imagePath: String?) {
19
+ var imagePath = imagePath
20
+ if (imagePath != null) {
21
+ if (completedImagePaths.contains(imagePath)) {
22
+ // Image path exists in the list, so remove it
23
+ completedImagePaths.remove(imagePath)
24
+ if (imagePath.startsWith("file://")) {
25
+ imagePath = imagePath.substring(7) // Remove "file://"
26
+ }
27
+
28
+ // Remove the image file
29
+ val file = File(imagePath)
30
+ if (file.exists()) {
31
+ if (file.delete()) {
32
+ Log.d(TAG, "Successfully deleted image file: $imagePath")
33
+ } else {
34
+ Log.d(TAG, "Failed to delete image file: $imagePath")
35
+ }
36
+ } else {
37
+ Log.d(TAG, "Image file not found: $imagePath")
38
+ }
39
+ } else {
40
+ Log.d(TAG, "Image path not found in the completedImagePaths list: $imagePath")
41
+ }
42
+ }
43
+ }
44
+
45
+ fun cleanupCache() {
46
+ // Iterate through the list of completed image paths and delete the corresponding files
47
+ for (imagePath in completedImagePaths) {
48
+ val file = File(imagePath)
49
+ if (file.exists()) {
50
+ if (file.delete()) {
51
+ Log.d(TAG, "Successfully deleted image file during cache cleanup: $imagePath")
52
+ } else {
53
+ Log.d(TAG, "Failed to delete image file during cache cleanup: $imagePath")
54
+ }
55
+ } else {
56
+ Log.d(TAG, "Image file not found during cache cleanup: $imagePath")
57
+ }
58
+ }
59
+ completedImagePaths.clear()
60
+ }
61
+ }
@@ -0,0 +1,177 @@
1
+ package com.reactnativecompressor.Utils
2
+
3
+ import android.annotation.SuppressLint
4
+ import android.content.ContentUris
5
+ import android.content.Context
6
+ import android.database.Cursor
7
+ import android.net.Uri
8
+ import android.os.Build
9
+ import android.os.Environment
10
+ import android.provider.DocumentsContract
11
+ import android.provider.MediaStore
12
+ import androidx.loader.content.CursorLoader
13
+
14
+ object RealPathUtil {
15
+ fun getRealPath(context: Context, fileUri: Uri): String? {
16
+ val realPath: String?
17
+ // SDK < API11
18
+ realPath = if (Build.VERSION.SDK_INT < 11) {
19
+ getRealPathFromURI_BelowAPI11(context, fileUri)
20
+ } else if (Build.VERSION.SDK_INT < 19) {
21
+ getRealPathFromURI_API11to18(context, fileUri)
22
+ } else {
23
+ getRealPathFromURI_API19(context, fileUri)
24
+ }
25
+ return realPath
26
+ }
27
+
28
+ @SuppressLint("NewApi")
29
+ fun getRealPathFromURI_API11to18(context: Context?, contentUri: Uri?): String? {
30
+ val proj = arrayOf(MediaStore.Images.Media.DATA)
31
+ var result: String? = null
32
+ val cursorLoader = CursorLoader(context!!, contentUri!!, proj, null, null, null)
33
+ val cursor = cursorLoader.loadInBackground()
34
+ if (cursor != null) {
35
+ val column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA)
36
+ cursor.moveToFirst()
37
+ result = cursor.getString(column_index)
38
+ cursor.close()
39
+ }
40
+ return result
41
+ }
42
+
43
+ fun getRealPathFromURI_BelowAPI11(context: Context, contentUri: Uri?): String {
44
+ val proj = arrayOf(MediaStore.Images.Media.DATA)
45
+ val cursor = context.contentResolver.query(contentUri!!, proj, null, null, null)
46
+ var column_index = 0
47
+ var result = ""
48
+ if (cursor != null) {
49
+ column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA)
50
+ cursor.moveToFirst()
51
+ result = cursor.getString(column_index)
52
+ cursor.close()
53
+ return result
54
+ }
55
+ return result
56
+ }
57
+
58
+ /**
59
+ * Get a file path from a Uri. This will get the the path for Storage Access
60
+ * Framework Documents, as well as the _data field for the MediaStore and
61
+ * other file-based ContentProviders.
62
+ *
63
+ * @param context The context.
64
+ * @param uri The Uri to query.
65
+ * @author paulburke
66
+ */
67
+ @SuppressLint("NewApi")
68
+ fun getRealPathFromURI_API19(context: Context, uri: Uri): String? {
69
+ val isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT
70
+
71
+ // DocumentProvider
72
+ if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
73
+ // ExternalStorageProvider
74
+ if (isExternalStorageDocument(uri)) {
75
+ val docId = DocumentsContract.getDocumentId(uri)
76
+ val split = docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
77
+ val type = split[0]
78
+ if ("primary".equals(type, ignoreCase = true)) {
79
+ return Environment.getExternalStorageDirectory().toString() + "/" + split[1]
80
+ }
81
+
82
+ // TODO handle non-primary volumes
83
+ } else if (isDownloadsDocument(uri)) {
84
+ val id = DocumentsContract.getDocumentId(uri)
85
+ val contentUri = ContentUris.withAppendedId(
86
+ Uri.parse("content://downloads/public_downloads"), java.lang.Long.valueOf(id))
87
+ return getDataColumn(context, contentUri, null, null)
88
+ } else if (isMediaDocument(uri)) {
89
+ val docId = DocumentsContract.getDocumentId(uri)
90
+ val split = docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
91
+ val type = split[0]
92
+ var contentUri: Uri? = null
93
+ if ("image" == type) {
94
+ contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
95
+ } else if ("video" == type) {
96
+ contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI
97
+ } else if ("audio" == type) {
98
+ contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
99
+ }
100
+ val selection = "_id=?"
101
+ val selectionArgs = arrayOf(
102
+ split[1]
103
+ )
104
+ return getDataColumn(context, contentUri, selection, selectionArgs)
105
+ }
106
+ } else if ("content".equals(uri.scheme, ignoreCase = true)) {
107
+
108
+ // Return the remote address
109
+ return if (isGooglePhotosUri(uri)) uri.lastPathSegment else getDataColumn(context, uri, null, null)
110
+ } else if ("file".equals(uri.scheme, ignoreCase = true)) {
111
+ return uri.path
112
+ }
113
+ return null
114
+ }
115
+
116
+ /**
117
+ * Get the value of the data column for this Uri. This is useful for
118
+ * MediaStore Uris, and other file-based ContentProviders.
119
+ *
120
+ * @param context The context.
121
+ * @param uri The Uri to query.
122
+ * @param selection (Optional) Filter used in the query.
123
+ * @param selectionArgs (Optional) Selection arguments used in the query.
124
+ * @return The value of the _data column, which is typically a file path.
125
+ */
126
+ fun getDataColumn(context: Context, uri: Uri?, selection: String?,
127
+ selectionArgs: Array<String>?): String? {
128
+ var cursor: Cursor? = null
129
+ val column = "_data"
130
+ val projection = arrayOf(
131
+ column
132
+ )
133
+ try {
134
+ cursor = context.contentResolver.query(uri!!, projection, selection, selectionArgs,
135
+ null)
136
+ if (cursor != null && cursor.moveToFirst()) {
137
+ val index = cursor.getColumnIndexOrThrow(column)
138
+ return cursor.getString(index)
139
+ }
140
+ } finally {
141
+ cursor?.close()
142
+ }
143
+ return null
144
+ }
145
+
146
+ /**
147
+ * @param uri The Uri to check.
148
+ * @return Whether the Uri authority is ExternalStorageProvider.
149
+ */
150
+ fun isExternalStorageDocument(uri: Uri): Boolean {
151
+ return "com.android.externalstorage.documents" == uri.authority
152
+ }
153
+
154
+ /**
155
+ * @param uri The Uri to check.
156
+ * @return Whether the Uri authority is DownloadsProvider.
157
+ */
158
+ fun isDownloadsDocument(uri: Uri): Boolean {
159
+ return "com.android.providers.downloads.documents" == uri.authority
160
+ }
161
+
162
+ /**
163
+ * @param uri The Uri to check.
164
+ * @return Whether the Uri authority is MediaProvider.
165
+ */
166
+ fun isMediaDocument(uri: Uri): Boolean {
167
+ return "com.android.providers.media.documents" == uri.authority
168
+ }
169
+
170
+ /**
171
+ * @param uri The Uri to check.
172
+ * @return Whether the Uri authority is Google Photos.
173
+ */
174
+ fun isGooglePhotosUri(uri: Uri): Boolean {
175
+ return "com.google.android.apps.photos.content" == uri.authority
176
+ }
177
+ }
@@ -0,0 +1,117 @@
1
+ package com.reactnativecompressor.Utils
2
+
3
+ import android.util.Log
4
+ import com.facebook.react.bridge.Arguments
5
+ import com.facebook.react.bridge.Promise
6
+ import com.facebook.react.bridge.ReactApplicationContext
7
+ import com.facebook.react.bridge.ReadableMap
8
+ import io.github.lizhangqu.coreprogress.ProgressHelper
9
+ import io.github.lizhangqu.coreprogress.ProgressUIListener
10
+ import okhttp3.Call
11
+ import okhttp3.Callback
12
+ import okhttp3.MediaType
13
+ import okhttp3.MediaType.Companion.toMediaTypeOrNull
14
+ import okhttp3.OkHttpClient
15
+ import okhttp3.Request.Builder
16
+ import okhttp3.RequestBody
17
+ import okhttp3.Response
18
+ import java.io.File
19
+ import java.io.IOException
20
+ import java.util.Locale
21
+
22
+ object Uploader {
23
+ private const val TAG = "asyncTaskUploader"
24
+
25
+ fun upload(fileUrl: String, _options: ReadableMap?, reactContext: ReactApplicationContext, promise: Promise) {
26
+ val options = _options?.let { UploaderHelper.fromMap(it) }
27
+ val uploadableFile = File(fileUrl)
28
+ val url = options?.url
29
+ var contentType: String? = "video"
30
+ val okHttpClient = OkHttpClient()
31
+ val builder = Builder()
32
+ if (url != null) {
33
+ builder.url(url)
34
+ }
35
+ val headerIterator = options!!.headers!!.keySetIterator()
36
+ while (headerIterator.hasNextKey()) {
37
+ val key = headerIterator.nextKey()
38
+ val value = options.headers!!.getString(key)
39
+ Log.d(TAG, "$key value: $value")
40
+ builder.addHeader(key, value.toString())
41
+ if (key.lowercase(Locale.getDefault()) === "content-type:") {
42
+ contentType = value
43
+ }
44
+ }
45
+ val mediaType: MediaType = contentType?.toMediaTypeOrNull()!!;
46
+ val body = RequestBody.create(mediaType, uploadableFile)
47
+ val requestBody = ProgressHelper.withProgress(body, object : ProgressUIListener() {
48
+ //if you don't need this method, don't override this methd. It isn't an abstract method, just an empty method.
49
+ override fun onUIProgressStart(totalBytes: Long) {
50
+ super.onUIProgressStart(totalBytes)
51
+ Log.d(TAG, "onUIProgressStart:$totalBytes")
52
+ }
53
+
54
+ override fun onUIProgressChanged(numBytes: Long, totalBytes: Long, percent: Float, speed: Float) {
55
+ EventEmitterHandler.sendUploadProgressEvent(numBytes,totalBytes,options.uuid)
56
+ Log.d(TAG, "=============start===============")
57
+ Log.d(TAG, "numBytes:$numBytes")
58
+ Log.d(TAG, "totalBytes:$totalBytes")
59
+ Log.d(TAG, "percent:$percent")
60
+ Log.d(TAG, "speed:$speed")
61
+ Log.d(TAG, "============= end ===============")
62
+ }
63
+
64
+ //if you don't need this method, don't override this methd. It isn't an abstract method, just an empty method.
65
+ override fun onUIProgressFinish() {
66
+ super.onUIProgressFinish()
67
+ Log.d(TAG, "onUIProgressFinish:")
68
+ }
69
+ })
70
+ builder.put(requestBody)
71
+ val call = okHttpClient.newCall(builder.build())
72
+ call.enqueue(object : Callback {
73
+ override fun onFailure(call: Call, e: IOException) {
74
+ Log.d(TAG, "=============onFailure===============")
75
+ promise.reject("")
76
+ e.printStackTrace()
77
+ }
78
+
79
+ @Throws(IOException::class)
80
+ override fun onResponse(call: Call, response: Response) {
81
+ Log.d(TAG, "=============onResponse===============")
82
+ Log.d(TAG, "request headers:" + response.request.headers)
83
+ Log.d(TAG, "response code:" + response.code)
84
+ Log.d(TAG, "response headers:" + response.headers)
85
+ Log.d(TAG, "response body:" + response.body!!.string())
86
+ val param = Arguments.createMap()
87
+ param.putInt("status", response.code)
88
+ promise.resolve(param)
89
+ }
90
+ })
91
+ }
92
+ }
93
+
94
+
95
+ class UploaderHelper {
96
+ var uuid: String? = null
97
+ var method: String? = null
98
+ var headers: ReadableMap? = null
99
+ var url: String? = null
100
+
101
+ companion object {
102
+ fun fromMap(map: ReadableMap): UploaderHelper {
103
+ val options = UploaderHelper()
104
+ val iterator = map.keySetIterator()
105
+ while (iterator.hasNextKey()) {
106
+ val key = iterator.nextKey()
107
+ when (key) {
108
+ "uuid" -> options.uuid = map.getString(key)
109
+ "method" -> options.method = map.getString(key)
110
+ "headers" -> options.headers = map.getMap(key)
111
+ "url" -> options.url = map.getString(key)
112
+ }
113
+ }
114
+ return options
115
+ }
116
+ }
117
+ }
@@ -0,0 +1,162 @@
1
+ package com.reactnativecompressor.Utils
2
+
3
+ import android.content.ContentResolver
4
+ import android.content.res.AssetFileDescriptor
5
+ import android.net.Uri
6
+ import android.provider.OpenableColumns
7
+ import android.util.Log
8
+ import com.facebook.react.bridge.Promise
9
+ import com.facebook.react.bridge.ReactApplicationContext
10
+ import numan.dev.videocompressor.VideoCompressTask
11
+ import numan.dev.videocompressor.VideoCompressor
12
+ import java.io.FileNotFoundException
13
+ import java.io.IOException
14
+ import java.net.HttpURLConnection
15
+ import java.net.URL
16
+ import java.util.UUID
17
+
18
+ object Utils {
19
+ private const val TAG = "react-native-compessor"
20
+ var compressorExports: MutableMap<String, VideoCompressTask> = HashMap()
21
+ @JvmStatic
22
+ fun generateCacheFilePath(extension: String, reactContext: ReactApplicationContext): String {
23
+ val outputDir = reactContext.cacheDir
24
+ return String.format("%s/%s.$extension", outputDir.path, UUID.randomUUID().toString())
25
+ }
26
+
27
+ @JvmStatic
28
+ fun compressVideo(srcPath: String, destinationPath: String, resultWidth: Int, resultHeight: Int, videoBitRate: Float, uuid: String,progressDivider:Int, promise: Promise, reactContext: ReactApplicationContext) {
29
+ val currentVideoCompression = intArrayOf(0)
30
+ try {
31
+ val export = VideoCompressor.convertVideo(srcPath, destinationPath, resultWidth, resultHeight, videoBitRate.toInt(), object : VideoCompressor.ProgressListener {
32
+ override fun onStart() {
33
+ //convert start
34
+ }
35
+
36
+ override fun onFinish(result: Boolean) {
37
+ val fileUrl = "file://$destinationPath"
38
+ //convert finish,result(true is success,false is fail)
39
+ promise.resolve(fileUrl)
40
+ MediaCache.removeCompletedImagePath(fileUrl)
41
+ }
42
+
43
+ override fun onError(errorMessage: String) {
44
+ if (errorMessage == "class java.lang.AssertionError") {
45
+ promise.resolve("file://$srcPath")
46
+ } else {
47
+ promise.reject("Compression has canncelled")
48
+ }
49
+ }
50
+
51
+ override fun onProgress(percent: Float) {
52
+ val roundProgress = Math.round(percent)
53
+ if (progressDivider==0||(roundProgress % progressDivider == 0 && roundProgress > currentVideoCompression[0])) {
54
+ EventEmitterHandler.emitVideoCompressProgress((percent / 100).toDouble(),uuid)
55
+ currentVideoCompression[0] = roundProgress
56
+ }
57
+ }
58
+ })
59
+ compressorExports[uuid] = export
60
+ } catch (ex: Exception) {
61
+ promise.reject(ex)
62
+ } finally {
63
+ currentVideoCompression[0] = 0
64
+ }
65
+ }
66
+
67
+ fun cancelCompressionHelper(uuid: String) {
68
+ try {
69
+ val export = compressorExports[uuid]
70
+ export!!.cancel(true)
71
+ } catch (ex: Exception) {
72
+ }
73
+ }
74
+
75
+ @JvmStatic
76
+ fun getFileSizeFromURL(urlString: String?): Int {
77
+ val url: URL
78
+ var conn: HttpURLConnection? = null
79
+ return try {
80
+ url = URL(urlString)
81
+ conn = url.openConnection() as HttpURLConnection
82
+ conn.requestMethod = "HEAD"
83
+ conn!!.inputStream
84
+ conn.contentLength
85
+ } catch (e: IOException) {
86
+ throw RuntimeException(e)
87
+ } finally {
88
+ conn?.disconnect()
89
+ }
90
+ }
91
+
92
+ @JvmStatic
93
+ fun getRealPath(fileUrl: String?, reactContext: ReactApplicationContext, vararg args: Any?): String? {
94
+ var fileUrl = fileUrl
95
+ if (fileUrl!!.startsWith("content://")) {
96
+ try {
97
+ val uri = Uri.parse(fileUrl)
98
+ fileUrl = RealPathUtil.getRealPath(reactContext, uri)
99
+ } catch (ex: Exception) {
100
+ Log.d(TAG, " Please see this issue: https://github.com/numandev1/react-native-compressor/issues/25")
101
+ }
102
+ } else if (fileUrl.startsWith("http://") || fileUrl.startsWith("https://")) {
103
+ val uuid: String = if (args.size > 0) args[0].toString() else ""
104
+ val progressDivider: Int = if (args.size > 1) args[1] as Int else 0
105
+ fileUrl = Downloader.downloadMediaWithProgress(fileUrl, uuid,progressDivider, reactContext)
106
+ Log.d(TAG, "getRealPath: $fileUrl")
107
+ }
108
+ return fileUrl
109
+ }
110
+
111
+
112
+
113
+ fun getFileSize(filePath: String, promise: Promise, reactContext:ReactApplicationContext) {
114
+ var filePath: String? = filePath
115
+ if (filePath!!.startsWith("http://") || filePath.startsWith("https://")) {
116
+ promise.resolve(getFileSizeFromURL(filePath))
117
+ } else {
118
+ filePath = getRealPath(filePath, reactContext)
119
+ val uri = Uri.parse(filePath)
120
+ val contentResolver = reactContext.contentResolver
121
+ val fileSize = getLength(uri, contentResolver)
122
+ if (fileSize >= 0) {
123
+ promise.resolve(fileSize.toString())
124
+ } else {
125
+ promise.resolve("")
126
+ }
127
+ }
128
+ }
129
+
130
+ fun getLength(uri: Uri, contentResolver: ContentResolver): Long {
131
+ var assetFileDescriptor: AssetFileDescriptor? = null
132
+ try {
133
+ assetFileDescriptor = contentResolver.openAssetFileDescriptor(uri, "r")
134
+ } catch (e: FileNotFoundException) {
135
+ // Do nothing
136
+ }
137
+ val length = assetFileDescriptor?.length ?: -1L
138
+ if (length != -1L) {
139
+ return length
140
+ }
141
+ return if (ContentResolver.SCHEME_CONTENT == uri.scheme) {
142
+ val cursor = contentResolver.query(uri, arrayOf(OpenableColumns.SIZE), null, null, null)
143
+ if (cursor != null) {
144
+ try {
145
+ val sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE)
146
+ if (sizeIndex != -1 && cursor.moveToFirst()) {
147
+ return try {
148
+ cursor.getLong(sizeIndex)
149
+ } catch (ignored: Throwable) {
150
+ -1L
151
+ }
152
+ }
153
+ } finally {
154
+ cursor.close()
155
+ }
156
+ }
157
+ -1L
158
+ } else {
159
+ -1L
160
+ }
161
+ }
162
+ }
@@ -0,0 +1,158 @@
1
+ package com.reactnativecompressor.Utils
2
+
3
+ import android.content.Context
4
+ import android.graphics.Bitmap
5
+ import android.graphics.BitmapFactory
6
+ import android.media.MediaMetadataRetriever
7
+ import android.net.Uri
8
+ import android.os.Build
9
+ import android.text.TextUtils
10
+ import android.webkit.URLUtil
11
+ import androidx.annotation.RequiresApi
12
+ import com.facebook.react.bridge.Arguments
13
+ import com.facebook.react.bridge.GuardedResultAsyncTask
14
+ import com.facebook.react.bridge.Promise
15
+ import com.facebook.react.bridge.ReactApplicationContext
16
+ import com.facebook.react.bridge.ReactContext
17
+ import com.facebook.react.bridge.ReactMethod
18
+ import com.facebook.react.bridge.ReadableMap
19
+ import java.io.File
20
+ import java.io.FileOutputStream
21
+ import java.io.IOException
22
+ import java.io.OutputStream
23
+ import java.io.UnsupportedEncodingException
24
+ import java.lang.ref.WeakReference
25
+ import java.net.URLDecoder
26
+ import java.util.UUID
27
+
28
+
29
+ class CreateVideoThumbnail(private val reactContext: ReactApplicationContext) {
30
+ @ReactMethod
31
+ fun create(fileUrl:String,options: ReadableMap, promise: Promise) {
32
+ ProcessDataTask(reactContext,fileUrl, promise, options).execute()
33
+ }
34
+
35
+ private class ProcessDataTask(reactContext: ReactContext,private val filePath:String, private val promise: Promise, private val options: ReadableMap) : GuardedResultAsyncTask<ReadableMap?>(reactContext.exceptionHandler) {
36
+ private val weakContext: WeakReference<Context>
37
+
38
+ init {
39
+ weakContext = WeakReference(reactContext.applicationContext)
40
+ }
41
+
42
+ @RequiresApi(Build.VERSION_CODES.N)
43
+ override fun doInBackgroundGuarded(): ReadableMap? {
44
+ val format = "jpeg"
45
+ val cacheName = if (options.hasKey("cacheName")) options.getString("cacheName") else ""
46
+ val thumbnailDir = weakContext.get()!!.applicationContext.cacheDir.absolutePath + "/thumbnails"
47
+ val cacheDir = createDirIfNotExists(thumbnailDir)
48
+ if (!TextUtils.isEmpty(cacheName)) {
49
+ val file = File(thumbnailDir, "$cacheName.$format")
50
+ if (file.exists()) {
51
+ val map = Arguments.createMap()
52
+ map.putString("path", "file://" + file.absolutePath)
53
+ val image = BitmapFactory.decodeFile(file.absolutePath)
54
+ map.putDouble("size", image.byteCount.toDouble())
55
+ map.putString("mime", "image/$format")
56
+ map.putDouble("width", image.width.toDouble())
57
+ map.putDouble("height", image.height.toDouble())
58
+ return map
59
+ }
60
+ }
61
+ val headers: Map<String, String> = if (options.hasKey("headers")) options.getMap("headers")!!.toHashMap() as Map<String, String> else HashMap<String, String>()
62
+ val fileName = if (TextUtils.isEmpty(cacheName)) "thumb-" + UUID.randomUUID().toString() else "$cacheName.$format"
63
+ var fOut: OutputStream? = null
64
+ try {
65
+ val file = File(cacheDir, fileName)
66
+ val context = weakContext.get()
67
+ val image = getBitmapAtTime(context, filePath, 0, headers)
68
+ file.createNewFile()
69
+ fOut = FileOutputStream(file)
70
+
71
+ // 100 means no compression, the lower you go, the stronger the compression
72
+ image.compress(Bitmap.CompressFormat.JPEG, 90, fOut)
73
+ fOut.flush()
74
+ fOut.close()
75
+
76
+ val map = Arguments.createMap()
77
+ map.putString("path", "file://" + file.absolutePath)
78
+ map.putDouble("size", image.byteCount.toDouble())
79
+ map.putString("mime", "image/$format")
80
+ map.putDouble("width", image.width.toDouble())
81
+ map.putDouble("height", image.height.toDouble())
82
+ return map
83
+ } catch (e: Exception) {
84
+ promise.reject("CreateVideoThumbnail_ERROR", e)
85
+ }
86
+ return null
87
+ }
88
+
89
+ override fun onPostExecuteGuarded(readableArray: ReadableMap?) {
90
+ promise.resolve(readableArray)
91
+ }
92
+ }
93
+
94
+ companion object {
95
+ // delete previously added files one by one untill requred space is available
96
+ fun clearCache(cacheDir: String?,promise:Promise, reactContext: ReactApplicationContext) {
97
+ val cacheDirectory=cacheDir?.takeIf { it.isNotEmpty() } ?:"/thumbnails"
98
+ val thumbnailDir: String = reactContext.getApplicationContext().getCacheDir().getAbsolutePath() + cacheDirectory
99
+ val thumbnailDirFile = createDirIfNotExists(thumbnailDir)
100
+
101
+ if (thumbnailDirFile != null) {
102
+ val files = thumbnailDirFile.listFiles()
103
+
104
+ // Loop through the files and delete them
105
+ if (files != null) {
106
+ for (file in files) {
107
+ if (file.isFile) {
108
+ file.delete()
109
+ }
110
+ }
111
+ }
112
+ }
113
+ promise.resolve("done")
114
+ }
115
+
116
+ private fun createDirIfNotExists(path: String): File {
117
+ val dir = File(path)
118
+ if (dir.exists()) {
119
+ return dir
120
+ }
121
+ try {
122
+ dir.mkdirs()
123
+ // Add .nomedia to hide the thumbnail directory from gallery
124
+ val noMedia = File(path, ".nomedia")
125
+ noMedia.createNewFile()
126
+ } catch (e: IOException) {
127
+ e.printStackTrace()
128
+ }
129
+ return dir
130
+ }
131
+
132
+ private fun getBitmapAtTime(context: Context?, filePath: String?, time: Int, headers: Map<String, String>): Bitmap {
133
+ val retriever = MediaMetadataRetriever()
134
+ if (URLUtil.isFileUrl(filePath)) {
135
+ val decodedPath: String?
136
+ decodedPath = try {
137
+ URLDecoder.decode(filePath, "UTF-8")
138
+ } catch (e: UnsupportedEncodingException) {
139
+ filePath
140
+ }
141
+ retriever.setDataSource(decodedPath!!.replace("file://", ""))
142
+ } else if (filePath!!.contains("content://")) {
143
+ retriever.setDataSource(context, Uri.parse(filePath))
144
+ } else {
145
+ check(Build.VERSION.SDK_INT >= 14) { "Remote videos aren't supported on sdk_version < 14" }
146
+ retriever.setDataSource(filePath, headers)
147
+ }
148
+ val image = retriever.getFrameAtTime((time * 1000).toLong(), MediaMetadataRetriever.OPTION_CLOSEST_SYNC)
149
+ try {
150
+ retriever.release()
151
+ } catch (e: IOException) {
152
+ throw RuntimeException(e)
153
+ }
154
+ checkNotNull(image) { "File doesn't exist or not supported" }
155
+ return image
156
+ }
157
+ }
158
+ }