capacitor-native-video-compressor 1.1.3 → 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/README.md +51 -57
- package/android/build.gradle +5 -0
- package/android/src/main/AndroidManifest.xml +8 -14
- package/android/src/main/java/com/atomic/videocompressor/NativeVideoCompressorPlugin.java +217 -116
- package/android/src/main/java/com/atomic/videocompressor/VideoCompressionWorker.java +120 -0
- package/dist/docs.json +15 -38
- package/dist/esm/definitions.d.ts +30 -25
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/web.d.ts +3 -5
- package/dist/esm/web.js +6 -7
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +6 -7
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +6 -7
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/NativeVideoCompressorPlugin/NativeVideoCompressorPlugin.swift +0 -8
- package/package.json +1 -1
- package/android/src/main/java/com/atomic/videocompressor/VideoCompressionService.java +0 -328
package/README.md
CHANGED
|
@@ -52,18 +52,22 @@ allprojects {
|
|
|
52
52
|
}
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
-
#### compileSdk 35+ (required)
|
|
56
|
-
|
|
57
|
-
The compression foreground service declares `android:foregroundServiceType="mediaProcessing
|
|
58
|
-
`mediaProcessing` exists only from **API 35 (Android 15)
|
|
59
|
-
`
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
55
|
+
#### minSdk 35 / compileSdk 35+ (required)
|
|
56
|
+
|
|
57
|
+
The compression foreground service declares `android:foregroundServiceType="mediaProcessing"`,
|
|
58
|
+
and `mediaProcessing` exists only from **API 35 (Android 15)**. On API 34 and below the
|
|
59
|
+
system reads the type as unknown and `startForeground()` throws
|
|
60
|
+
`InvalidForegroundServiceTypeException` - the service never reaches the foreground and
|
|
61
|
+
compression is killed as soon as the user leaves the app. There is no second type to fall
|
|
62
|
+
back to, so the plugin requires `minSdkVersion` 35.
|
|
63
|
+
|
|
64
|
+
**The service covers compression only.** It stops as soon as the compress finishes. Anything
|
|
65
|
+
you do with the output afterwards - uploading it, for instance - runs without a foreground
|
|
66
|
+
service, because JS in the webview is paused while the app is backgrounded anyway: holding
|
|
67
|
+
the service open for it would declare work that does not happen. Google Play classifies
|
|
68
|
+
foreground services by *use case*, not by how many services you run, and a `dataSync`
|
|
69
|
+
declaration covering a phase that is really "waiting for the user to come back" is what got
|
|
70
|
+
version code 30 of Destini rejected.
|
|
67
71
|
|
|
68
72
|
## Web Configuration (Required)
|
|
69
73
|
|
|
@@ -225,10 +229,9 @@ The `quality` parameter determines the output resolution and compression ratio.
|
|
|
225
229
|
|
|
226
230
|
* [`compressVideo(...)`](#compressvideo)
|
|
227
231
|
* [`cancel(...)`](#cancel)
|
|
228
|
-
* [`initialize()`](#initialize)
|
|
229
232
|
* [`finishUpload(...)`](#finishupload)
|
|
233
|
+
* [`initialize()`](#initialize)
|
|
230
234
|
* [`addListener('onProgress', ...)`](#addlisteneronprogress-)
|
|
231
|
-
* [`addListener('onCancelRequested', ...)`](#addlisteneroncancelrequested-)
|
|
232
235
|
* [Interfaces](#interfaces)
|
|
233
236
|
|
|
234
237
|
</docgen-index>
|
|
@@ -242,6 +245,16 @@ The `quality` parameter determines the output resolution and compression ratio.
|
|
|
242
245
|
compressVideo(options: CompressOptions) => Promise<CompressResult>
|
|
243
246
|
```
|
|
244
247
|
|
|
248
|
+
Compress one video and resolve with the path of the result.
|
|
249
|
+
|
|
250
|
+
Android: the work runs inside a WorkManager worker, which keeps the
|
|
251
|
+
process out of the frozen cached state while the user is away — there is
|
|
252
|
+
no foreground service and no notification, because Google Play classes
|
|
253
|
+
this use case as data sync plus local processing and rejects
|
|
254
|
+
`FOREGROUND_SERVICE_MEDIA_PROCESSING` for it. WorkManager stops a worker
|
|
255
|
+
after 10 minutes, so a compress that runs longer rejects with
|
|
256
|
+
{@link CANCELLED}.
|
|
257
|
+
|
|
245
258
|
| Param | Type |
|
|
246
259
|
| ------------- | ----------------------------------------------------------- |
|
|
247
260
|
| **`options`** | <code><a href="#compressoptions">CompressOptions</a></code> |
|
|
@@ -273,68 +286,49 @@ thread (native), and none of them watch an AbortSignal.
|
|
|
273
286
|
--------------------
|
|
274
287
|
|
|
275
288
|
|
|
276
|
-
### initialize()
|
|
277
|
-
|
|
278
|
-
```typescript
|
|
279
|
-
initialize() => Promise<{ success: boolean; message?: string; }>
|
|
280
|
-
```
|
|
281
|
-
|
|
282
|
-
**Returns:** <code>Promise<{ success: boolean; message?: string; }></code>
|
|
283
|
-
|
|
284
|
-
--------------------
|
|
285
|
-
|
|
286
|
-
|
|
287
289
|
### finishUpload(...)
|
|
288
290
|
|
|
289
291
|
```typescript
|
|
290
|
-
finishUpload(options: { jobId: string; }) => Promise<
|
|
292
|
+
finishUpload(options: { jobId: string; }) => Promise<{ released: boolean; }>
|
|
291
293
|
```
|
|
292
294
|
|
|
293
|
-
|
|
294
|
-
bại đều phải gọi) để Android hạ foreground service xuống.
|
|
295
|
+
Release the worker that `holdForUpload` kept alive for this job.
|
|
295
296
|
|
|
296
|
-
|
|
297
|
+
Call it on every terminal branch of the upload, success or failure. A
|
|
298
|
+
missed call leaves the device feeding an idle job until the hold expires.
|
|
299
|
+
`released` is false when no job by that id was still being held, which is
|
|
300
|
+
the normal outcome of a late or duplicate call, not an error.
|
|
297
301
|
|
|
298
302
|
| Param | Type |
|
|
299
303
|
| ------------- | ------------------------------- |
|
|
300
304
|
| **`options`** | <code>{ jobId: string; }</code> |
|
|
301
305
|
|
|
306
|
+
**Returns:** <code>Promise<{ released: boolean; }></code>
|
|
307
|
+
|
|
302
308
|
--------------------
|
|
303
309
|
|
|
304
310
|
|
|
305
|
-
###
|
|
311
|
+
### initialize()
|
|
306
312
|
|
|
307
313
|
```typescript
|
|
308
|
-
|
|
314
|
+
initialize() => Promise<{ success: boolean; message?: string; }>
|
|
309
315
|
```
|
|
310
316
|
|
|
311
|
-
|
|
312
|
-
| ------------------ | --------------------------------------------------------------------- |
|
|
313
|
-
| **`eventName`** | <code>'onProgress'</code> |
|
|
314
|
-
| **`listenerFunc`** | <code>(info: { status: string; percent?: number; }) => void</code> |
|
|
315
|
-
|
|
316
|
-
**Returns:** <code>Promise<<a href="#pluginlistenerhandle">PluginListenerHandle</a>></code>
|
|
317
|
+
**Returns:** <code>Promise<{ success: boolean; message?: string; }></code>
|
|
317
318
|
|
|
318
319
|
--------------------
|
|
319
320
|
|
|
320
321
|
|
|
321
|
-
### addListener('
|
|
322
|
+
### addListener('onProgress', ...)
|
|
322
323
|
|
|
323
324
|
```typescript
|
|
324
|
-
addListener(eventName: '
|
|
325
|
+
addListener(eventName: 'onProgress', listenerFunc: (info: { status: string; percent?: number; }) => void) => Promise<PluginListenerHandle>
|
|
325
326
|
```
|
|
326
327
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
chạy. Bỏ qua event này nghĩa là nút Huỷ không thật sự huỷ, và Play đánh
|
|
332
|
-
trượt tiêu chí "can be terminated or stopped by the user".
|
|
333
|
-
|
|
334
|
-
| Param | Type |
|
|
335
|
-
| ------------------ | -------------------------------- |
|
|
336
|
-
| **`eventName`** | <code>'onCancelRequested'</code> |
|
|
337
|
-
| **`listenerFunc`** | <code>() => void</code> |
|
|
328
|
+
| Param | Type |
|
|
329
|
+
| ------------------ | --------------------------------------------------------------------- |
|
|
330
|
+
| **`eventName`** | <code>'onProgress'</code> |
|
|
331
|
+
| **`listenerFunc`** | <code>(info: { status: string; percent?: number; }) => void</code> |
|
|
338
332
|
|
|
339
333
|
**Returns:** <code>Promise<<a href="#pluginlistenerhandle">PluginListenerHandle</a>></code>
|
|
340
334
|
|
|
@@ -354,13 +348,13 @@ trượt tiêu chí "can be terminated or stopped by the user".
|
|
|
354
348
|
|
|
355
349
|
#### CompressOptions
|
|
356
350
|
|
|
357
|
-
| Prop | Type | Description
|
|
358
|
-
| ------------------- | ------------------------------------------------------------------------------- |
|
|
359
|
-
| **`sourcePath`** | <code>string</code> |
|
|
360
|
-
| **`destPath`** | <code>string</code> |
|
|
361
|
-
| **`quality`** | <code>'VERY_HIGH' \| 'HIGH' \| 'MEDIUM' \| 'LOW' \| 'VERY_LOW' \| '360P'</code> |
|
|
362
|
-
| **`jobId`** | <code>string</code> | Names this compress so `cancel({ jobId })` can target it. Every platform compresses one video at a time, so cancellation is global by nature. Two independent callers in the same app (say a comment queue and a notes queue) therefore need a way to say *which* job they mean — without one, a cancel from either kills whatever the other is running.
|
|
363
|
-
| **`holdForUpload`** | <code>boolean</code> | Android:
|
|
351
|
+
| Prop | Type | Description |
|
|
352
|
+
| ------------------- | ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
353
|
+
| **`sourcePath`** | <code>string</code> | |
|
|
354
|
+
| **`destPath`** | <code>string</code> | |
|
|
355
|
+
| **`quality`** | <code>'VERY_HIGH' \| 'HIGH' \| 'MEDIUM' \| 'LOW' \| 'VERY_LOW' \| '360P'</code> | |
|
|
356
|
+
| **`jobId`** | <code>string</code> | Names this compress so `cancel({ jobId })` can target it. Every platform compresses one video at a time, so cancellation is global by nature. Two independent callers in the same app (say a comment queue and a notes queue) therefore need a way to say *which* job they mean — without one, a cancel from either kills whatever the other is running. |
|
|
357
|
+
| **`holdForUpload`** | <code>boolean</code> | Android only: keep the background worker alive after the compress ends, until `finishUpload({ jobId })` is called. The upload runs as JavaScript in the webview, and the moment the worker returns the process becomes cached and is frozen — measured on Android 16, an in-flight upload dies about five seconds later. Set this whenever the compressed file is about to be uploaded, and always pair it with a `finishUpload` in a `finally`. |
|
|
364
358
|
|
|
365
359
|
|
|
366
360
|
#### CancelResult
|
package/android/build.gradle
CHANGED
|
@@ -3,6 +3,7 @@ ext {
|
|
|
3
3
|
androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.7.1'
|
|
4
4
|
androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.3.0'
|
|
5
5
|
androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.7.0'
|
|
6
|
+
androidxWorkVersion = project.hasProperty('androidxWorkVersion') ? rootProject.ext.androidxWorkVersion : '2.10.0'
|
|
6
7
|
}
|
|
7
8
|
|
|
8
9
|
buildscript {
|
|
@@ -21,6 +22,8 @@ android {
|
|
|
21
22
|
namespace = "com.atomic.videocompressor"
|
|
22
23
|
compileSdk = project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 36
|
|
23
24
|
defaultConfig {
|
|
25
|
+
// 24 trở lại: mức 35 trước đây chỉ để đáp ứng foreground service type
|
|
26
|
+
// mediaProcessing, thứ đã gỡ hẳn. WorkManager chạy từ API 21.
|
|
24
27
|
minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 24
|
|
25
28
|
targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 36
|
|
26
29
|
versionCode 1
|
|
@@ -57,4 +60,6 @@ dependencies {
|
|
|
57
60
|
androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
|
|
58
61
|
androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
|
|
59
62
|
implementation 'com.github.AbedElazizShe:LightCompressor:1.3.2'
|
|
63
|
+
// Giữ process khỏi bị đóng băng trong lúc nén, thay cho foreground service.
|
|
64
|
+
implementation "androidx.work:work-runtime:$androidxWorkVersion"
|
|
60
65
|
}
|
|
@@ -1,17 +1,11 @@
|
|
|
1
1
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
phủ được cả hai pha: nén rồi tải lên là một thao tác đồng bộ dữ liệu liên
|
|
7
|
-
tục do user khởi tạo - đúng ví dụ "upload photo to the cloud" mà policy nêu. -->
|
|
8
|
-
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
|
|
9
|
-
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
|
2
|
+
<!-- Cố ý rỗng. Google Play xếp "nén rồi upload video do user chọn" vào data
|
|
3
|
+
sync + local processing chứ không phải media transcoding, nên
|
|
4
|
+
FOREGROUND_SERVICE_MEDIA_PROCESSING bị từ chối ở version code 32 kèm
|
|
5
|
+
yêu cầu chuyển sang WorkManager.
|
|
10
6
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
android:exported="false" />
|
|
16
|
-
</application>
|
|
7
|
+
Pha nén giờ chạy trong VideoCompressionWorker, thứ WorkManager tự
|
|
8
|
+
quản lý - không service nào để khai, và mọi permission plugin từng
|
|
9
|
+
thêm vào app (FOREGROUND_SERVICE, FOREGROUND_SERVICE_MEDIA_PROCESSING,
|
|
10
|
+
POST_NOTIFICATIONS) đều đã gỡ. Đừng thêm lại. -->
|
|
17
11
|
</manifest>
|