react-native-compressor 1.18.2 → 2.0.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/android/CMakeLists.txt +27 -0
- package/android/build.gradle +56 -58
- package/android/gradle.properties +5 -5
- package/android/src/main/AndroidManifestNew.xml +3 -0
- package/android/src/main/cpp/cpp-adapter.cpp +8 -0
- package/android/src/main/java/com/margelo/nitro/compressor/HybridCompressor.kt +213 -0
- package/android/src/main/java/com/reactnativecompressor/Image/ImageCompressor.kt +41 -21
- package/android/src/main/java/com/reactnativecompressor/NitroCompressorPackage.kt +30 -0
- package/android/src/main/java/com/reactnativecompressor/NitroPromiseAdapter.kt +55 -0
- package/android/src/main/java/com/reactnativecompressor/Utils/EventEmitterHandler.kt +49 -45
- package/android/src/main/java/com/reactnativecompressor/Utils/Utils.kt +1 -1
- package/android/src/main/java/com/reactnativecompressor/Video/AutoVideoCompression.kt +1 -1
- package/android/src/main/java/com/reactnativecompressor/Video/VideoCompressionProfile.kt +21 -11
- package/android/src/main/java/com/reactnativecompressor/Video/VideoCompressor/compressor/Compressor.kt +311 -76
- package/android/src/main/java/com/reactnativecompressor/Video/VideoCompressor/utils/CompressorUtils.kt +96 -5
- package/android/src/main/java/com/reactnativecompressor/Video/VideoCompressor/utils/LocationExtractor.kt +397 -0
- package/android/src/main/java/com/reactnativecompressor/Video/VideoCompressor/videoHelpers/LocationBox.kt +47 -0
- package/android/src/main/java/com/reactnativecompressor/Video/VideoCompressor/videoHelpers/MP4Builder.kt +39 -7
- package/android/src/main/java/com/reactnativecompressor/Video/VideoCompressor/videoHelpers/Mp4Movie.kt +7 -0
- package/android/src/main/java/com/reactnativecompressor/Video/VideoCompressor/videoHelpers/OutputSurface.kt +30 -4
- package/android/src/main/java/com/reactnativecompressor/Video/VideoCompressorHelper.kt +28 -2
- package/ios/Audio/FormatConverter/FormatConverter+Compressed.swift +1 -1
- package/ios/Audio/FormatConverter/FormatConverter+Utilities.swift +1 -1
- package/ios/Audio/FormatConverter/FormatConverter.swift +5 -3
- package/ios/HybridCompressor.swift +247 -0
- package/ios/Image/ImageCompressor.swift +8 -2
- package/ios/Utils/EventEmitterHandler.swift +56 -34
- package/ios/Video/NextLevelSessionExporter.swift +43 -8
- package/ios/Video/VideoMain.swift +44 -14
- package/lib/commonjs/Audio/index.js +1 -1
- package/lib/commonjs/Audio/index.js.map +1 -1
- package/lib/commonjs/Image/index.js +2 -22
- package/lib/commonjs/Image/index.js.map +1 -1
- package/lib/commonjs/Main.js +8 -8
- package/lib/commonjs/Main.js.map +1 -1
- package/lib/commonjs/Video/index.js +25 -65
- package/lib/commonjs/Video/index.js.map +1 -1
- package/lib/commonjs/specs/Compressor.nitro.js +6 -0
- package/lib/commonjs/specs/Compressor.nitro.js.map +1 -0
- package/lib/commonjs/utils/Downloader.js +8 -24
- package/lib/commonjs/utils/Downloader.js.map +1 -1
- package/lib/commonjs/utils/Uploader.js +3 -18
- package/lib/commonjs/utils/Uploader.js.map +1 -1
- package/lib/commonjs/utils/helpers.js +18 -1
- package/lib/commonjs/utils/helpers.js.map +1 -1
- package/lib/commonjs/utils/index.js +13 -13
- package/lib/commonjs/utils/index.js.map +1 -1
- package/lib/module/Audio/index.js +2 -2
- package/lib/module/Audio/index.js.map +1 -1
- package/lib/module/Image/index.js +3 -23
- package/lib/module/Image/index.js.map +1 -1
- package/lib/module/Main.js +9 -9
- package/lib/module/Main.js.map +1 -1
- package/lib/module/Video/index.js +26 -66
- package/lib/module/Video/index.js.map +1 -1
- package/lib/module/specs/Compressor.nitro.js +4 -0
- package/lib/module/specs/Compressor.nitro.js.map +1 -0
- package/lib/module/utils/Downloader.js +10 -26
- package/lib/module/utils/Downloader.js.map +1 -1
- package/lib/module/utils/Uploader.js +5 -20
- package/lib/module/utils/Uploader.js.map +1 -1
- package/lib/module/utils/helpers.js +16 -0
- package/lib/module/utils/helpers.js.map +1 -1
- package/lib/module/utils/index.js +2 -1
- package/lib/module/utils/index.js.map +1 -1
- package/lib/typescript/src/Image/index.d.ts.map +1 -1
- package/lib/typescript/src/Main.d.ts +2 -1
- package/lib/typescript/src/Main.d.ts.map +1 -1
- package/lib/typescript/src/Video/index.d.ts +1 -1
- package/lib/typescript/src/Video/index.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/specs/Compressor.nitro.d.ts +44 -0
- package/lib/typescript/src/specs/Compressor.nitro.d.ts.map +1 -0
- package/lib/typescript/src/utils/Downloader.d.ts.map +1 -1
- package/lib/typescript/src/utils/Uploader.d.ts.map +1 -1
- package/lib/typescript/src/utils/helpers.d.ts +7 -0
- package/lib/typescript/src/utils/helpers.d.ts.map +1 -1
- package/lib/typescript/src/utils/index.d.ts.map +1 -1
- package/nitrogen/generated/android/NitroCompressor+autolinking.cmake +81 -0
- package/nitrogen/generated/android/NitroCompressor+autolinking.gradle +27 -0
- package/nitrogen/generated/android/NitroCompressorOnLoad.cpp +60 -0
- package/nitrogen/generated/android/NitroCompressorOnLoad.hpp +34 -0
- package/nitrogen/generated/android/c++/JFunc_void.hpp +75 -0
- package/nitrogen/generated/android/c++/JFunc_void_double.hpp +75 -0
- package/nitrogen/generated/android/c++/JFunc_void_double_double.hpp +75 -0
- package/nitrogen/generated/android/c++/JHybridCompressorSpec.cpp +293 -0
- package/nitrogen/generated/android/c++/JHybridCompressorSpec.hpp +78 -0
- package/nitrogen/generated/android/c++/JVideoThumbnailResult.hpp +73 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/compressor/Func_void.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/compressor/Func_void_double.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/compressor/Func_void_double_double.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/compressor/HybridCompressorSpec.kt +141 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/compressor/NitroCompressorOnLoad.kt +35 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/compressor/VideoThumbnailResult.kt +71 -0
- package/nitrogen/generated/ios/c++/HybridCompressorSpecSwift.cpp +11 -0
- package/nitrogen/generated/ios/c++/HybridCompressorSpecSwift.hpp +204 -0
- package/nitrogen/generated/ios/react_native_compressor+autolinking.rb +62 -0
- package/nitrogen/generated/ios/react_native_compressor-Swift-Cxx-Bridge.cpp +89 -0
- package/nitrogen/generated/ios/react_native_compressor-Swift-Cxx-Bridge.hpp +337 -0
- package/nitrogen/generated/ios/react_native_compressor-Swift-Cxx-Umbrella.hpp +51 -0
- package/nitrogen/generated/ios/react_native_compressorAutolinking.mm +33 -0
- package/nitrogen/generated/ios/react_native_compressorAutolinking.swift +26 -0
- package/nitrogen/generated/ios/swift/Func_void.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_VideoThumbnailResult.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_double.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_double_double.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_std__exception_ptr.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_std__shared_ptr_AnyMap_.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_std__string.swift +46 -0
- package/nitrogen/generated/ios/swift/HybridCompressorSpec.swift +70 -0
- package/nitrogen/generated/ios/swift/HybridCompressorSpec_cxx.swift +493 -0
- package/nitrogen/generated/ios/swift/VideoThumbnailResult.swift +49 -0
- package/nitrogen/generated/shared/c++/HybridCompressorSpec.cpp +36 -0
- package/nitrogen/generated/shared/c++/HybridCompressorSpec.hpp +83 -0
- package/nitrogen/generated/shared/c++/VideoThumbnailResult.hpp +99 -0
- package/package.json +8 -12
- package/react-native-compressor.podspec +6 -24
- package/src/Audio/index.tsx +2 -2
- package/src/Image/index.tsx +3 -26
- package/src/Main.tsx +14 -16
- package/src/Video/index.tsx +25 -77
- package/src/specs/Compressor.nitro.ts +56 -0
- package/src/utils/Downloader.tsx +8 -27
- package/src/utils/Uploader.tsx +12 -24
- package/src/utils/helpers.ts +18 -0
- package/src/utils/index.tsx +3 -2
- package/android/src/main/java/com/reactnativecompressor/CompressorModule.kt +0 -180
- package/android/src/main/java/com/reactnativecompressor/CompressorPackage.kt +0 -34
- package/android/src/newarch/CompressorSpec.kt +0 -5
- package/android/src/oldarch/CompressorSpec.kt +0 -36
- package/ios/Compressor-Bridging-Header.h +0 -6
- package/ios/Compressor.h +0 -5
- package/ios/Compressor.mm +0 -82
- package/ios/CompressorManager.swift +0 -117
- package/lib/commonjs/Spec/NativeCompressor.js +0 -9
- package/lib/commonjs/Spec/NativeCompressor.js.map +0 -1
- package/lib/module/Spec/NativeCompressor.js +0 -5
- package/lib/module/Spec/NativeCompressor.js.map +0 -1
- package/lib/typescript/src/Spec/NativeCompressor.d.ts +0 -30
- package/lib/typescript/src/Spec/NativeCompressor.d.ts.map +0 -1
- package/src/Spec/NativeCompressor.ts +0 -41
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
project(NitroCompressor)
|
|
2
|
+
cmake_minimum_required(VERSION 3.9.0)
|
|
3
|
+
|
|
4
|
+
set(PACKAGE_NAME NitroCompressor)
|
|
5
|
+
set(CMAKE_VERBOSE_MAKEFILE ON)
|
|
6
|
+
set(CMAKE_CXX_STANDARD 20)
|
|
7
|
+
|
|
8
|
+
# Define the C++ library and add all of our own sources.
|
|
9
|
+
add_library(${PACKAGE_NAME} SHARED
|
|
10
|
+
src/main/cpp/cpp-adapter.cpp
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
# Add all files generated by Nitrogen (sources, headers, prefab links).
|
|
14
|
+
include(${CMAKE_SOURCE_DIR}/../nitrogen/generated/android/NitroCompressor+autolinking.cmake)
|
|
15
|
+
|
|
16
|
+
# Local includes
|
|
17
|
+
include_directories(
|
|
18
|
+
"src/main/cpp"
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
find_library(LOG_LIB log)
|
|
22
|
+
|
|
23
|
+
target_link_libraries(
|
|
24
|
+
${PACKAGE_NAME}
|
|
25
|
+
${LOG_LIB}
|
|
26
|
+
android
|
|
27
|
+
)
|
package/android/build.gradle
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
buildscript {
|
|
2
|
-
|
|
2
|
+
// Buildscript is evaluated before everything else so we can't use getExtOrDefault
|
|
3
3
|
def kotlin_version = rootProject.ext.has("kotlinVersion") ? rootProject.ext.get("kotlinVersion") : project.properties["Compressor_kotlinVersion"]
|
|
4
4
|
|
|
5
5
|
repositories {
|
|
@@ -8,25 +8,21 @@ buildscript {
|
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
dependencies {
|
|
11
|
-
classpath "com.android.tools.build:gradle:
|
|
11
|
+
classpath "com.android.tools.build:gradle:9.2.1"
|
|
12
12
|
// noinspection DifferentKotlinGradleVersion
|
|
13
13
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
def
|
|
18
|
-
|
|
17
|
+
def reactNativeArchitectures() {
|
|
18
|
+
def value = rootProject.getProperties().get("reactNativeArchitectures")
|
|
19
|
+
return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
apply plugin: "com.android.library"
|
|
22
23
|
apply plugin: "kotlin-android"
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
def appProject = rootProject.allprojects.find { it.plugins.hasPlugin('com.android.application') }
|
|
26
|
-
|
|
27
|
-
if (isNewArchitectureEnabled()) {
|
|
28
|
-
apply plugin: "com.facebook.react"
|
|
29
|
-
}
|
|
24
|
+
// Nitrogen: registers the generated Kotlin sources with this Gradle project.
|
|
25
|
+
apply from: '../nitrogen/generated/android/NitroCompressor+autolinking.gradle'
|
|
30
26
|
|
|
31
27
|
def getExtOrDefault(name) {
|
|
32
28
|
return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties["Compressor_" + name]
|
|
@@ -36,40 +32,62 @@ def getExtOrIntegerDefault(name) {
|
|
|
36
32
|
return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["Compressor_" + name]).toInteger()
|
|
37
33
|
}
|
|
38
34
|
|
|
39
|
-
def supportsNamespace() {
|
|
40
|
-
def parsed = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION.tokenize('.')
|
|
41
|
-
def major = parsed[0].toInteger()
|
|
42
|
-
def minor = parsed[1].toInteger()
|
|
43
|
-
|
|
44
|
-
// Namespace support was added in 7.3.0
|
|
45
|
-
if (major == 7 && minor >= 3) {
|
|
46
|
-
return true
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
return major >= 8
|
|
50
|
-
}
|
|
51
|
-
|
|
52
35
|
android {
|
|
53
|
-
|
|
54
|
-
namespace "com.reactnativecompressor"
|
|
36
|
+
namespace "com.reactnativecompressor"
|
|
55
37
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
38
|
+
ndkVersion getExtOrDefault("ndkVersion")
|
|
39
|
+
compileSdkVersion getExtOrIntegerDefault("compileSdkVersion")
|
|
40
|
+
|
|
41
|
+
sourceSets {
|
|
42
|
+
main {
|
|
43
|
+
manifest.srcFile "src/main/AndroidManifestNew.xml"
|
|
60
44
|
}
|
|
61
45
|
}
|
|
62
46
|
|
|
63
|
-
compileSdkVersion getExtOrIntegerDefault("compileSdkVersion")
|
|
64
|
-
|
|
65
47
|
defaultConfig {
|
|
66
48
|
minSdkVersion getExtOrIntegerDefault("minSdkVersion")
|
|
67
49
|
targetSdkVersion getExtOrIntegerDefault("targetSdkVersion")
|
|
68
|
-
|
|
50
|
+
|
|
51
|
+
externalNativeBuild {
|
|
52
|
+
cmake {
|
|
53
|
+
cppFlags "-frtti -fexceptions -Wall -Wextra -fstack-protector-all -std=c++20"
|
|
54
|
+
arguments "-DANDROID_STL=c++_shared", "-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON"
|
|
55
|
+
abiFilters(*reactNativeArchitectures())
|
|
56
|
+
}
|
|
57
|
+
}
|
|
69
58
|
}
|
|
59
|
+
|
|
60
|
+
externalNativeBuild {
|
|
61
|
+
cmake {
|
|
62
|
+
path "CMakeLists.txt"
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
70
66
|
buildFeatures {
|
|
71
|
-
|
|
67
|
+
prefab true
|
|
72
68
|
}
|
|
69
|
+
|
|
70
|
+
packagingOptions {
|
|
71
|
+
excludes = [
|
|
72
|
+
"META-INF",
|
|
73
|
+
"META-INF/**",
|
|
74
|
+
"**/libc++_shared.so",
|
|
75
|
+
"**/libfbjni.so",
|
|
76
|
+
"**/libjsi.so",
|
|
77
|
+
"**/libfolly_json.so",
|
|
78
|
+
"**/libfolly_runtime.so",
|
|
79
|
+
"**/libglog.so",
|
|
80
|
+
"**/libhermes.so",
|
|
81
|
+
"**/libhermes-executor-debug.so",
|
|
82
|
+
"**/libhermes_executor.so",
|
|
83
|
+
"**/libreactnative.so",
|
|
84
|
+
"**/libreactnativejni.so",
|
|
85
|
+
"**/libturbomodulejsijni.so",
|
|
86
|
+
"**/libreact_nativemodule_core.so",
|
|
87
|
+
"**/libjscexecutor.so"
|
|
88
|
+
]
|
|
89
|
+
}
|
|
90
|
+
|
|
73
91
|
buildTypes {
|
|
74
92
|
release {
|
|
75
93
|
minifyEnabled false
|
|
@@ -81,22 +99,8 @@ android {
|
|
|
81
99
|
}
|
|
82
100
|
|
|
83
101
|
compileOptions {
|
|
84
|
-
sourceCompatibility JavaVersion.
|
|
85
|
-
targetCompatibility JavaVersion.
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
sourceSets {
|
|
89
|
-
main {
|
|
90
|
-
if (isNewArchitectureEnabled()) {
|
|
91
|
-
java.srcDirs += [
|
|
92
|
-
"src/newarch",
|
|
93
|
-
// This is needed to build Kotlin project with NewArch enabled
|
|
94
|
-
"${project.buildDir}/generated/source/codegen/java"
|
|
95
|
-
]
|
|
96
|
-
} else {
|
|
97
|
-
java.srcDirs += ["src/oldarch"]
|
|
98
|
-
}
|
|
99
|
-
}
|
|
102
|
+
sourceCompatibility JavaVersion.VERSION_17
|
|
103
|
+
targetCompatibility JavaVersion.VERSION_17
|
|
100
104
|
}
|
|
101
105
|
}
|
|
102
106
|
|
|
@@ -114,6 +118,8 @@ dependencies {
|
|
|
114
118
|
//noinspection GradleDynamicVersion
|
|
115
119
|
implementation "com.facebook.react:react-native:+"
|
|
116
120
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
|
121
|
+
// Nitro core (provided as a Gradle subproject by autolinking).
|
|
122
|
+
implementation project(":react-native-nitro-modules")
|
|
117
123
|
|
|
118
124
|
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4"
|
|
119
125
|
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4"
|
|
@@ -121,11 +127,3 @@ dependencies {
|
|
|
121
127
|
implementation 'com.github.kaushik-naik:TAndroidLame:277c2ab4b0'
|
|
122
128
|
implementation 'javazoom:jlayer:1.0.1'
|
|
123
129
|
}
|
|
124
|
-
|
|
125
|
-
if (isNewArchitectureEnabled()) {
|
|
126
|
-
react {
|
|
127
|
-
jsRootDir = file("../src/")
|
|
128
|
-
libraryName = "Compressor"
|
|
129
|
-
codegenJavaPackageName = "com.reactnativecompressor"
|
|
130
|
-
}
|
|
131
|
-
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
Compressor_kotlinVersion=1.
|
|
2
|
-
Compressor_minSdkVersion=
|
|
3
|
-
Compressor_targetSdkVersion=
|
|
4
|
-
Compressor_compileSdkVersion=
|
|
5
|
-
|
|
1
|
+
Compressor_kotlinVersion=2.1.20
|
|
2
|
+
Compressor_minSdkVersion=24
|
|
3
|
+
Compressor_targetSdkVersion=36
|
|
4
|
+
Compressor_compileSdkVersion=36
|
|
5
|
+
Compressor_ndkVersion=27.1.12297006
|
|
@@ -1,2 +1,5 @@
|
|
|
1
1
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
2
|
+
<!-- activateBackgroundTask acquires a PARTIAL_WAKE_LOCK; declare it here so it
|
|
3
|
+
merges into consuming apps (otherwise wakeLock.acquire() throws SecurityException). -->
|
|
4
|
+
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
|
2
5
|
</manifest>
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
package com.margelo.nitro.compressor
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.Arguments
|
|
4
|
+
import com.facebook.react.bridge.ReadableMap
|
|
5
|
+
import com.facebook.react.bridge.WritableMap
|
|
6
|
+
import com.margelo.nitro.NitroModules
|
|
7
|
+
import com.margelo.nitro.core.AnyMap
|
|
8
|
+
import com.margelo.nitro.core.Promise
|
|
9
|
+
import com.reactnativecompressor.Audio.AudioMain
|
|
10
|
+
import com.reactnativecompressor.Image.ImageMain
|
|
11
|
+
import com.reactnativecompressor.NitroPromiseAdapter
|
|
12
|
+
import com.reactnativecompressor.Utils.CreateVideoThumbnailClass
|
|
13
|
+
import com.reactnativecompressor.Utils.Downloader
|
|
14
|
+
import com.reactnativecompressor.Utils.EventEmitterHandler
|
|
15
|
+
import com.reactnativecompressor.Utils.Uploader
|
|
16
|
+
import com.reactnativecompressor.Utils.Utils
|
|
17
|
+
import com.reactnativecompressor.Video.VideoMain
|
|
18
|
+
import java.util.UUID
|
|
19
|
+
import java.util.concurrent.Executors
|
|
20
|
+
import com.facebook.react.bridge.Promise as RNPromise
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Nitro HybridObject implementation of the single `Compressor` native module.
|
|
24
|
+
*
|
|
25
|
+
* Thin binding layer: it converts Nitro's `AnyMap` options into the
|
|
26
|
+
* `ReadableMap` the existing domain code already consumes, bridges the Nitro
|
|
27
|
+
* `Promise` to the domain layer's `com.facebook.react.bridge.Promise` via
|
|
28
|
+
* [NitroPromiseAdapter], and registers progress callbacks (keyed by `uuid`)
|
|
29
|
+
* with [EventEmitterHandler]. All heavy logic stays in the domain classes,
|
|
30
|
+
* which run on a background executor so the JS thread is never blocked.
|
|
31
|
+
*/
|
|
32
|
+
class HybridCompressor : HybridCompressorSpec() {
|
|
33
|
+
private val reactContext
|
|
34
|
+
get() = NitroModules.applicationContext
|
|
35
|
+
?: throw IllegalStateException("react-native-compressor: ReactApplicationContext is not available")
|
|
36
|
+
|
|
37
|
+
private val executor = Executors.newCachedThreadPool()
|
|
38
|
+
|
|
39
|
+
private val imageMain by lazy { ImageMain(reactContext) }
|
|
40
|
+
private val videoMain by lazy { VideoMain(reactContext) }
|
|
41
|
+
private val audioMain by lazy { AudioMain(reactContext) }
|
|
42
|
+
private val uploader by lazy { Uploader(reactContext) }
|
|
43
|
+
private val videoThumbnail by lazy { CreateVideoThumbnailClass(reactContext) }
|
|
44
|
+
|
|
45
|
+
// region Converters
|
|
46
|
+
|
|
47
|
+
private val toStringResult: (Any?) -> String = { it as? String ?: "" }
|
|
48
|
+
|
|
49
|
+
private val toAnyMapResult: (Any?) -> AnyMap = { value ->
|
|
50
|
+
val hashMap = (value as? ReadableMap)?.toHashMap() ?: HashMap()
|
|
51
|
+
AnyMap.fromMap(hashMap, true)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
private val toThumbnailResult: (Any?) -> VideoThumbnailResult = { value ->
|
|
55
|
+
val map = value as ReadableMap
|
|
56
|
+
VideoThumbnailResult(
|
|
57
|
+
if (map.hasKey("path")) map.getString("path") ?: "" else "",
|
|
58
|
+
if (map.hasKey("size")) map.getDouble("size") else 0.0,
|
|
59
|
+
if (map.hasKey("mime")) map.getString("mime") ?: "" else "",
|
|
60
|
+
if (map.hasKey("width")) map.getDouble("width") else 0.0,
|
|
61
|
+
if (map.hasKey("height")) map.getDouble("height") else 0.0,
|
|
62
|
+
)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// endregion
|
|
66
|
+
|
|
67
|
+
// region Image
|
|
68
|
+
|
|
69
|
+
override fun image_compress(imagePath: String, optionMap: AnyMap, onDownloadProgress: ((progress: Double) -> Unit)?): Promise<String> {
|
|
70
|
+
val map = toWritableMap(optionMap)
|
|
71
|
+
// Remote-image download progress is keyed by `uuid` inside the Downloader. The JS
|
|
72
|
+
// layer no longer sends one (the callback is passed directly), so mint one here.
|
|
73
|
+
val uuid = if (map.hasKey("uuid")) map.getString("uuid") ?: UUID.randomUUID().toString() else UUID.randomUUID().toString()
|
|
74
|
+
map.putString("uuid", uuid)
|
|
75
|
+
EventEmitterHandler.registerDownloadProgress(uuid, onDownloadProgress)
|
|
76
|
+
return runOnExecutor(toStringResult, { EventEmitterHandler.unregister(uuid) }) { promise ->
|
|
77
|
+
imageMain.image_compress(imagePath, map, promise)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
override fun getImageMetaData(filePath: String): Promise<AnyMap> {
|
|
82
|
+
return runOnExecutor(toAnyMapResult) { promise -> imageMain.getImageMetaData(filePath, promise) }
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// endregion
|
|
86
|
+
|
|
87
|
+
// region Video
|
|
88
|
+
|
|
89
|
+
override fun compress(
|
|
90
|
+
fileUrl: String,
|
|
91
|
+
optionMap: AnyMap,
|
|
92
|
+
onProgress: ((progress: Double) -> Unit)?,
|
|
93
|
+
onDownloadProgress: ((progress: Double) -> Unit)?,
|
|
94
|
+
): Promise<String> {
|
|
95
|
+
val map = toWritableMap(optionMap)
|
|
96
|
+
val uuid = if (map.hasKey("uuid")) map.getString("uuid") ?: "" else ""
|
|
97
|
+
EventEmitterHandler.registerVideoCompressProgress(uuid, onProgress)
|
|
98
|
+
EventEmitterHandler.registerDownloadProgress(uuid, onDownloadProgress)
|
|
99
|
+
return runOnExecutor(toStringResult, { EventEmitterHandler.unregister(uuid) }) { promise ->
|
|
100
|
+
videoMain.compress(fileUrl, map, promise)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
override fun cancelCompression(uuid: String) {
|
|
105
|
+
videoMain.cancelCompression(uuid)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
override fun getVideoMetaData(filePath: String): Promise<AnyMap> {
|
|
109
|
+
return runOnExecutor(toAnyMapResult) { promise -> videoMain.getVideoMetaData(filePath, promise) }
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
override fun activateBackgroundTask(options: AnyMap, onExpired: (() -> Unit)?): Promise<String> {
|
|
113
|
+
EventEmitterHandler.setBackgroundTaskExpiredCallback(onExpired)
|
|
114
|
+
val map = toWritableMap(options)
|
|
115
|
+
return runOnExecutor(toStringResult) { promise -> videoMain.activateBackgroundTask(map, promise) }
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
override fun deactivateBackgroundTask(options: AnyMap): Promise<String> {
|
|
119
|
+
EventEmitterHandler.setBackgroundTaskExpiredCallback(null)
|
|
120
|
+
val map = toWritableMap(options)
|
|
121
|
+
return runOnExecutor(toStringResult) { promise -> videoMain.deactivateBackgroundTask(map, promise) }
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// endregion
|
|
125
|
+
|
|
126
|
+
// region Audio
|
|
127
|
+
|
|
128
|
+
override fun compress_audio(fileUrl: String, optionMap: AnyMap): Promise<String> {
|
|
129
|
+
val map = toWritableMap(optionMap)
|
|
130
|
+
return runOnExecutor(toStringResult) { promise -> audioMain.compress_audio(fileUrl, map, promise) }
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// endregion
|
|
134
|
+
|
|
135
|
+
// region Upload / Download
|
|
136
|
+
|
|
137
|
+
override fun upload(fileUrl: String, options: AnyMap, onProgress: ((written: Double, total: Double) -> Unit)?): Promise<AnyMap> {
|
|
138
|
+
val map = toWritableMap(options)
|
|
139
|
+
val uuid = if (map.hasKey("uuid")) map.getString("uuid") ?: "" else ""
|
|
140
|
+
EventEmitterHandler.registerUploadProgress(uuid, onProgress)
|
|
141
|
+
return runOnExecutor(toAnyMapResult, { EventEmitterHandler.unregister(uuid) }) { promise ->
|
|
142
|
+
uploader.upload(fileUrl, map, reactContext, promise)
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
override fun cancelUpload(uuid: String, shouldCancelAll: Boolean) {
|
|
147
|
+
uploader.cancelUpload(uuid, shouldCancelAll)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
override fun download(fileUrl: String, options: AnyMap, onProgress: ((progress: Double) -> Unit)?): Promise<String> {
|
|
151
|
+
val uuid = if (options.contains("uuid") && options.isString("uuid")) options.getString("uuid") else ""
|
|
152
|
+
val progressDivider = if (options.contains("progressDivider") && options.isDouble("progressDivider")) options.getDouble("progressDivider").toInt() else 0
|
|
153
|
+
EventEmitterHandler.registerDownloadProgress(uuid, onProgress)
|
|
154
|
+
return runOnExecutor(toStringResult, { EventEmitterHandler.unregister(uuid) }) { promise ->
|
|
155
|
+
val downloadedFilePath = Downloader.downloadMediaWithProgress(fileUrl, uuid, progressDivider, reactContext)
|
|
156
|
+
if (downloadedFilePath != null) promise.resolve(downloadedFilePath) else promise.reject("Unable to download", "Unable to download")
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// endregion
|
|
161
|
+
|
|
162
|
+
// region Others
|
|
163
|
+
|
|
164
|
+
override fun generateFilePath(fileExtension: String): Promise<String> {
|
|
165
|
+
return runOnExecutor(toStringResult) { promise -> promise.resolve(Utils.generateCacheFilePath(fileExtension, reactContext)) }
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
override fun getRealPath(path: String, type: String): Promise<String> {
|
|
169
|
+
// Utils.getRealPath already returns a `file://`-prefixed path via slashifyFilePath,
|
|
170
|
+
// so it must not be prefixed again (that produced a malformed `file://file:///…`).
|
|
171
|
+
return runOnExecutor(toStringResult) { promise -> promise.resolve(Utils.getRealPath(path, reactContext)) }
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
override fun getFileSize(filePath: String): Promise<String> {
|
|
175
|
+
return runOnExecutor(toStringResult) { promise -> Utils.getFileSize(filePath, promise, reactContext) }
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
override fun createVideoThumbnail(fileUrl: String, options: AnyMap): Promise<VideoThumbnailResult> {
|
|
179
|
+
val map = toWritableMap(options)
|
|
180
|
+
return runOnExecutor(toThumbnailResult) { promise -> videoThumbnail.create(fileUrl, map, promise) }
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
override fun clearCache(cacheDir: String?): Promise<String> {
|
|
184
|
+
return runOnExecutor(toStringResult) { promise -> CreateVideoThumbnailClass.clearCache(cacheDir, promise, reactContext) }
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// endregion
|
|
188
|
+
|
|
189
|
+
// region Helpers
|
|
190
|
+
|
|
191
|
+
/** Convert a Nitro [AnyMap] to a React Native [WritableMap] that the domain parsers consume. */
|
|
192
|
+
private fun toWritableMap(map: AnyMap): WritableMap = Arguments.makeNativeMap(map.toHashMap())
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Runs [block] on a background thread, handing it a bridge [RNPromise] that
|
|
196
|
+
* resolves/rejects the returned Nitro [Promise]. Synchronous throws are routed
|
|
197
|
+
* to the same adapter so the Promise is never double-settled.
|
|
198
|
+
*/
|
|
199
|
+
private fun <T> runOnExecutor(convert: (Any?) -> T, onSettle: () -> Unit = {}, block: (RNPromise) -> Unit): Promise<T> {
|
|
200
|
+
val promise = Promise<T>()
|
|
201
|
+
val adapter = NitroPromiseAdapter(promise, convert, onSettle)
|
|
202
|
+
executor.execute {
|
|
203
|
+
try {
|
|
204
|
+
block(adapter)
|
|
205
|
+
} catch (e: Throwable) {
|
|
206
|
+
adapter.reject(e)
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return promise
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// endregion
|
|
213
|
+
}
|
|
@@ -9,6 +9,7 @@ import android.graphics.Paint
|
|
|
9
9
|
import android.media.ExifInterface
|
|
10
10
|
import android.net.Uri
|
|
11
11
|
import android.util.Base64
|
|
12
|
+
import android.util.Log
|
|
12
13
|
import com.facebook.react.bridge.ReactApplicationContext
|
|
13
14
|
import com.reactnativecompressor.Utils.MediaCache
|
|
14
15
|
import com.reactnativecompressor.Utils.Utils.exifAttributes
|
|
@@ -21,6 +22,8 @@ import java.io.IOException
|
|
|
21
22
|
import java.net.MalformedURLException
|
|
22
23
|
|
|
23
24
|
object ImageCompressor {
|
|
25
|
+
private const val TAG = "ImageCompressor"
|
|
26
|
+
|
|
24
27
|
fun getRNFileUrl(filePath: String?): String? {
|
|
25
28
|
var filePath = filePath
|
|
26
29
|
val returnAbleFile = File(filePath)
|
|
@@ -56,25 +59,38 @@ object ImageCompressor {
|
|
|
56
59
|
return BitmapFactory.decodeFile(filePath)
|
|
57
60
|
}
|
|
58
61
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
62
|
+
/**
|
|
63
|
+
* Strip "file://" / "content://" scheme so legacy ExifInterface can open
|
|
64
|
+
* the underlying JPEG. ExifInterface(String) only accepts raw filesystem
|
|
65
|
+
* paths — passing a URI string makes it fail silently inside the
|
|
66
|
+
* try/catch and drops every EXIF tag, including GPS.
|
|
67
|
+
*/
|
|
68
|
+
private fun normalizeToFilePath(input: String): String {
|
|
69
|
+
if (input.startsWith("file://") || input.startsWith("content://")) {
|
|
70
|
+
return Uri.parse(input).path ?: input
|
|
71
|
+
}
|
|
72
|
+
return input
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
fun copyExifInfo(imagePath: String, outputUri: String) {
|
|
76
|
+
try {
|
|
77
|
+
val sourcePath = normalizeToFilePath(imagePath)
|
|
78
|
+
val outPath = normalizeToFilePath(outputUri)
|
|
79
|
+
val sourceExif = ExifInterface(sourcePath)
|
|
80
|
+
val compressedExif = ExifInterface(outPath)
|
|
81
|
+
var copied = 0
|
|
82
|
+
for (tag in exifAttributes) {
|
|
83
|
+
val sourceValue = sourceExif.getAttribute(tag) ?: continue
|
|
84
|
+
if (compressedExif.getAttribute(tag) == null) {
|
|
85
|
+
compressedExif.setAttribute(tag, sourceValue)
|
|
86
|
+
copied++
|
|
87
|
+
}
|
|
71
88
|
}
|
|
72
|
-
|
|
89
|
+
compressedExif.saveAttributes()
|
|
90
|
+
Log.i(TAG, "copyExifInfo copied $copied tags from $sourcePath -> $outPath")
|
|
91
|
+
} catch (e: Exception) {
|
|
92
|
+
Log.w(TAG, "copyExifInfo failed for $imagePath", e)
|
|
73
93
|
}
|
|
74
|
-
compressedExif.saveAttributes()
|
|
75
|
-
} catch (e: Exception) {
|
|
76
|
-
e.printStackTrace()
|
|
77
|
-
}
|
|
78
94
|
}
|
|
79
95
|
|
|
80
96
|
fun encodeImage(imageDataByteArrayOutputStream: ByteArrayOutputStream, isBase64: Boolean, outputExtension: String?,imagePath: String?, reactContext: ReactApplicationContext?): String? {
|
|
@@ -84,10 +100,14 @@ object ImageCompressor {
|
|
|
84
100
|
} else {
|
|
85
101
|
val outputUri = generateCacheFilePath(outputExtension!!, reactContext!!)
|
|
86
102
|
try {
|
|
87
|
-
|
|
88
|
-
|
|
103
|
+
// Close the stream before ExifInterface re-opens the file so
|
|
104
|
+
// the JPEG bytes are fully flushed; otherwise saveAttributes()
|
|
105
|
+
// may truncate the in-flight write.
|
|
106
|
+
FileOutputStream(outputUri).use { fos ->
|
|
107
|
+
imageDataByteArrayOutputStream.writeTo(fos)
|
|
108
|
+
}
|
|
89
109
|
|
|
90
|
-
|
|
110
|
+
copyExifInfo(imagePath!!, outputUri)
|
|
91
111
|
|
|
92
112
|
return getRNFileUrl(outputUri)
|
|
93
113
|
} catch (e: Exception) {
|
|
@@ -262,7 +282,7 @@ object ImageCompressor {
|
|
|
262
282
|
if (bitmap == null || imagePath == null) return bitmap
|
|
263
283
|
|
|
264
284
|
return try {
|
|
265
|
-
val exif = ExifInterface(imagePath)
|
|
285
|
+
val exif = ExifInterface(normalizeToFilePath(imagePath))
|
|
266
286
|
val orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL)
|
|
267
287
|
val matrix = Matrix()
|
|
268
288
|
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
package com.reactnativecompressor
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.BaseReactPackage
|
|
4
|
+
import com.facebook.react.bridge.NativeModule
|
|
5
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
6
|
+
import com.facebook.react.module.model.ReactModuleInfoProvider
|
|
7
|
+
import com.margelo.nitro.compressor.NitroCompressorOnLoad
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Empty React package whose sole jobs are:
|
|
11
|
+
* 1. Make this library discoverable by React Native autolinking (which keys off a
|
|
12
|
+
* `ReactPackage`), so the Gradle project + native `.so` get wired into the app.
|
|
13
|
+
* 2. Load the Nitro C++ library (`libNitroCompressor.so`) on first class-load so the
|
|
14
|
+
* `Compressor` HybridObject is registered. The module itself is served by Nitro,
|
|
15
|
+
* not by `getModule`, so no native modules are returned here.
|
|
16
|
+
*
|
|
17
|
+
* Lives in the `com.reactnativecompressor` namespace (not `com.margelo.nitro.compressor`)
|
|
18
|
+
* because the React Native CLI derives the autolink import path from the Android namespace.
|
|
19
|
+
*/
|
|
20
|
+
class NitroCompressorPackage : BaseReactPackage() {
|
|
21
|
+
override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? = null
|
|
22
|
+
|
|
23
|
+
override fun getReactModuleInfoProvider(): ReactModuleInfoProvider = ReactModuleInfoProvider { HashMap() }
|
|
24
|
+
|
|
25
|
+
companion object {
|
|
26
|
+
init {
|
|
27
|
+
NitroCompressorOnLoad.initializeNative()
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
package com.reactnativecompressor
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.Promise
|
|
4
|
+
import com.facebook.react.bridge.WritableMap
|
|
5
|
+
import java.util.concurrent.atomic.AtomicBoolean
|
|
6
|
+
import com.margelo.nitro.core.Promise as NitroPromise
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Adapts the React Native bridge [Promise] to a Nitro [NitroPromise] so the existing
|
|
10
|
+
* domain methods — which speak the bridge `Promise` contract (`resolve`/`reject`) — can
|
|
11
|
+
* drive a Nitro Promise unchanged.
|
|
12
|
+
*
|
|
13
|
+
* [convert] maps the resolved bridge value to the Nitro result type [T];
|
|
14
|
+
* [onSettle] runs once on resolve/reject (used to unregister progress callbacks).
|
|
15
|
+
*/
|
|
16
|
+
class NitroPromiseAdapter<T>(
|
|
17
|
+
private val promise: NitroPromise<T>,
|
|
18
|
+
private val convert: (Any?) -> T,
|
|
19
|
+
private val onSettle: () -> Unit = {},
|
|
20
|
+
) : Promise {
|
|
21
|
+
private val settled = AtomicBoolean(false)
|
|
22
|
+
|
|
23
|
+
override fun resolve(value: Any?) {
|
|
24
|
+
if (!settled.compareAndSet(false, true)) return
|
|
25
|
+
onSettle()
|
|
26
|
+
promise.resolve(convert(value))
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
private fun rejectInternal(message: String?, throwable: Throwable?) {
|
|
30
|
+
if (!settled.compareAndSet(false, true)) return
|
|
31
|
+
onSettle()
|
|
32
|
+
promise.reject(throwable ?: Throwable(message ?: "react-native-compressor error"))
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
override fun reject(code: String?, message: String?) = rejectInternal(message ?: code, null)
|
|
36
|
+
|
|
37
|
+
override fun reject(code: String?, throwable: Throwable?) = rejectInternal(code, throwable)
|
|
38
|
+
|
|
39
|
+
override fun reject(code: String?, message: String?, throwable: Throwable?) = rejectInternal(message ?: code, throwable)
|
|
40
|
+
|
|
41
|
+
override fun reject(throwable: Throwable) = rejectInternal(throwable.message, throwable)
|
|
42
|
+
|
|
43
|
+
override fun reject(throwable: Throwable, userInfo: WritableMap) = rejectInternal(throwable.message, throwable)
|
|
44
|
+
|
|
45
|
+
override fun reject(code: String?, userInfo: WritableMap) = rejectInternal(code, null)
|
|
46
|
+
|
|
47
|
+
override fun reject(code: String?, throwable: Throwable?, userInfo: WritableMap) = rejectInternal(code, throwable)
|
|
48
|
+
|
|
49
|
+
override fun reject(code: String?, message: String?, userInfo: WritableMap) = rejectInternal(message ?: code, null)
|
|
50
|
+
|
|
51
|
+
override fun reject(code: String?, message: String?, throwable: Throwable?, userInfo: WritableMap?) = rejectInternal(message ?: code, throwable)
|
|
52
|
+
|
|
53
|
+
@Deprecated("Prefer passing a module-specific error code to JS.", ReplaceWith("reject(code, message)"))
|
|
54
|
+
override fun reject(message: String) = rejectInternal(message, null)
|
|
55
|
+
}
|