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.
- package/README.md +51 -8
- package/android/build.gradle +9 -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 +169 -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/Utils/createVideoThumbnail.kt +158 -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 +34 -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 +37 -389
- package/ios/CompressorManager.swift +107 -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/CreateVideoThumbnail.swift +111 -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 +25 -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 +47 -1
- 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 +9 -7
- 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 +9 -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 +16 -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 +7 -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 +4 -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 +25 -2
- package/src/Video/index.tsx +11 -84
- package/src/expo-plugin/compressor.ts +1 -1
- package/src/index.tsx +12 -3
- package/src/utils/Downloader.tsx +38 -0
- package/src/utils/Uploader.tsx +78 -0
- package/src/utils/index.tsx +15 -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,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
|
+
}
|