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 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|dataSync"`.
58
- `mediaProcessing` exists only from **API 35 (Android 15)**, so your app must build with
59
- `compileSdkVersion` 35 or higher.
60
-
61
- At runtime the service picks the type by API level: `mediaProcessing` on API 35+,
62
- `dataSync` on API 29-34. Both types and both permissions are declared, so the plugin
63
- runs correctly on Android 10 through 15+. Passing only `mediaProcessing` on API 34
64
- makes the system read the type as unknown and `startForeground()` throws
65
- `InvalidForegroundServiceTypeException` - the service never reaches the foreground
66
- and compression is killed as soon as the user leaves the app.
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&lt;{ success: boolean; message?: string; }&gt;</code>
283
-
284
- --------------------
285
-
286
-
287
289
  ### finishUpload(...)
288
290
 
289
291
  ```typescript
290
- finishUpload(options: { jobId: string; }) => Promise<void>
292
+ finishUpload(options: { jobId: string; }) => Promise<{ released: boolean; }>
291
293
  ```
292
294
 
293
- Báo rằng phần upload đi kèm `holdForUpload` đã xong (thành công hay thất
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
- No-op trên web và iOS.
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&lt;{ released: boolean; }&gt;</code>
307
+
302
308
  --------------------
303
309
 
304
310
 
305
- ### addListener('onProgress', ...)
311
+ ### initialize()
306
312
 
307
313
  ```typescript
308
- addListener(eventName: 'onProgress', listenerFunc: (info: { status: string; percent?: number; }) => void) => Promise<PluginListenerHandle>
314
+ initialize() => Promise<{ success: boolean; message?: string; }>
309
315
  ```
310
316
 
311
- | Param | Type |
312
- | ------------------ | --------------------------------------------------------------------- |
313
- | **`eventName`** | <code>'onProgress'</code> |
314
- | **`listenerFunc`** | <code>(info: { status: string; percent?: number; }) =&gt; void</code> |
315
-
316
- **Returns:** <code>Promise&lt;<a href="#pluginlistenerhandle">PluginListenerHandle</a>&gt;</code>
317
+ **Returns:** <code>Promise&lt;{ success: boolean; message?: string; }&gt;</code>
317
318
 
318
319
  --------------------
319
320
 
320
321
 
321
- ### addListener('onCancelRequested', ...)
322
+ ### addListener('onProgress', ...)
322
323
 
323
324
  ```typescript
324
- addListener(eventName: 'onCancelRequested', listenerFunc: () => void) => Promise<PluginListenerHandle>
325
+ addListener(eventName: 'onProgress', listenerFunc: (info: { status: string; percent?: number; }) => void) => Promise<PluginListenerHandle>
325
326
  ```
326
327
 
327
- Android: user đã bấm "Huỷ" trên notification của foreground service.
328
-
329
- Pha nén được plugin tự huỷ trước khi event này bắn, nhưng pha upload chạy
330
- trong JS nên không ai dừng hộ được - caller phải tự abort request đang
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>() =&gt; void</code> |
328
+ | Param | Type |
329
+ | ------------------ | --------------------------------------------------------------------- |
330
+ | **`eventName`** | <code>'onProgress'</code> |
331
+ | **`listenerFunc`** | <code>(info: { status: string; percent?: number; }) =&gt; void</code> |
338
332
 
339
333
  **Returns:** <code>Promise&lt;<a href="#pluginlistenerhandle">PluginListenerHandle</a>&gt;</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: giữ foreground service sống sau khi nén xong, để phần upload do JS chạy tiếp theo không bị hệ thống giết khi user rời app. Bật cờ này là một lời hứa: caller PHẢI gọi {@link NativeVideoCompressorPlugin.finishUpload} khi upload kết thúc, kể cả ở nhánh lỗi. Không gọi thì service chỉ còn watchdog dọn hộ sau 15 phút, và trong 15 phút đó user nhìn thấy một notification nói rằng app đang làm việc trong khi nó không làm gì cả. Không có tác dụng trên web và iOS. |
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
@@ -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
- <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
3
- <!-- Chỉ dataSync, không khai mediaProcessing.
4
- Hai type nghĩa là hai quyền phải biện minh riêng trong Play Console với
5
- cùng một video demo, và mediaProcessing khó thuyết phục hơn hẳn. dataSync
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
- <application>
12
- <service
13
- android:name="com.atomic.videocompressor.VideoCompressionService"
14
- android:foregroundServiceType="dataSync"
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>