capacitor-native-video-compressor 1.2.0 → 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 +40 -30
- package/android/build.gradle +6 -4
- package/android/src/main/AndroidManifest.xml +8 -17
- package/android/src/main/java/com/atomic/videocompressor/NativeVideoCompressorPlugin.java +222 -108
- package/android/src/main/java/com/atomic/videocompressor/VideoCompressionWorker.java +120 -0
- package/dist/docs.json +24 -24
- package/dist/esm/definitions.d.ts +35 -9
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/web.d.ts +3 -0
- package/dist/esm/web.js +6 -0
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +6 -0
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +6 -0
- package/dist/plugin.js.map +1 -1
- package/package.json +1 -1
- package/android/src/main/java/com/atomic/videocompressor/VideoCompressionService.java +0 -310
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
package com.atomic.videocompressor;
|
|
2
|
+
|
|
3
|
+
import android.content.Context;
|
|
4
|
+
import androidx.annotation.NonNull;
|
|
5
|
+
import androidx.annotation.Nullable;
|
|
6
|
+
import androidx.work.Worker;
|
|
7
|
+
import androidx.work.WorkerParameters;
|
|
8
|
+
import java.util.Map;
|
|
9
|
+
import java.util.concurrent.ConcurrentHashMap;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Giữ pha nén chạy được khi user rời app, không cần foreground service.
|
|
13
|
+
*
|
|
14
|
+
* Android 14+ đóng băng process ở trạng thái cached, nên nén bằng một thread
|
|
15
|
+
* thường sẽ đứng lại ngay khi user chuyển app. Foreground service từng là cách
|
|
16
|
+
* giữ nó sống, nhưng Google Play từ chối version code 32: use case này được xếp
|
|
17
|
+
* vào data sync + local processing, không đủ điều kiện cho
|
|
18
|
+
* FOREGROUND_SERVICE_MEDIA_PROCESSING, và họ chỉ đích danh WorkManager làm
|
|
19
|
+
* phương án thay thế. Process đang chạy một worker thì không bị đóng băng.
|
|
20
|
+
*
|
|
21
|
+
* Worker chỉ là vỏ bọc: việc nén thật do plugin đăng ký qua {@link #register}
|
|
22
|
+
* ngay trước khi enqueue, vì chỉ plugin mới giữ được PluginCall để trả kết quả.
|
|
23
|
+
*
|
|
24
|
+
* Với `holdForUpload`, worker ở lại sau khi nén xong cho tới khi caller gọi
|
|
25
|
+
* `finishUpload`. Pha upload chạy bằng JS trong webview, và khoảnh khắc worker
|
|
26
|
+
* kết thúc là khoảnh khắc process thành cached rồi bị đóng băng - đo trên
|
|
27
|
+
* Android 16: upload chết đúng ~5 giây sau khi worker trả về. Giữ worker sống
|
|
28
|
+
* xuyên pha upload là cách duy nhất còn lại để nó sống sót, vì foreground
|
|
29
|
+
* service đã bị Play cấm cho use case này.
|
|
30
|
+
*
|
|
31
|
+
* Giới hạn phải biết: WorkManager dừng worker sau 10 phút, tính chung cả nén
|
|
32
|
+
* lẫn upload. Quá mức đó {@link #onStopped()} huỷ việc đang chạy và caller
|
|
33
|
+
* nhận lỗi CANCELLED - đổi lại lấy được nhiều thời gian hơn thì phải có
|
|
34
|
+
* foreground service, thứ không còn được phép ở đây.
|
|
35
|
+
*/
|
|
36
|
+
public class VideoCompressionWorker extends Worker {
|
|
37
|
+
|
|
38
|
+
static final String KEY_JOB_ID = "jobId";
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Việc nén do plugin đăng ký, tra theo jobId.
|
|
42
|
+
*
|
|
43
|
+
* Static vì worker được WorkManager dựng mới, không nhận được tham chiếu
|
|
44
|
+
* nào từ phía plugin. Map chỉ có nghĩa trong phạm vi một process: process
|
|
45
|
+
* chết là WebView chết, không còn ai nhận kết quả nén nữa.
|
|
46
|
+
*/
|
|
47
|
+
private static final Map<String, Job> pending = new ConcurrentHashMap<>();
|
|
48
|
+
|
|
49
|
+
interface Job {
|
|
50
|
+
/**
|
|
51
|
+
* Chạy pha nén, và với holdForUpload thì ở lại tới khi caller gọi
|
|
52
|
+
* finishUpload. Chỉ trả về khi không còn gì phải giữ process sống.
|
|
53
|
+
*/
|
|
54
|
+
void run();
|
|
55
|
+
|
|
56
|
+
/** WorkManager đòi dừng - hết 10 phút, hoặc work bị cancel. */
|
|
57
|
+
void stop();
|
|
58
|
+
|
|
59
|
+
/** Caller báo đã upload xong; worker được phép kết thúc. */
|
|
60
|
+
void release();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
static void register(String jobId, Job job) {
|
|
64
|
+
pending.put(jobId, job);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
static void unregister(String jobId) {
|
|
68
|
+
pending.remove(jobId);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Thả worker đang giữ process sống cho pha upload của jobId.
|
|
73
|
+
*
|
|
74
|
+
* False khi không còn job nào mang id đó - worker đã kết thúc từ trước,
|
|
75
|
+
* không có gì để thả. Đó là trạng thái bình thường, không phải lỗi.
|
|
76
|
+
*/
|
|
77
|
+
static boolean release(String jobId) {
|
|
78
|
+
Job job = pending.get(jobId);
|
|
79
|
+
if (job == null) return false;
|
|
80
|
+
job.release();
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
@Nullable
|
|
85
|
+
private volatile Job running = null;
|
|
86
|
+
|
|
87
|
+
public VideoCompressionWorker(@NonNull Context context, @NonNull WorkerParameters params) {
|
|
88
|
+
super(context, params);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
@NonNull
|
|
92
|
+
@Override
|
|
93
|
+
public Result doWork() {
|
|
94
|
+
String jobId = getInputData().getString(KEY_JOB_ID);
|
|
95
|
+
if (jobId == null) return Result.success();
|
|
96
|
+
|
|
97
|
+
Job job = pending.get(jobId);
|
|
98
|
+
// Không còn job: process đã bị kill và WorkManager dựng lại worker ở
|
|
99
|
+
// process mới. WebView đã chết cùng nó nên không còn ai nhận kết quả -
|
|
100
|
+
// chạy lại pha nén chỉ tốn pin. Success chứ không retry, để WorkManager
|
|
101
|
+
// bỏ hẳn work này đi.
|
|
102
|
+
if (job == null) return Result.success();
|
|
103
|
+
|
|
104
|
+
running = job;
|
|
105
|
+
try {
|
|
106
|
+
job.run();
|
|
107
|
+
} finally {
|
|
108
|
+
running = null;
|
|
109
|
+
pending.remove(jobId);
|
|
110
|
+
}
|
|
111
|
+
return Result.success();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
@Override
|
|
115
|
+
public void onStopped() {
|
|
116
|
+
Job job = running;
|
|
117
|
+
if (job != null) job.stop();
|
|
118
|
+
super.onStopped();
|
|
119
|
+
}
|
|
120
|
+
}
|
package/dist/docs.json
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
],
|
|
18
18
|
"returns": "Promise<CompressResult>",
|
|
19
19
|
"tags": [],
|
|
20
|
-
"docs": "",
|
|
20
|
+
"docs": "Compress one video and resolve with the path of the result.\n\nAndroid: the work runs inside a WorkManager worker, which keeps the\nprocess out of the frozen cached state while the user is away — there is\nno foreground service and no notification, because Google Play classes\nthis use case as data sync plus local processing and rejects\n`FOREGROUND_SERVICE_MEDIA_PROCESSING` for it. WorkManager stops a worker\nafter 10 minutes, so a compress that runs longer rejects with\n{@link CANCELLED}.",
|
|
21
21
|
"complexTypes": [
|
|
22
22
|
"CompressResult",
|
|
23
23
|
"CompressOptions"
|
|
@@ -43,6 +43,22 @@
|
|
|
43
43
|
],
|
|
44
44
|
"slug": "cancel"
|
|
45
45
|
},
|
|
46
|
+
{
|
|
47
|
+
"name": "finishUpload",
|
|
48
|
+
"signature": "(options: { jobId: string; }) => Promise<{ released: boolean; }>",
|
|
49
|
+
"parameters": [
|
|
50
|
+
{
|
|
51
|
+
"name": "options",
|
|
52
|
+
"docs": "",
|
|
53
|
+
"type": "{ jobId: string; }"
|
|
54
|
+
}
|
|
55
|
+
],
|
|
56
|
+
"returns": "Promise<{ released: boolean; }>",
|
|
57
|
+
"tags": [],
|
|
58
|
+
"docs": "Release the worker that `holdForUpload` kept alive for this job.\n\nCall it on every terminal branch of the upload, success or failure. A\nmissed call leaves the device feeding an idle job until the hold expires.\n`released` is false when no job by that id was still being held, which is\nthe normal outcome of a late or duplicate call, not an error.",
|
|
59
|
+
"complexTypes": [],
|
|
60
|
+
"slug": "finishupload"
|
|
61
|
+
},
|
|
46
62
|
{
|
|
47
63
|
"name": "initialize",
|
|
48
64
|
"signature": "() => Promise<{ success: boolean; message?: string; }>",
|
|
@@ -75,29 +91,6 @@
|
|
|
75
91
|
"PluginListenerHandle"
|
|
76
92
|
],
|
|
77
93
|
"slug": "addlisteneronprogress-"
|
|
78
|
-
},
|
|
79
|
-
{
|
|
80
|
-
"name": "addListener",
|
|
81
|
-
"signature": "(eventName: 'onCancelRequested', listenerFunc: () => void) => Promise<PluginListenerHandle>",
|
|
82
|
-
"parameters": [
|
|
83
|
-
{
|
|
84
|
-
"name": "eventName",
|
|
85
|
-
"docs": "",
|
|
86
|
-
"type": "'onCancelRequested'"
|
|
87
|
-
},
|
|
88
|
-
{
|
|
89
|
-
"name": "listenerFunc",
|
|
90
|
-
"docs": "",
|
|
91
|
-
"type": "() => void"
|
|
92
|
-
}
|
|
93
|
-
],
|
|
94
|
-
"returns": "Promise<PluginListenerHandle>",
|
|
95
|
-
"tags": [],
|
|
96
|
-
"docs": "Android: user đã bấm \"Huỷ\" trên notification của foreground service.\n\nPha nén được plugin tự huỷ trước khi event này bắn, nhưng việc caller định\nlàm với kết quả nén - upload chẳng hạn - thì không ai dừng hộ được, nên\ncaller phải tự abort. Bỏ qua event này nghĩa là nút Huỷ không thật sự huỷ,\nvà Play đánh trượt tiêu chí \"can be terminated or stopped by the user\".",
|
|
97
|
-
"complexTypes": [
|
|
98
|
-
"PluginListenerHandle"
|
|
99
|
-
],
|
|
100
|
-
"slug": "addlisteneroncancelrequested-"
|
|
101
94
|
}
|
|
102
95
|
],
|
|
103
96
|
"properties": []
|
|
@@ -160,6 +153,13 @@
|
|
|
160
153
|
"docs": "Names this compress so `cancel({ jobId })` can target it.\n\nEvery platform compresses one video at a time, so cancellation is global\nby nature. Two independent callers in the same app (say a comment queue\nand a notes queue) therefore need a way to say *which* job they mean —\nwithout one, a cancel from either kills whatever the other is running.",
|
|
161
154
|
"complexTypes": [],
|
|
162
155
|
"type": "string | undefined"
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
"name": "holdForUpload",
|
|
159
|
+
"tags": [],
|
|
160
|
+
"docs": "Android only: keep the background worker alive after the compress ends,\nuntil `finishUpload({ jobId })` is called.\n\nThe upload runs as JavaScript in the webview, and the moment the worker\nreturns the process becomes cached and is frozen — measured on Android 16,\nan in-flight upload dies about five seconds later. Set this whenever the\ncompressed file is about to be uploaded, and always pair it with a\n`finishUpload` in a `finally`.",
|
|
161
|
+
"complexTypes": [],
|
|
162
|
+
"type": "boolean | undefined"
|
|
163
163
|
}
|
|
164
164
|
]
|
|
165
165
|
},
|
|
@@ -18,6 +18,17 @@ export interface CompressOptions {
|
|
|
18
18
|
* without one, a cancel from either kills whatever the other is running.
|
|
19
19
|
*/
|
|
20
20
|
jobId?: string;
|
|
21
|
+
/**
|
|
22
|
+
* Android only: keep the background worker alive after the compress ends,
|
|
23
|
+
* until `finishUpload({ jobId })` is called.
|
|
24
|
+
*
|
|
25
|
+
* The upload runs as JavaScript in the webview, and the moment the worker
|
|
26
|
+
* returns the process becomes cached and is frozen — measured on Android 16,
|
|
27
|
+
* an in-flight upload dies about five seconds later. Set this whenever the
|
|
28
|
+
* compressed file is about to be uploaded, and always pair it with a
|
|
29
|
+
* `finishUpload` in a `finally`.
|
|
30
|
+
*/
|
|
31
|
+
holdForUpload?: boolean;
|
|
21
32
|
}
|
|
22
33
|
export interface CompressResult {
|
|
23
34
|
success: boolean;
|
|
@@ -35,6 +46,17 @@ export interface CancelResult {
|
|
|
35
46
|
cancelled: boolean;
|
|
36
47
|
}
|
|
37
48
|
export interface NativeVideoCompressorPlugin {
|
|
49
|
+
/**
|
|
50
|
+
* Compress one video and resolve with the path of the result.
|
|
51
|
+
*
|
|
52
|
+
* Android: the work runs inside a WorkManager worker, which keeps the
|
|
53
|
+
* process out of the frozen cached state while the user is away — there is
|
|
54
|
+
* no foreground service and no notification, because Google Play classes
|
|
55
|
+
* this use case as data sync plus local processing and rejects
|
|
56
|
+
* `FOREGROUND_SERVICE_MEDIA_PROCESSING` for it. WorkManager stops a worker
|
|
57
|
+
* after 10 minutes, so a compress that runs longer rejects with
|
|
58
|
+
* {@link CANCELLED}.
|
|
59
|
+
*/
|
|
38
60
|
compressVideo(options: CompressOptions): Promise<CompressResult>;
|
|
39
61
|
/**
|
|
40
62
|
* Stop the compress that is running now. `compressVideo` then rejects with
|
|
@@ -45,6 +67,19 @@ export interface NativeVideoCompressorPlugin {
|
|
|
45
67
|
* thread (native), and none of them watch an AbortSignal.
|
|
46
68
|
*/
|
|
47
69
|
cancel(options?: CancelOptions): Promise<CancelResult>;
|
|
70
|
+
/**
|
|
71
|
+
* Release the worker that `holdForUpload` kept alive for this job.
|
|
72
|
+
*
|
|
73
|
+
* Call it on every terminal branch of the upload, success or failure. A
|
|
74
|
+
* missed call leaves the device feeding an idle job until the hold expires.
|
|
75
|
+
* `released` is false when no job by that id was still being held, which is
|
|
76
|
+
* the normal outcome of a late or duplicate call, not an error.
|
|
77
|
+
*/
|
|
78
|
+
finishUpload(options: {
|
|
79
|
+
jobId: string;
|
|
80
|
+
}): Promise<{
|
|
81
|
+
released: boolean;
|
|
82
|
+
}>;
|
|
48
83
|
initialize(): Promise<{
|
|
49
84
|
success: boolean;
|
|
50
85
|
message?: string;
|
|
@@ -53,13 +88,4 @@ export interface NativeVideoCompressorPlugin {
|
|
|
53
88
|
status: string;
|
|
54
89
|
percent?: number;
|
|
55
90
|
}) => void): Promise<PluginListenerHandle>;
|
|
56
|
-
/**
|
|
57
|
-
* Android: user đã bấm "Huỷ" trên notification của foreground service.
|
|
58
|
-
*
|
|
59
|
-
* Pha nén được plugin tự huỷ trước khi event này bắn, nhưng việc caller định
|
|
60
|
-
* làm với kết quả nén - upload chẳng hạn - thì không ai dừng hộ được, nên
|
|
61
|
-
* caller phải tự abort. Bỏ qua event này nghĩa là nút Huỷ không thật sự huỷ,
|
|
62
|
-
* và Play đánh trượt tiêu chí "can be terminated or stopped by the user".
|
|
63
|
-
*/
|
|
64
|
-
addListener(eventName: 'onCancelRequested', listenerFunc: () => void): Promise<PluginListenerHandle>;
|
|
65
91
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,WAAW,CAAC","sourcesContent":["import type { PluginListenerHandle } from '@capacitor/core';\n\n/**\n * `code` on the error `compressVideo` rejects with when `cancel()` stopped it.\n * Callers should treat it as \"the user asked for this\" — no error toast, no\n * retry prompt — and tell it apart from a genuine compression failure.\n */\nexport const CANCELLED = 'CANCELLED';\n\nexport interface CompressOptions {\n sourcePath: string;\n destPath?: string;\n quality?: 'VERY_HIGH' | 'HIGH' | 'MEDIUM' | 'LOW' | 'VERY_LOW' | '360P';\n /**\n * Names this compress so `cancel({ jobId })` can target it.\n *\n * Every platform compresses one video at a time, so cancellation is global\n * by nature. Two independent callers in the same app (say a comment queue\n * and a notes queue) therefore need a way to say *which* job they mean —\n * without one, a cancel from either kills whatever the other is running.\n */\n jobId?: string;\n}\n\nexport interface CompressResult {\n success: boolean;\n destPath: string;\n}\n\nexport interface CancelOptions {\n /**\n * Only cancel if this is the job currently running. Omit to cancel whatever\n * is running, whoever started it.\n */\n jobId?: string;\n}\n\nexport interface CancelResult {\n /** False when nothing was running, or when a different job was. */\n cancelled: boolean;\n}\n\nexport interface NativeVideoCompressorPlugin {\n compressVideo(options: CompressOptions): Promise<CompressResult>;\n\n /**\n * Stop the compress that is running now. `compressVideo` then rejects with\n * code {@link CANCELLED} and its partial output is deleted.\n *\n * This is the only way to stop one: the work is synchronous WASM inside a\n * web worker (web) or an AVFoundation / LightCompressor pass on its own\n * thread (native), and none of them watch an AbortSignal.\n */\n cancel(options?: CancelOptions): Promise<CancelResult>;\n\n
|
|
1
|
+
{"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,WAAW,CAAC","sourcesContent":["import type { PluginListenerHandle } from '@capacitor/core';\n\n/**\n * `code` on the error `compressVideo` rejects with when `cancel()` stopped it.\n * Callers should treat it as \"the user asked for this\" — no error toast, no\n * retry prompt — and tell it apart from a genuine compression failure.\n */\nexport const CANCELLED = 'CANCELLED';\n\nexport interface CompressOptions {\n sourcePath: string;\n destPath?: string;\n quality?: 'VERY_HIGH' | 'HIGH' | 'MEDIUM' | 'LOW' | 'VERY_LOW' | '360P';\n /**\n * Names this compress so `cancel({ jobId })` can target it.\n *\n * Every platform compresses one video at a time, so cancellation is global\n * by nature. Two independent callers in the same app (say a comment queue\n * and a notes queue) therefore need a way to say *which* job they mean —\n * without one, a cancel from either kills whatever the other is running.\n */\n jobId?: string;\n /**\n * Android only: keep the background worker alive after the compress ends,\n * until `finishUpload({ jobId })` is called.\n *\n * The upload runs as JavaScript in the webview, and the moment the worker\n * returns the process becomes cached and is frozen — measured on Android 16,\n * an in-flight upload dies about five seconds later. Set this whenever the\n * compressed file is about to be uploaded, and always pair it with a\n * `finishUpload` in a `finally`.\n */\n holdForUpload?: boolean;\n}\n\nexport interface CompressResult {\n success: boolean;\n destPath: string;\n}\n\nexport interface CancelOptions {\n /**\n * Only cancel if this is the job currently running. Omit to cancel whatever\n * is running, whoever started it.\n */\n jobId?: string;\n}\n\nexport interface CancelResult {\n /** False when nothing was running, or when a different job was. */\n cancelled: boolean;\n}\n\nexport interface NativeVideoCompressorPlugin {\n /**\n * Compress one video and resolve with the path of the result.\n *\n * Android: the work runs inside a WorkManager worker, which keeps the\n * process out of the frozen cached state while the user is away — there is\n * no foreground service and no notification, because Google Play classes\n * this use case as data sync plus local processing and rejects\n * `FOREGROUND_SERVICE_MEDIA_PROCESSING` for it. WorkManager stops a worker\n * after 10 minutes, so a compress that runs longer rejects with\n * {@link CANCELLED}.\n */\n compressVideo(options: CompressOptions): Promise<CompressResult>;\n\n /**\n * Stop the compress that is running now. `compressVideo` then rejects with\n * code {@link CANCELLED} and its partial output is deleted.\n *\n * This is the only way to stop one: the work is synchronous WASM inside a\n * web worker (web) or an AVFoundation / LightCompressor pass on its own\n * thread (native), and none of them watch an AbortSignal.\n */\n cancel(options?: CancelOptions): Promise<CancelResult>;\n\n /**\n * Release the worker that `holdForUpload` kept alive for this job.\n *\n * Call it on every terminal branch of the upload, success or failure. A\n * missed call leaves the device feeding an idle job until the hold expires.\n * `released` is false when no job by that id was still being held, which is\n * the normal outcome of a late or duplicate call, not an error.\n */\n finishUpload(options: { jobId: string }): Promise<{ released: boolean }>;\n\n initialize(): Promise<{ success: boolean; message?: string }>;\n\n // Đã sửa lại chuẩn cho Capacitor mới nhất (chỉ trả về Promise)\n addListener(\n eventName: 'onProgress',\n listenerFunc: (info: { status: string; percent?: number }) => void,\n ): Promise<PluginListenerHandle>;\n}\n"]}
|
package/dist/esm/web.d.ts
CHANGED
|
@@ -12,6 +12,9 @@ export declare class NativeVideoCompressorWeb extends WebPlugin implements Nativ
|
|
|
12
12
|
success: boolean;
|
|
13
13
|
message?: string;
|
|
14
14
|
}>;
|
|
15
|
+
finishUpload(): Promise<{
|
|
16
|
+
released: boolean;
|
|
17
|
+
}>;
|
|
15
18
|
compressVideo(options: CompressOptions): Promise<CompressResult>;
|
|
16
19
|
/**
|
|
17
20
|
* Kill whichever engine is mid-compress. Only one is ever active at a time,
|
package/dist/esm/web.js
CHANGED
|
@@ -75,6 +75,12 @@ export class NativeVideoCompressorWeb extends WebPlugin {
|
|
|
75
75
|
// first use. Kept as a resolving no-op so callers that await it still pass.
|
|
76
76
|
return { success: true, message: 'ready' };
|
|
77
77
|
}
|
|
78
|
+
async finishUpload() {
|
|
79
|
+
// Nothing holds a process alive on the web — the tab either lives or the
|
|
80
|
+
// whole compress is gone with it. Resolving no-op so the one call site
|
|
81
|
+
// can fire unconditionally instead of branching on platform.
|
|
82
|
+
return { released: false };
|
|
83
|
+
}
|
|
78
84
|
async compressVideo(options) {
|
|
79
85
|
var _a, _b;
|
|
80
86
|
this.currentJobId = (_a = options.jobId) !== null && _a !== void 0 ? _a : 'default';
|
package/dist/esm/web.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"web.js","sourceRoot":"","sources":["../../src/web.ts"],"names":[],"mappings":"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","sourcesContent":["import { WebPlugin } from '@capacitor/core';\nimport { FFmpeg } from '@ffmpeg/ffmpeg';\nimport { toBlobURL } from '@ffmpeg/util';\nimport {\n ALL_FORMATS,\n BlobSource,\n BufferTarget,\n Conversion,\n ConversionCanceledError,\n Input,\n Mp4OutputFormat,\n Output,\n Quality,\n canEncodeVideo,\n} from 'mediabunny';\n\nimport type {\n NativeVideoCompressorPlugin,\n CompressOptions,\n CompressResult,\n CancelOptions,\n CancelResult,\n} from './definitions';\nimport { CANCELLED } from './definitions';\n\ntype VideoQuality = NonNullable<CompressOptions['quality']>;\n\n/**\n * Rejection shape for a cancelled compress. `code` matches what `call.reject`\n * puts on the error object on iOS/Android, so one `error.code === 'CANCELLED'`\n * check works on every platform — and on both web engines below.\n */\nclass CancelledError extends Error {\n code = CANCELLED;\n constructor() {\n super('Đã hủy nén');\n this.name = 'CancelledError';\n }\n}\n\n/**\n * WebCodecs target per quality tier: cap height + a bitrate ceiling. The\n * quantizer (constant 26, CRF-like) drives quality where the encoder honours\n * it; H.264 on most browsers is bitrate-driven, so `bitrate` is the real knob.\n * fps is capped to 30 at call time (never upsampled). Audio is left to\n * mediabunny, which re-encodes to AAC for the MP4 container.\n */\nconst WEBCODECS_LADDER: Record<VideoQuality, { height: number; bitrate: number }> = {\n VERY_HIGH: { height: 1080, bitrate: 4000000 },\n HIGH: { height: 720, bitrate: 2000000 },\n MEDIUM: { height: 540, bitrate: 1200000 },\n LOW: { height: 480, bitrate: 1000000 },\n '360P': { height: 360, bitrate: 700000 },\n VERY_LOW: { height: 240, bitrate: 350000 },\n};\n\nconst WEBCODECS_QUANTIZER = 26;\nconst FPS_CAP = 30;\n\nconst CORE_BASE_URL = 'https://unpkg.com/@ffmpeg/core@0.12.6/dist/esm';\n\n/**\n * Cancelling means terminating the worker, which drops the loaded core with\n * it — so without this the next compress re-downloads ~30 MB of wasm and\n * \"cancel\" quietly costs more than it saves. Module scope, not instance: the\n * bytes are identical for every instance and every job. Only the ffmpeg\n * fallback ever pays this download; the WebCodecs primary path needs no core.\n */\nlet corePromise: Promise<{ coreURL: string; wasmURL: string }> | null = null;\n\nconst loadCoreURLs = () => {\n if (!corePromise) {\n corePromise = Promise.all([\n toBlobURL(`${CORE_BASE_URL}/ffmpeg-core.js`, 'text/javascript'),\n toBlobURL(`${CORE_BASE_URL}/ffmpeg-core.wasm`, 'application/wasm'),\n ])\n .then(([coreURL, wasmURL]) => ({ coreURL, wasmURL }))\n .catch((e) => {\n // Don't cache a failure — a flaky network shouldn't make every later\n // compress in this session fail too.\n corePromise = null;\n throw e;\n });\n }\n return corePromise;\n};\n\nexport class NativeVideoCompressorWeb extends WebPlugin implements NativeVideoCompressorPlugin {\n private ffmpeg: FFmpeg | null = null;\n private isLoaded = false;\n /** The mediabunny conversion running now, so `cancel()` can abort it. */\n private activeConversion: Conversion | null = null;\n /** The job `compressVideo` is running right now, for `cancel({ jobId })`. */\n private currentJobId: string | null = null;\n private cancelRequested = false;\n\n async initialize(): Promise<{ success: boolean; message?: string }> {\n // WebCodecs (the primary path) needs no core download, so there is nothing\n // to warm. Eagerly loading the ffmpeg core here would pull ~30 MB for a\n // path most compresses never take. The fallback lazy-loads its core on\n // first use. Kept as a resolving no-op so callers that await it still pass.\n return { success: true, message: 'ready' };\n }\n\n async compressVideo(options: CompressOptions): Promise<CompressResult> {\n this.currentJobId = options.jobId ?? 'default';\n this.cancelRequested = false;\n\n // Checked at every stage boundary, not just around the encode: cancelling\n // an engine only reaches work already handed to it, so a cancel during the\n // source fetch or before init has nothing to interrupt and would otherwise\n // let the compress carry on as if nothing had happened.\n const throwIfCancelled = () => {\n if (this.cancelRequested) throw new CancelledError();\n };\n\n try {\n throwIfCancelled();\n\n const blob = await this.fetchSourceBlob(options.sourcePath);\n throwIfCancelled();\n\n const quality: VideoQuality = options.quality ?? 'MEDIUM';\n\n if (await this.canUseWebCodecs(quality)) {\n try {\n const result = await this.compressWithWebCodecs(blob, quality, throwIfCancelled);\n if (result) return result;\n // null → conversion invalid / tracks discarded → fall back to ffmpeg.\n } catch (e: any) {\n if (e?.code === CANCELLED) throw e; // user cancel: never fall back\n console.warn('[compress] WebCodecs failed, falling back to ffmpeg:', e);\n throwIfCancelled();\n }\n }\n\n return await this.compressWithFfmpeg(blob, quality, throwIfCancelled);\n } catch (error: any) {\n if (error?.code === CANCELLED) {\n console.log('[compress] cancelled');\n } else {\n console.error('[compress] failed:', error);\n }\n throw error;\n } finally {\n this.currentJobId = null;\n this.cancelRequested = false;\n this.activeConversion = null;\n }\n }\n\n /**\n * Kill whichever engine is mid-compress. Only one is ever active at a time,\n * so acting on both is safe.\n *\n * WebCodecs: `conversion.cancel()` makes the in-flight `execute()` reject\n * with `ConversionCanceledError`, surfaced as a `CancelledError` below.\n * ffmpeg: `exec()` runs synchronously inside the worker and never reaches a\n * message poll while encoding, so `worker.terminate()` is the only way to\n * stop it — its cost is the loaded core, which is why the core URLs are\n * cached above.\n */\n async cancel(options?: CancelOptions): Promise<CancelResult> {\n if (!this.currentJobId) return { cancelled: false };\n if (options?.jobId && options.jobId !== this.currentJobId) return { cancelled: false };\n\n // Set even when neither engine exists yet: the job may still be fetching\n // the source or inside loadFFmpeg, which read this flag at their boundaries.\n this.cancelRequested = true;\n\n const conversion = this.activeConversion;\n this.activeConversion = null;\n // Fire-and-forget: the CancelledError surfaces from the execute() rejection,\n // not from here; awaiting would only make cancel() slower.\n conversion?.cancel().catch(() => undefined);\n\n this.ffmpeg?.terminate();\n this.ffmpeg = null;\n this.isLoaded = false;\n\n return { cancelled: true };\n }\n\n private async fetchSourceBlob(sourcePath: string): Promise<Blob> {\n try {\n const response = await fetch(sourcePath);\n return await response.blob();\n } catch (err) {\n console.error('[compress] Error fetching source file:', err);\n throw new Error('Could not fetch source video file');\n }\n }\n\n private async canUseWebCodecs(quality: VideoQuality): Promise<boolean> {\n try {\n const { bitrate } = WEBCODECS_LADDER[quality];\n return await canEncodeVideo('avc', {\n quality: new Quality({ quantizer: WEBCODECS_QUANTIZER, bitrate }),\n });\n } catch {\n return false;\n }\n }\n\n private async compressWithWebCodecs(\n blob: Blob,\n quality: VideoQuality,\n throwIfCancelled: () => void,\n ): Promise<CompressResult | null> {\n const { height, bitrate } = WEBCODECS_LADDER[quality];\n\n const input = new Input({ formats: ALL_FORMATS, source: new BlobSource(blob) });\n const output = new Output({ format: new Mp4OutputFormat(), target: new BufferTarget() });\n\n // Cap fps at 30, but never upsample: probe the source and take the min.\n // A failed probe just omits frameRate, leaving the source rate untouched.\n let frameRate: number | undefined;\n try {\n const track = await input.getPrimaryVideoTrack();\n if (track) {\n const stats = await track.computePacketStats(100);\n if (stats.averagePacketRate > 0) {\n frameRate = Math.min(FPS_CAP, Math.round(stats.averagePacketRate));\n }\n }\n } catch {\n frameRate = undefined;\n }\n\n throwIfCancelled();\n\n const conversion = await Conversion.init({\n input,\n output,\n video: {\n height,\n quality: new Quality({ quantizer: WEBCODECS_QUANTIZER, bitrate }),\n hardwareAcceleration: 'prefer-hardware',\n ...(frameRate ? { frameRate } : {}),\n },\n });\n\n if (!conversion.isValid) {\n console.warn(\n '[compress] WebCodecs conversion invalid:',\n conversion.discardedTracks.map((t) => t.reason),\n );\n return null;\n }\n\n this.activeConversion = conversion;\n\n this.notifyListeners('onProgress', { status: 'started', percent: 0 });\n conversion.onProgress = (progress) => {\n this.notifyListeners('onProgress', { status: 'progress', percent: Math.round(progress * 100) });\n };\n\n throwIfCancelled();\n try {\n await conversion.execute();\n } catch (e: any) {\n if (this.cancelRequested || e instanceof ConversionCanceledError) throw new CancelledError();\n throw e;\n }\n\n const buffer = output.target.buffer;\n if (!buffer) throw new Error('WebCodecs produced no output');\n\n const outBlob = new Blob([buffer], { type: 'video/mp4' });\n return { success: true, destPath: URL.createObjectURL(outBlob) };\n }\n\n private async loadFFmpeg() {\n if (this.isLoaded && this.ffmpeg) return;\n\n this.notifyListeners('onProgress', { status: 'loading_core', percent: 0 });\n\n // Built on a local, published to the field only once it is ready and still\n // wanted. Assigning `this.ffmpeg` up front and setting `isLoaded` after the\n // await left a window where a cancel could null the instance and the load,\n // finishing a moment later, would set `isLoaded = true` on nothing at all —\n // after which every compressVideo threw 'FFmpeg failed to load' for the\n // rest of the page's life.\n const instance = new FFmpeg();\n\n instance.on('progress', ({ progress }) => {\n this.notifyListeners('onProgress', { status: 'progress', percent: Math.round(progress * 100) });\n });\n\n instance.on('log', ({ message }) => {\n console.log('[FFmpeg Log]', message);\n });\n\n try {\n await instance.load(await loadCoreURLs());\n } catch (e: any) {\n instance.terminate();\n console.error('Failed to load FFmpeg:', e);\n throw e;\n }\n\n // Cancelled while this was loading. Terminate what we just built rather\n // than publish it — the caller asked for none of it.\n if (this.cancelRequested) {\n instance.terminate();\n throw new CancelledError();\n }\n\n this.ffmpeg = instance;\n this.isLoaded = true;\n }\n\n private async compressWithFfmpeg(\n blob: Blob,\n quality: VideoQuality,\n throwIfCancelled: () => void,\n ): Promise<CompressResult> {\n if (!this.isLoaded) {\n await this.loadFFmpeg();\n }\n throwIfCancelled();\n\n if (!this.ffmpeg) {\n throw new Error('FFmpeg failed to load');\n }\n // Bound once. `this.ffmpeg` is nulled by cancel(), so reading it after any\n // of the awaits below can hand back null on a reference the compiler\n // narrowed here — surfacing a cancel as a TypeError, not a CancelledError.\n const ffmpeg = this.ffmpeg;\n\n this.notifyListeners('onProgress', { status: 'started', percent: 0 });\n\n const inputFileName = 'input.mp4';\n const outputFileName = 'output.mp4';\n\n let crf = '28';\n let scale = '';\n let audioBitrate = '128k';\n\n switch (quality) {\n case 'VERY_HIGH':\n crf = '23';\n scale = '-vf scale=-2:1080';\n audioBitrate = '128k';\n break;\n case 'HIGH':\n crf = '28';\n scale = '-vf scale=-2:720';\n audioBitrate = '128k';\n break;\n case 'MEDIUM':\n crf = '30';\n scale = '-vf scale=-2:540';\n audioBitrate = '96k';\n break;\n case 'LOW':\n crf = '32';\n scale = '-vf scale=-2:480';\n audioBitrate = '96k';\n break;\n case '360P':\n crf = '28';\n scale = '-vf scale=-2:360';\n audioBitrate = '64k';\n break;\n case 'VERY_LOW':\n crf = '35';\n scale = '-vf scale=-2:240';\n audioBitrate = '48k';\n break;\n }\n\n const inputData = new Uint8Array(await blob.arrayBuffer());\n\n try {\n await ffmpeg.writeFile(inputFileName, inputData);\n } catch (err) {\n if (this.cancelRequested) throw new CancelledError();\n console.error('[FFmpeg] Error writing to FS:', err);\n throw new Error('Could not write to FFmpeg FS');\n }\n throwIfCancelled();\n\n const args: string[] = ['-i', inputFileName];\n args.push('-c:v', 'libx264', '-preset', 'superfast', '-crf', crf);\n if (scale) {\n args.push(...scale.split(' '));\n }\n args.push('-c:a', 'aac', '-b:a', audioBitrate);\n args.push(outputFileName);\n\n try {\n await ffmpeg.exec(args);\n } catch (err) {\n // terminate() rejects every in-flight call, so a cancel arrives here\n // looking like a generic failure unless we name it.\n if (this.cancelRequested) throw new CancelledError();\n console.error('[FFmpeg] Error executing command:', err);\n throw new Error('FFmpeg execution failed');\n }\n throwIfCancelled();\n\n let data: any;\n try {\n data = await ffmpeg.readFile(outputFileName);\n } catch (err) {\n if (this.cancelRequested) throw new CancelledError();\n console.error('[FFmpeg] Error reading output file:', err);\n throw new Error('Could not read FFmpeg output');\n }\n\n await ffmpeg.deleteFile(inputFileName);\n await ffmpeg.deleteFile(outputFileName);\n\n const outBlob = new Blob([data as any], { type: 'video/mp4' });\n return { success: true, destPath: URL.createObjectURL(outBlob) };\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"web.js","sourceRoot":"","sources":["../../src/web.ts"],"names":[],"mappings":"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","sourcesContent":["import { WebPlugin } from '@capacitor/core';\nimport { FFmpeg } from '@ffmpeg/ffmpeg';\nimport { toBlobURL } from '@ffmpeg/util';\nimport {\n ALL_FORMATS,\n BlobSource,\n BufferTarget,\n Conversion,\n ConversionCanceledError,\n Input,\n Mp4OutputFormat,\n Output,\n Quality,\n canEncodeVideo,\n} from 'mediabunny';\n\nimport type {\n NativeVideoCompressorPlugin,\n CompressOptions,\n CompressResult,\n CancelOptions,\n CancelResult,\n} from './definitions';\nimport { CANCELLED } from './definitions';\n\ntype VideoQuality = NonNullable<CompressOptions['quality']>;\n\n/**\n * Rejection shape for a cancelled compress. `code` matches what `call.reject`\n * puts on the error object on iOS/Android, so one `error.code === 'CANCELLED'`\n * check works on every platform — and on both web engines below.\n */\nclass CancelledError extends Error {\n code = CANCELLED;\n constructor() {\n super('Đã hủy nén');\n this.name = 'CancelledError';\n }\n}\n\n/**\n * WebCodecs target per quality tier: cap height + a bitrate ceiling. The\n * quantizer (constant 26, CRF-like) drives quality where the encoder honours\n * it; H.264 on most browsers is bitrate-driven, so `bitrate` is the real knob.\n * fps is capped to 30 at call time (never upsampled). Audio is left to\n * mediabunny, which re-encodes to AAC for the MP4 container.\n */\nconst WEBCODECS_LADDER: Record<VideoQuality, { height: number; bitrate: number }> = {\n VERY_HIGH: { height: 1080, bitrate: 4000000 },\n HIGH: { height: 720, bitrate: 2000000 },\n MEDIUM: { height: 540, bitrate: 1200000 },\n LOW: { height: 480, bitrate: 1000000 },\n '360P': { height: 360, bitrate: 700000 },\n VERY_LOW: { height: 240, bitrate: 350000 },\n};\n\nconst WEBCODECS_QUANTIZER = 26;\nconst FPS_CAP = 30;\n\nconst CORE_BASE_URL = 'https://unpkg.com/@ffmpeg/core@0.12.6/dist/esm';\n\n/**\n * Cancelling means terminating the worker, which drops the loaded core with\n * it — so without this the next compress re-downloads ~30 MB of wasm and\n * \"cancel\" quietly costs more than it saves. Module scope, not instance: the\n * bytes are identical for every instance and every job. Only the ffmpeg\n * fallback ever pays this download; the WebCodecs primary path needs no core.\n */\nlet corePromise: Promise<{ coreURL: string; wasmURL: string }> | null = null;\n\nconst loadCoreURLs = () => {\n if (!corePromise) {\n corePromise = Promise.all([\n toBlobURL(`${CORE_BASE_URL}/ffmpeg-core.js`, 'text/javascript'),\n toBlobURL(`${CORE_BASE_URL}/ffmpeg-core.wasm`, 'application/wasm'),\n ])\n .then(([coreURL, wasmURL]) => ({ coreURL, wasmURL }))\n .catch((e) => {\n // Don't cache a failure — a flaky network shouldn't make every later\n // compress in this session fail too.\n corePromise = null;\n throw e;\n });\n }\n return corePromise;\n};\n\nexport class NativeVideoCompressorWeb extends WebPlugin implements NativeVideoCompressorPlugin {\n private ffmpeg: FFmpeg | null = null;\n private isLoaded = false;\n /** The mediabunny conversion running now, so `cancel()` can abort it. */\n private activeConversion: Conversion | null = null;\n /** The job `compressVideo` is running right now, for `cancel({ jobId })`. */\n private currentJobId: string | null = null;\n private cancelRequested = false;\n\n async initialize(): Promise<{ success: boolean; message?: string }> {\n // WebCodecs (the primary path) needs no core download, so there is nothing\n // to warm. Eagerly loading the ffmpeg core here would pull ~30 MB for a\n // path most compresses never take. The fallback lazy-loads its core on\n // first use. Kept as a resolving no-op so callers that await it still pass.\n return { success: true, message: 'ready' };\n }\n\n async finishUpload(): Promise<{ released: boolean }> {\n // Nothing holds a process alive on the web — the tab either lives or the\n // whole compress is gone with it. Resolving no-op so the one call site\n // can fire unconditionally instead of branching on platform.\n return { released: false };\n }\n\n async compressVideo(options: CompressOptions): Promise<CompressResult> {\n this.currentJobId = options.jobId ?? 'default';\n this.cancelRequested = false;\n\n // Checked at every stage boundary, not just around the encode: cancelling\n // an engine only reaches work already handed to it, so a cancel during the\n // source fetch or before init has nothing to interrupt and would otherwise\n // let the compress carry on as if nothing had happened.\n const throwIfCancelled = () => {\n if (this.cancelRequested) throw new CancelledError();\n };\n\n try {\n throwIfCancelled();\n\n const blob = await this.fetchSourceBlob(options.sourcePath);\n throwIfCancelled();\n\n const quality: VideoQuality = options.quality ?? 'MEDIUM';\n\n if (await this.canUseWebCodecs(quality)) {\n try {\n const result = await this.compressWithWebCodecs(blob, quality, throwIfCancelled);\n if (result) return result;\n // null → conversion invalid / tracks discarded → fall back to ffmpeg.\n } catch (e: any) {\n if (e?.code === CANCELLED) throw e; // user cancel: never fall back\n console.warn('[compress] WebCodecs failed, falling back to ffmpeg:', e);\n throwIfCancelled();\n }\n }\n\n return await this.compressWithFfmpeg(blob, quality, throwIfCancelled);\n } catch (error: any) {\n if (error?.code === CANCELLED) {\n console.log('[compress] cancelled');\n } else {\n console.error('[compress] failed:', error);\n }\n throw error;\n } finally {\n this.currentJobId = null;\n this.cancelRequested = false;\n this.activeConversion = null;\n }\n }\n\n /**\n * Kill whichever engine is mid-compress. Only one is ever active at a time,\n * so acting on both is safe.\n *\n * WebCodecs: `conversion.cancel()` makes the in-flight `execute()` reject\n * with `ConversionCanceledError`, surfaced as a `CancelledError` below.\n * ffmpeg: `exec()` runs synchronously inside the worker and never reaches a\n * message poll while encoding, so `worker.terminate()` is the only way to\n * stop it — its cost is the loaded core, which is why the core URLs are\n * cached above.\n */\n async cancel(options?: CancelOptions): Promise<CancelResult> {\n if (!this.currentJobId) return { cancelled: false };\n if (options?.jobId && options.jobId !== this.currentJobId) return { cancelled: false };\n\n // Set even when neither engine exists yet: the job may still be fetching\n // the source or inside loadFFmpeg, which read this flag at their boundaries.\n this.cancelRequested = true;\n\n const conversion = this.activeConversion;\n this.activeConversion = null;\n // Fire-and-forget: the CancelledError surfaces from the execute() rejection,\n // not from here; awaiting would only make cancel() slower.\n conversion?.cancel().catch(() => undefined);\n\n this.ffmpeg?.terminate();\n this.ffmpeg = null;\n this.isLoaded = false;\n\n return { cancelled: true };\n }\n\n private async fetchSourceBlob(sourcePath: string): Promise<Blob> {\n try {\n const response = await fetch(sourcePath);\n return await response.blob();\n } catch (err) {\n console.error('[compress] Error fetching source file:', err);\n throw new Error('Could not fetch source video file');\n }\n }\n\n private async canUseWebCodecs(quality: VideoQuality): Promise<boolean> {\n try {\n const { bitrate } = WEBCODECS_LADDER[quality];\n return await canEncodeVideo('avc', {\n quality: new Quality({ quantizer: WEBCODECS_QUANTIZER, bitrate }),\n });\n } catch {\n return false;\n }\n }\n\n private async compressWithWebCodecs(\n blob: Blob,\n quality: VideoQuality,\n throwIfCancelled: () => void,\n ): Promise<CompressResult | null> {\n const { height, bitrate } = WEBCODECS_LADDER[quality];\n\n const input = new Input({ formats: ALL_FORMATS, source: new BlobSource(blob) });\n const output = new Output({ format: new Mp4OutputFormat(), target: new BufferTarget() });\n\n // Cap fps at 30, but never upsample: probe the source and take the min.\n // A failed probe just omits frameRate, leaving the source rate untouched.\n let frameRate: number | undefined;\n try {\n const track = await input.getPrimaryVideoTrack();\n if (track) {\n const stats = await track.computePacketStats(100);\n if (stats.averagePacketRate > 0) {\n frameRate = Math.min(FPS_CAP, Math.round(stats.averagePacketRate));\n }\n }\n } catch {\n frameRate = undefined;\n }\n\n throwIfCancelled();\n\n const conversion = await Conversion.init({\n input,\n output,\n video: {\n height,\n quality: new Quality({ quantizer: WEBCODECS_QUANTIZER, bitrate }),\n hardwareAcceleration: 'prefer-hardware',\n ...(frameRate ? { frameRate } : {}),\n },\n });\n\n if (!conversion.isValid) {\n console.warn(\n '[compress] WebCodecs conversion invalid:',\n conversion.discardedTracks.map((t) => t.reason),\n );\n return null;\n }\n\n this.activeConversion = conversion;\n\n this.notifyListeners('onProgress', { status: 'started', percent: 0 });\n conversion.onProgress = (progress) => {\n this.notifyListeners('onProgress', { status: 'progress', percent: Math.round(progress * 100) });\n };\n\n throwIfCancelled();\n try {\n await conversion.execute();\n } catch (e: any) {\n if (this.cancelRequested || e instanceof ConversionCanceledError) throw new CancelledError();\n throw e;\n }\n\n const buffer = output.target.buffer;\n if (!buffer) throw new Error('WebCodecs produced no output');\n\n const outBlob = new Blob([buffer], { type: 'video/mp4' });\n return { success: true, destPath: URL.createObjectURL(outBlob) };\n }\n\n private async loadFFmpeg() {\n if (this.isLoaded && this.ffmpeg) return;\n\n this.notifyListeners('onProgress', { status: 'loading_core', percent: 0 });\n\n // Built on a local, published to the field only once it is ready and still\n // wanted. Assigning `this.ffmpeg` up front and setting `isLoaded` after the\n // await left a window where a cancel could null the instance and the load,\n // finishing a moment later, would set `isLoaded = true` on nothing at all —\n // after which every compressVideo threw 'FFmpeg failed to load' for the\n // rest of the page's life.\n const instance = new FFmpeg();\n\n instance.on('progress', ({ progress }) => {\n this.notifyListeners('onProgress', { status: 'progress', percent: Math.round(progress * 100) });\n });\n\n instance.on('log', ({ message }) => {\n console.log('[FFmpeg Log]', message);\n });\n\n try {\n await instance.load(await loadCoreURLs());\n } catch (e: any) {\n instance.terminate();\n console.error('Failed to load FFmpeg:', e);\n throw e;\n }\n\n // Cancelled while this was loading. Terminate what we just built rather\n // than publish it — the caller asked for none of it.\n if (this.cancelRequested) {\n instance.terminate();\n throw new CancelledError();\n }\n\n this.ffmpeg = instance;\n this.isLoaded = true;\n }\n\n private async compressWithFfmpeg(\n blob: Blob,\n quality: VideoQuality,\n throwIfCancelled: () => void,\n ): Promise<CompressResult> {\n if (!this.isLoaded) {\n await this.loadFFmpeg();\n }\n throwIfCancelled();\n\n if (!this.ffmpeg) {\n throw new Error('FFmpeg failed to load');\n }\n // Bound once. `this.ffmpeg` is nulled by cancel(), so reading it after any\n // of the awaits below can hand back null on a reference the compiler\n // narrowed here — surfacing a cancel as a TypeError, not a CancelledError.\n const ffmpeg = this.ffmpeg;\n\n this.notifyListeners('onProgress', { status: 'started', percent: 0 });\n\n const inputFileName = 'input.mp4';\n const outputFileName = 'output.mp4';\n\n let crf = '28';\n let scale = '';\n let audioBitrate = '128k';\n\n switch (quality) {\n case 'VERY_HIGH':\n crf = '23';\n scale = '-vf scale=-2:1080';\n audioBitrate = '128k';\n break;\n case 'HIGH':\n crf = '28';\n scale = '-vf scale=-2:720';\n audioBitrate = '128k';\n break;\n case 'MEDIUM':\n crf = '30';\n scale = '-vf scale=-2:540';\n audioBitrate = '96k';\n break;\n case 'LOW':\n crf = '32';\n scale = '-vf scale=-2:480';\n audioBitrate = '96k';\n break;\n case '360P':\n crf = '28';\n scale = '-vf scale=-2:360';\n audioBitrate = '64k';\n break;\n case 'VERY_LOW':\n crf = '35';\n scale = '-vf scale=-2:240';\n audioBitrate = '48k';\n break;\n }\n\n const inputData = new Uint8Array(await blob.arrayBuffer());\n\n try {\n await ffmpeg.writeFile(inputFileName, inputData);\n } catch (err) {\n if (this.cancelRequested) throw new CancelledError();\n console.error('[FFmpeg] Error writing to FS:', err);\n throw new Error('Could not write to FFmpeg FS');\n }\n throwIfCancelled();\n\n const args: string[] = ['-i', inputFileName];\n args.push('-c:v', 'libx264', '-preset', 'superfast', '-crf', crf);\n if (scale) {\n args.push(...scale.split(' '));\n }\n args.push('-c:a', 'aac', '-b:a', audioBitrate);\n args.push(outputFileName);\n\n try {\n await ffmpeg.exec(args);\n } catch (err) {\n // terminate() rejects every in-flight call, so a cancel arrives here\n // looking like a generic failure unless we name it.\n if (this.cancelRequested) throw new CancelledError();\n console.error('[FFmpeg] Error executing command:', err);\n throw new Error('FFmpeg execution failed');\n }\n throwIfCancelled();\n\n let data: any;\n try {\n data = await ffmpeg.readFile(outputFileName);\n } catch (err) {\n if (this.cancelRequested) throw new CancelledError();\n console.error('[FFmpeg] Error reading output file:', err);\n throw new Error('Could not read FFmpeg output');\n }\n\n await ffmpeg.deleteFile(inputFileName);\n await ffmpeg.deleteFile(outputFileName);\n\n const outBlob = new Blob([data as any], { type: 'video/mp4' });\n return { success: true, destPath: URL.createObjectURL(outBlob) };\n }\n}\n"]}
|
package/dist/plugin.cjs.js
CHANGED
|
@@ -88,6 +88,12 @@ class NativeVideoCompressorWeb extends core.WebPlugin {
|
|
|
88
88
|
// first use. Kept as a resolving no-op so callers that await it still pass.
|
|
89
89
|
return { success: true, message: 'ready' };
|
|
90
90
|
}
|
|
91
|
+
async finishUpload() {
|
|
92
|
+
// Nothing holds a process alive on the web — the tab either lives or the
|
|
93
|
+
// whole compress is gone with it. Resolving no-op so the one call site
|
|
94
|
+
// can fire unconditionally instead of branching on platform.
|
|
95
|
+
return { released: false };
|
|
96
|
+
}
|
|
91
97
|
async compressVideo(options) {
|
|
92
98
|
var _a, _b;
|
|
93
99
|
this.currentJobId = (_a = options.jobId) !== null && _a !== void 0 ? _a : 'default';
|
package/dist/plugin.cjs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.cjs.js","sources":["esm/definitions.js","esm/index.js","esm/web.js"],"sourcesContent":["/**\n * `code` on the error `compressVideo` rejects with when `cancel()` stopped it.\n * Callers should treat it as \"the user asked for this\" — no error toast, no\n * retry prompt — and tell it apart from a genuine compression failure.\n */\nexport const CANCELLED = 'CANCELLED';\n//# sourceMappingURL=definitions.js.map","import { registerPlugin } from '@capacitor/core';\nconst NativeVideoCompressor = registerPlugin('NativeVideoCompressor', {\n web: () => import('./web').then((m) => new m.NativeVideoCompressorWeb()),\n});\nexport * from './definitions';\nexport { NativeVideoCompressor };\n//# sourceMappingURL=index.js.map","import { WebPlugin } from '@capacitor/core';\nimport { FFmpeg } from '@ffmpeg/ffmpeg';\nimport { toBlobURL } from '@ffmpeg/util';\nimport { ALL_FORMATS, BlobSource, BufferTarget, Conversion, ConversionCanceledError, Input, Mp4OutputFormat, Output, Quality, canEncodeVideo, } from 'mediabunny';\nimport { CANCELLED } from './definitions';\n/**\n * Rejection shape for a cancelled compress. `code` matches what `call.reject`\n * puts on the error object on iOS/Android, so one `error.code === 'CANCELLED'`\n * check works on every platform — and on both web engines below.\n */\nclass CancelledError extends Error {\n constructor() {\n super('Đã hủy nén');\n this.code = CANCELLED;\n this.name = 'CancelledError';\n }\n}\n/**\n * WebCodecs target per quality tier: cap height + a bitrate ceiling. The\n * quantizer (constant 26, CRF-like) drives quality where the encoder honours\n * it; H.264 on most browsers is bitrate-driven, so `bitrate` is the real knob.\n * fps is capped to 30 at call time (never upsampled). Audio is left to\n * mediabunny, which re-encodes to AAC for the MP4 container.\n */\nconst WEBCODECS_LADDER = {\n VERY_HIGH: { height: 1080, bitrate: 4000000 },\n HIGH: { height: 720, bitrate: 2000000 },\n MEDIUM: { height: 540, bitrate: 1200000 },\n LOW: { height: 480, bitrate: 1000000 },\n '360P': { height: 360, bitrate: 700000 },\n VERY_LOW: { height: 240, bitrate: 350000 },\n};\nconst WEBCODECS_QUANTIZER = 26;\nconst FPS_CAP = 30;\nconst CORE_BASE_URL = 'https://unpkg.com/@ffmpeg/core@0.12.6/dist/esm';\n/**\n * Cancelling means terminating the worker, which drops the loaded core with\n * it — so without this the next compress re-downloads ~30 MB of wasm and\n * \"cancel\" quietly costs more than it saves. Module scope, not instance: the\n * bytes are identical for every instance and every job. Only the ffmpeg\n * fallback ever pays this download; the WebCodecs primary path needs no core.\n */\nlet corePromise = null;\nconst loadCoreURLs = () => {\n if (!corePromise) {\n corePromise = Promise.all([\n toBlobURL(`${CORE_BASE_URL}/ffmpeg-core.js`, 'text/javascript'),\n toBlobURL(`${CORE_BASE_URL}/ffmpeg-core.wasm`, 'application/wasm'),\n ])\n .then(([coreURL, wasmURL]) => ({ coreURL, wasmURL }))\n .catch((e) => {\n // Don't cache a failure — a flaky network shouldn't make every later\n // compress in this session fail too.\n corePromise = null;\n throw e;\n });\n }\n return corePromise;\n};\nexport class NativeVideoCompressorWeb extends WebPlugin {\n constructor() {\n super(...arguments);\n this.ffmpeg = null;\n this.isLoaded = false;\n /** The mediabunny conversion running now, so `cancel()` can abort it. */\n this.activeConversion = null;\n /** The job `compressVideo` is running right now, for `cancel({ jobId })`. */\n this.currentJobId = null;\n this.cancelRequested = false;\n }\n async initialize() {\n // WebCodecs (the primary path) needs no core download, so there is nothing\n // to warm. Eagerly loading the ffmpeg core here would pull ~30 MB for a\n // path most compresses never take. The fallback lazy-loads its core on\n // first use. Kept as a resolving no-op so callers that await it still pass.\n return { success: true, message: 'ready' };\n }\n async compressVideo(options) {\n var _a, _b;\n this.currentJobId = (_a = options.jobId) !== null && _a !== void 0 ? _a : 'default';\n this.cancelRequested = false;\n // Checked at every stage boundary, not just around the encode: cancelling\n // an engine only reaches work already handed to it, so a cancel during the\n // source fetch or before init has nothing to interrupt and would otherwise\n // let the compress carry on as if nothing had happened.\n const throwIfCancelled = () => {\n if (this.cancelRequested)\n throw new CancelledError();\n };\n try {\n throwIfCancelled();\n const blob = await this.fetchSourceBlob(options.sourcePath);\n throwIfCancelled();\n const quality = (_b = options.quality) !== null && _b !== void 0 ? _b : 'MEDIUM';\n if (await this.canUseWebCodecs(quality)) {\n try {\n const result = await this.compressWithWebCodecs(blob, quality, throwIfCancelled);\n if (result)\n return result;\n // null → conversion invalid / tracks discarded → fall back to ffmpeg.\n }\n catch (e) {\n if ((e === null || e === void 0 ? void 0 : e.code) === CANCELLED)\n throw e; // user cancel: never fall back\n console.warn('[compress] WebCodecs failed, falling back to ffmpeg:', e);\n throwIfCancelled();\n }\n }\n return await this.compressWithFfmpeg(blob, quality, throwIfCancelled);\n }\n catch (error) {\n if ((error === null || error === void 0 ? void 0 : error.code) === CANCELLED) {\n console.log('[compress] cancelled');\n }\n else {\n console.error('[compress] failed:', error);\n }\n throw error;\n }\n finally {\n this.currentJobId = null;\n this.cancelRequested = false;\n this.activeConversion = null;\n }\n }\n /**\n * Kill whichever engine is mid-compress. Only one is ever active at a time,\n * so acting on both is safe.\n *\n * WebCodecs: `conversion.cancel()` makes the in-flight `execute()` reject\n * with `ConversionCanceledError`, surfaced as a `CancelledError` below.\n * ffmpeg: `exec()` runs synchronously inside the worker and never reaches a\n * message poll while encoding, so `worker.terminate()` is the only way to\n * stop it — its cost is the loaded core, which is why the core URLs are\n * cached above.\n */\n async cancel(options) {\n var _a;\n if (!this.currentJobId)\n return { cancelled: false };\n if ((options === null || options === void 0 ? void 0 : options.jobId) && options.jobId !== this.currentJobId)\n return { cancelled: false };\n // Set even when neither engine exists yet: the job may still be fetching\n // the source or inside loadFFmpeg, which read this flag at their boundaries.\n this.cancelRequested = true;\n const conversion = this.activeConversion;\n this.activeConversion = null;\n // Fire-and-forget: the CancelledError surfaces from the execute() rejection,\n // not from here; awaiting would only make cancel() slower.\n conversion === null || conversion === void 0 ? void 0 : conversion.cancel().catch(() => undefined);\n (_a = this.ffmpeg) === null || _a === void 0 ? void 0 : _a.terminate();\n this.ffmpeg = null;\n this.isLoaded = false;\n return { cancelled: true };\n }\n async fetchSourceBlob(sourcePath) {\n try {\n const response = await fetch(sourcePath);\n return await response.blob();\n }\n catch (err) {\n console.error('[compress] Error fetching source file:', err);\n throw new Error('Could not fetch source video file');\n }\n }\n async canUseWebCodecs(quality) {\n try {\n const { bitrate } = WEBCODECS_LADDER[quality];\n return await canEncodeVideo('avc', {\n quality: new Quality({ quantizer: WEBCODECS_QUANTIZER, bitrate }),\n });\n }\n catch (_a) {\n return false;\n }\n }\n async compressWithWebCodecs(blob, quality, throwIfCancelled) {\n const { height, bitrate } = WEBCODECS_LADDER[quality];\n const input = new Input({ formats: ALL_FORMATS, source: new BlobSource(blob) });\n const output = new Output({ format: new Mp4OutputFormat(), target: new BufferTarget() });\n // Cap fps at 30, but never upsample: probe the source and take the min.\n // A failed probe just omits frameRate, leaving the source rate untouched.\n let frameRate;\n try {\n const track = await input.getPrimaryVideoTrack();\n if (track) {\n const stats = await track.computePacketStats(100);\n if (stats.averagePacketRate > 0) {\n frameRate = Math.min(FPS_CAP, Math.round(stats.averagePacketRate));\n }\n }\n }\n catch (_a) {\n frameRate = undefined;\n }\n throwIfCancelled();\n const conversion = await Conversion.init({\n input,\n output,\n video: Object.assign({ height, quality: new Quality({ quantizer: WEBCODECS_QUANTIZER, bitrate }), hardwareAcceleration: 'prefer-hardware' }, (frameRate ? { frameRate } : {})),\n });\n if (!conversion.isValid) {\n console.warn('[compress] WebCodecs conversion invalid:', conversion.discardedTracks.map((t) => t.reason));\n return null;\n }\n this.activeConversion = conversion;\n this.notifyListeners('onProgress', { status: 'started', percent: 0 });\n conversion.onProgress = (progress) => {\n this.notifyListeners('onProgress', { status: 'progress', percent: Math.round(progress * 100) });\n };\n throwIfCancelled();\n try {\n await conversion.execute();\n }\n catch (e) {\n if (this.cancelRequested || e instanceof ConversionCanceledError)\n throw new CancelledError();\n throw e;\n }\n const buffer = output.target.buffer;\n if (!buffer)\n throw new Error('WebCodecs produced no output');\n const outBlob = new Blob([buffer], { type: 'video/mp4' });\n return { success: true, destPath: URL.createObjectURL(outBlob) };\n }\n async loadFFmpeg() {\n if (this.isLoaded && this.ffmpeg)\n return;\n this.notifyListeners('onProgress', { status: 'loading_core', percent: 0 });\n // Built on a local, published to the field only once it is ready and still\n // wanted. Assigning `this.ffmpeg` up front and setting `isLoaded` after the\n // await left a window where a cancel could null the instance and the load,\n // finishing a moment later, would set `isLoaded = true` on nothing at all —\n // after which every compressVideo threw 'FFmpeg failed to load' for the\n // rest of the page's life.\n const instance = new FFmpeg();\n instance.on('progress', ({ progress }) => {\n this.notifyListeners('onProgress', { status: 'progress', percent: Math.round(progress * 100) });\n });\n instance.on('log', ({ message }) => {\n console.log('[FFmpeg Log]', message);\n });\n try {\n await instance.load(await loadCoreURLs());\n }\n catch (e) {\n instance.terminate();\n console.error('Failed to load FFmpeg:', e);\n throw e;\n }\n // Cancelled while this was loading. Terminate what we just built rather\n // than publish it — the caller asked for none of it.\n if (this.cancelRequested) {\n instance.terminate();\n throw new CancelledError();\n }\n this.ffmpeg = instance;\n this.isLoaded = true;\n }\n async compressWithFfmpeg(blob, quality, throwIfCancelled) {\n if (!this.isLoaded) {\n await this.loadFFmpeg();\n }\n throwIfCancelled();\n if (!this.ffmpeg) {\n throw new Error('FFmpeg failed to load');\n }\n // Bound once. `this.ffmpeg` is nulled by cancel(), so reading it after any\n // of the awaits below can hand back null on a reference the compiler\n // narrowed here — surfacing a cancel as a TypeError, not a CancelledError.\n const ffmpeg = this.ffmpeg;\n this.notifyListeners('onProgress', { status: 'started', percent: 0 });\n const inputFileName = 'input.mp4';\n const outputFileName = 'output.mp4';\n let crf = '28';\n let scale = '';\n let audioBitrate = '128k';\n switch (quality) {\n case 'VERY_HIGH':\n crf = '23';\n scale = '-vf scale=-2:1080';\n audioBitrate = '128k';\n break;\n case 'HIGH':\n crf = '28';\n scale = '-vf scale=-2:720';\n audioBitrate = '128k';\n break;\n case 'MEDIUM':\n crf = '30';\n scale = '-vf scale=-2:540';\n audioBitrate = '96k';\n break;\n case 'LOW':\n crf = '32';\n scale = '-vf scale=-2:480';\n audioBitrate = '96k';\n break;\n case '360P':\n crf = '28';\n scale = '-vf scale=-2:360';\n audioBitrate = '64k';\n break;\n case 'VERY_LOW':\n crf = '35';\n scale = '-vf scale=-2:240';\n audioBitrate = '48k';\n break;\n }\n const inputData = new Uint8Array(await blob.arrayBuffer());\n try {\n await ffmpeg.writeFile(inputFileName, inputData);\n }\n catch (err) {\n if (this.cancelRequested)\n throw new CancelledError();\n console.error('[FFmpeg] Error writing to FS:', err);\n throw new Error('Could not write to FFmpeg FS');\n }\n throwIfCancelled();\n const args = ['-i', inputFileName];\n args.push('-c:v', 'libx264', '-preset', 'superfast', '-crf', crf);\n if (scale) {\n args.push(...scale.split(' '));\n }\n args.push('-c:a', 'aac', '-b:a', audioBitrate);\n args.push(outputFileName);\n try {\n await ffmpeg.exec(args);\n }\n catch (err) {\n // terminate() rejects every in-flight call, so a cancel arrives here\n // looking like a generic failure unless we name it.\n if (this.cancelRequested)\n throw new CancelledError();\n console.error('[FFmpeg] Error executing command:', err);\n throw new Error('FFmpeg execution failed');\n }\n throwIfCancelled();\n let data;\n try {\n data = await ffmpeg.readFile(outputFileName);\n }\n catch (err) {\n if (this.cancelRequested)\n throw new CancelledError();\n console.error('[FFmpeg] Error reading output file:', err);\n throw new Error('Could not read FFmpeg output');\n }\n await ffmpeg.deleteFile(inputFileName);\n await ffmpeg.deleteFile(outputFileName);\n const outBlob = new Blob([data], { type: 'video/mp4' });\n return { success: true, destPath: URL.createObjectURL(outBlob) };\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","toBlobURL","WebPlugin","canEncodeVideo","Quality","Input","ALL_FORMATS","BlobSource","Output","Mp4OutputFormat","BufferTarget","Conversion","ConversionCanceledError","FFmpeg"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"plugin.cjs.js","sources":["esm/definitions.js","esm/index.js","esm/web.js"],"sourcesContent":["/**\n * `code` on the error `compressVideo` rejects with when `cancel()` stopped it.\n * Callers should treat it as \"the user asked for this\" — no error toast, no\n * retry prompt — and tell it apart from a genuine compression failure.\n */\nexport const CANCELLED = 'CANCELLED';\n//# sourceMappingURL=definitions.js.map","import { registerPlugin } from '@capacitor/core';\nconst NativeVideoCompressor = registerPlugin('NativeVideoCompressor', {\n web: () => import('./web').then((m) => new m.NativeVideoCompressorWeb()),\n});\nexport * from './definitions';\nexport { NativeVideoCompressor };\n//# sourceMappingURL=index.js.map","import { WebPlugin } from '@capacitor/core';\nimport { FFmpeg } from '@ffmpeg/ffmpeg';\nimport { toBlobURL } from '@ffmpeg/util';\nimport { ALL_FORMATS, BlobSource, BufferTarget, Conversion, ConversionCanceledError, Input, Mp4OutputFormat, Output, Quality, canEncodeVideo, } from 'mediabunny';\nimport { CANCELLED } from './definitions';\n/**\n * Rejection shape for a cancelled compress. `code` matches what `call.reject`\n * puts on the error object on iOS/Android, so one `error.code === 'CANCELLED'`\n * check works on every platform — and on both web engines below.\n */\nclass CancelledError extends Error {\n constructor() {\n super('Đã hủy nén');\n this.code = CANCELLED;\n this.name = 'CancelledError';\n }\n}\n/**\n * WebCodecs target per quality tier: cap height + a bitrate ceiling. The\n * quantizer (constant 26, CRF-like) drives quality where the encoder honours\n * it; H.264 on most browsers is bitrate-driven, so `bitrate` is the real knob.\n * fps is capped to 30 at call time (never upsampled). Audio is left to\n * mediabunny, which re-encodes to AAC for the MP4 container.\n */\nconst WEBCODECS_LADDER = {\n VERY_HIGH: { height: 1080, bitrate: 4000000 },\n HIGH: { height: 720, bitrate: 2000000 },\n MEDIUM: { height: 540, bitrate: 1200000 },\n LOW: { height: 480, bitrate: 1000000 },\n '360P': { height: 360, bitrate: 700000 },\n VERY_LOW: { height: 240, bitrate: 350000 },\n};\nconst WEBCODECS_QUANTIZER = 26;\nconst FPS_CAP = 30;\nconst CORE_BASE_URL = 'https://unpkg.com/@ffmpeg/core@0.12.6/dist/esm';\n/**\n * Cancelling means terminating the worker, which drops the loaded core with\n * it — so without this the next compress re-downloads ~30 MB of wasm and\n * \"cancel\" quietly costs more than it saves. Module scope, not instance: the\n * bytes are identical for every instance and every job. Only the ffmpeg\n * fallback ever pays this download; the WebCodecs primary path needs no core.\n */\nlet corePromise = null;\nconst loadCoreURLs = () => {\n if (!corePromise) {\n corePromise = Promise.all([\n toBlobURL(`${CORE_BASE_URL}/ffmpeg-core.js`, 'text/javascript'),\n toBlobURL(`${CORE_BASE_URL}/ffmpeg-core.wasm`, 'application/wasm'),\n ])\n .then(([coreURL, wasmURL]) => ({ coreURL, wasmURL }))\n .catch((e) => {\n // Don't cache a failure — a flaky network shouldn't make every later\n // compress in this session fail too.\n corePromise = null;\n throw e;\n });\n }\n return corePromise;\n};\nexport class NativeVideoCompressorWeb extends WebPlugin {\n constructor() {\n super(...arguments);\n this.ffmpeg = null;\n this.isLoaded = false;\n /** The mediabunny conversion running now, so `cancel()` can abort it. */\n this.activeConversion = null;\n /** The job `compressVideo` is running right now, for `cancel({ jobId })`. */\n this.currentJobId = null;\n this.cancelRequested = false;\n }\n async initialize() {\n // WebCodecs (the primary path) needs no core download, so there is nothing\n // to warm. Eagerly loading the ffmpeg core here would pull ~30 MB for a\n // path most compresses never take. The fallback lazy-loads its core on\n // first use. Kept as a resolving no-op so callers that await it still pass.\n return { success: true, message: 'ready' };\n }\n async finishUpload() {\n // Nothing holds a process alive on the web — the tab either lives or the\n // whole compress is gone with it. Resolving no-op so the one call site\n // can fire unconditionally instead of branching on platform.\n return { released: false };\n }\n async compressVideo(options) {\n var _a, _b;\n this.currentJobId = (_a = options.jobId) !== null && _a !== void 0 ? _a : 'default';\n this.cancelRequested = false;\n // Checked at every stage boundary, not just around the encode: cancelling\n // an engine only reaches work already handed to it, so a cancel during the\n // source fetch or before init has nothing to interrupt and would otherwise\n // let the compress carry on as if nothing had happened.\n const throwIfCancelled = () => {\n if (this.cancelRequested)\n throw new CancelledError();\n };\n try {\n throwIfCancelled();\n const blob = await this.fetchSourceBlob(options.sourcePath);\n throwIfCancelled();\n const quality = (_b = options.quality) !== null && _b !== void 0 ? _b : 'MEDIUM';\n if (await this.canUseWebCodecs(quality)) {\n try {\n const result = await this.compressWithWebCodecs(blob, quality, throwIfCancelled);\n if (result)\n return result;\n // null → conversion invalid / tracks discarded → fall back to ffmpeg.\n }\n catch (e) {\n if ((e === null || e === void 0 ? void 0 : e.code) === CANCELLED)\n throw e; // user cancel: never fall back\n console.warn('[compress] WebCodecs failed, falling back to ffmpeg:', e);\n throwIfCancelled();\n }\n }\n return await this.compressWithFfmpeg(blob, quality, throwIfCancelled);\n }\n catch (error) {\n if ((error === null || error === void 0 ? void 0 : error.code) === CANCELLED) {\n console.log('[compress] cancelled');\n }\n else {\n console.error('[compress] failed:', error);\n }\n throw error;\n }\n finally {\n this.currentJobId = null;\n this.cancelRequested = false;\n this.activeConversion = null;\n }\n }\n /**\n * Kill whichever engine is mid-compress. Only one is ever active at a time,\n * so acting on both is safe.\n *\n * WebCodecs: `conversion.cancel()` makes the in-flight `execute()` reject\n * with `ConversionCanceledError`, surfaced as a `CancelledError` below.\n * ffmpeg: `exec()` runs synchronously inside the worker and never reaches a\n * message poll while encoding, so `worker.terminate()` is the only way to\n * stop it — its cost is the loaded core, which is why the core URLs are\n * cached above.\n */\n async cancel(options) {\n var _a;\n if (!this.currentJobId)\n return { cancelled: false };\n if ((options === null || options === void 0 ? void 0 : options.jobId) && options.jobId !== this.currentJobId)\n return { cancelled: false };\n // Set even when neither engine exists yet: the job may still be fetching\n // the source or inside loadFFmpeg, which read this flag at their boundaries.\n this.cancelRequested = true;\n const conversion = this.activeConversion;\n this.activeConversion = null;\n // Fire-and-forget: the CancelledError surfaces from the execute() rejection,\n // not from here; awaiting would only make cancel() slower.\n conversion === null || conversion === void 0 ? void 0 : conversion.cancel().catch(() => undefined);\n (_a = this.ffmpeg) === null || _a === void 0 ? void 0 : _a.terminate();\n this.ffmpeg = null;\n this.isLoaded = false;\n return { cancelled: true };\n }\n async fetchSourceBlob(sourcePath) {\n try {\n const response = await fetch(sourcePath);\n return await response.blob();\n }\n catch (err) {\n console.error('[compress] Error fetching source file:', err);\n throw new Error('Could not fetch source video file');\n }\n }\n async canUseWebCodecs(quality) {\n try {\n const { bitrate } = WEBCODECS_LADDER[quality];\n return await canEncodeVideo('avc', {\n quality: new Quality({ quantizer: WEBCODECS_QUANTIZER, bitrate }),\n });\n }\n catch (_a) {\n return false;\n }\n }\n async compressWithWebCodecs(blob, quality, throwIfCancelled) {\n const { height, bitrate } = WEBCODECS_LADDER[quality];\n const input = new Input({ formats: ALL_FORMATS, source: new BlobSource(blob) });\n const output = new Output({ format: new Mp4OutputFormat(), target: new BufferTarget() });\n // Cap fps at 30, but never upsample: probe the source and take the min.\n // A failed probe just omits frameRate, leaving the source rate untouched.\n let frameRate;\n try {\n const track = await input.getPrimaryVideoTrack();\n if (track) {\n const stats = await track.computePacketStats(100);\n if (stats.averagePacketRate > 0) {\n frameRate = Math.min(FPS_CAP, Math.round(stats.averagePacketRate));\n }\n }\n }\n catch (_a) {\n frameRate = undefined;\n }\n throwIfCancelled();\n const conversion = await Conversion.init({\n input,\n output,\n video: Object.assign({ height, quality: new Quality({ quantizer: WEBCODECS_QUANTIZER, bitrate }), hardwareAcceleration: 'prefer-hardware' }, (frameRate ? { frameRate } : {})),\n });\n if (!conversion.isValid) {\n console.warn('[compress] WebCodecs conversion invalid:', conversion.discardedTracks.map((t) => t.reason));\n return null;\n }\n this.activeConversion = conversion;\n this.notifyListeners('onProgress', { status: 'started', percent: 0 });\n conversion.onProgress = (progress) => {\n this.notifyListeners('onProgress', { status: 'progress', percent: Math.round(progress * 100) });\n };\n throwIfCancelled();\n try {\n await conversion.execute();\n }\n catch (e) {\n if (this.cancelRequested || e instanceof ConversionCanceledError)\n throw new CancelledError();\n throw e;\n }\n const buffer = output.target.buffer;\n if (!buffer)\n throw new Error('WebCodecs produced no output');\n const outBlob = new Blob([buffer], { type: 'video/mp4' });\n return { success: true, destPath: URL.createObjectURL(outBlob) };\n }\n async loadFFmpeg() {\n if (this.isLoaded && this.ffmpeg)\n return;\n this.notifyListeners('onProgress', { status: 'loading_core', percent: 0 });\n // Built on a local, published to the field only once it is ready and still\n // wanted. Assigning `this.ffmpeg` up front and setting `isLoaded` after the\n // await left a window where a cancel could null the instance and the load,\n // finishing a moment later, would set `isLoaded = true` on nothing at all —\n // after which every compressVideo threw 'FFmpeg failed to load' for the\n // rest of the page's life.\n const instance = new FFmpeg();\n instance.on('progress', ({ progress }) => {\n this.notifyListeners('onProgress', { status: 'progress', percent: Math.round(progress * 100) });\n });\n instance.on('log', ({ message }) => {\n console.log('[FFmpeg Log]', message);\n });\n try {\n await instance.load(await loadCoreURLs());\n }\n catch (e) {\n instance.terminate();\n console.error('Failed to load FFmpeg:', e);\n throw e;\n }\n // Cancelled while this was loading. Terminate what we just built rather\n // than publish it — the caller asked for none of it.\n if (this.cancelRequested) {\n instance.terminate();\n throw new CancelledError();\n }\n this.ffmpeg = instance;\n this.isLoaded = true;\n }\n async compressWithFfmpeg(blob, quality, throwIfCancelled) {\n if (!this.isLoaded) {\n await this.loadFFmpeg();\n }\n throwIfCancelled();\n if (!this.ffmpeg) {\n throw new Error('FFmpeg failed to load');\n }\n // Bound once. `this.ffmpeg` is nulled by cancel(), so reading it after any\n // of the awaits below can hand back null on a reference the compiler\n // narrowed here — surfacing a cancel as a TypeError, not a CancelledError.\n const ffmpeg = this.ffmpeg;\n this.notifyListeners('onProgress', { status: 'started', percent: 0 });\n const inputFileName = 'input.mp4';\n const outputFileName = 'output.mp4';\n let crf = '28';\n let scale = '';\n let audioBitrate = '128k';\n switch (quality) {\n case 'VERY_HIGH':\n crf = '23';\n scale = '-vf scale=-2:1080';\n audioBitrate = '128k';\n break;\n case 'HIGH':\n crf = '28';\n scale = '-vf scale=-2:720';\n audioBitrate = '128k';\n break;\n case 'MEDIUM':\n crf = '30';\n scale = '-vf scale=-2:540';\n audioBitrate = '96k';\n break;\n case 'LOW':\n crf = '32';\n scale = '-vf scale=-2:480';\n audioBitrate = '96k';\n break;\n case '360P':\n crf = '28';\n scale = '-vf scale=-2:360';\n audioBitrate = '64k';\n break;\n case 'VERY_LOW':\n crf = '35';\n scale = '-vf scale=-2:240';\n audioBitrate = '48k';\n break;\n }\n const inputData = new Uint8Array(await blob.arrayBuffer());\n try {\n await ffmpeg.writeFile(inputFileName, inputData);\n }\n catch (err) {\n if (this.cancelRequested)\n throw new CancelledError();\n console.error('[FFmpeg] Error writing to FS:', err);\n throw new Error('Could not write to FFmpeg FS');\n }\n throwIfCancelled();\n const args = ['-i', inputFileName];\n args.push('-c:v', 'libx264', '-preset', 'superfast', '-crf', crf);\n if (scale) {\n args.push(...scale.split(' '));\n }\n args.push('-c:a', 'aac', '-b:a', audioBitrate);\n args.push(outputFileName);\n try {\n await ffmpeg.exec(args);\n }\n catch (err) {\n // terminate() rejects every in-flight call, so a cancel arrives here\n // looking like a generic failure unless we name it.\n if (this.cancelRequested)\n throw new CancelledError();\n console.error('[FFmpeg] Error executing command:', err);\n throw new Error('FFmpeg execution failed');\n }\n throwIfCancelled();\n let data;\n try {\n data = await ffmpeg.readFile(outputFileName);\n }\n catch (err) {\n if (this.cancelRequested)\n throw new CancelledError();\n console.error('[FFmpeg] Error reading output file:', err);\n throw new Error('Could not read FFmpeg output');\n }\n await ffmpeg.deleteFile(inputFileName);\n await ffmpeg.deleteFile(outputFileName);\n const outBlob = new Blob([data], { type: 'video/mp4' });\n return { success: true, destPath: URL.createObjectURL(outBlob) };\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","toBlobURL","WebPlugin","canEncodeVideo","Quality","Input","ALL_FORMATS","BlobSource","Output","Mp4OutputFormat","BufferTarget","Conversion","ConversionCanceledError","FFmpeg"],"mappings":"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"}
|
package/dist/plugin.js
CHANGED
|
@@ -84,6 +84,12 @@ var capacitorNativeVideoCompressor = (function (exports, core, ffmpeg, util, med
|
|
|
84
84
|
// first use. Kept as a resolving no-op so callers that await it still pass.
|
|
85
85
|
return { success: true, message: 'ready' };
|
|
86
86
|
}
|
|
87
|
+
async finishUpload() {
|
|
88
|
+
// Nothing holds a process alive on the web — the tab either lives or the
|
|
89
|
+
// whole compress is gone with it. Resolving no-op so the one call site
|
|
90
|
+
// can fire unconditionally instead of branching on platform.
|
|
91
|
+
return { released: false };
|
|
92
|
+
}
|
|
87
93
|
async compressVideo(options) {
|
|
88
94
|
var _a, _b;
|
|
89
95
|
this.currentJobId = (_a = options.jobId) !== null && _a !== void 0 ? _a : 'default';
|