capacitor-native-video-compressor 1.0.4 → 1.0.5
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 +88 -5
- package/android/src/main/java/com/atomic/videocompressor/NativeVideoCompressorPlugin.java +88 -5
- package/dist/docs.json +58 -0
- package/dist/esm/definitions.d.ts +35 -0
- package/dist/esm/definitions.js +6 -1
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/web.d.ts +16 -1
- package/dist/esm/web.js +135 -23
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +142 -23
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +142 -23
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/NativeVideoCompressorPlugin/NativeVideoCompressorPlugin.swift +112 -8
- package/package.json +1 -1
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 { fetchFile, toBlobURL } from '@ffmpeg/util';\n\nimport type { NativeVideoCompressorPlugin, CompressOptions, CompressResult } from './definitions';\n\nexport class NativeVideoCompressorWeb extends WebPlugin implements NativeVideoCompressorPlugin {\n private ffmpeg: FFmpeg | null = null;\n private isLoaded = false;\n\n private async loadFFmpeg() {\n if (this.isLoaded && this.ffmpeg) return;\n\n this.notifyListeners('onProgress', { status: 'loading_core', percent: 0 });\n\n try {\n this.ffmpeg = new FFmpeg();\n\n // Listen to FFmpeg progress and map it to capacitor plugin events\n this.ffmpeg.on('progress', ({ progress }) => {\n // progress is a fraction between 0 and 1\n this.notifyListeners('onProgress', { status: 'progress', percent: Math.round(progress * 100) });\n });\n\n this.ffmpeg.on('log', ({ message }) => {\n console.log('[FFmpeg Log]', message);\n });\n\n // Use standard single-threaded version for best compatibility across browsers\n // without requiring complex SharedArrayBuffer headers configuration\n const baseURL = 'https://unpkg.com/@ffmpeg/core@0.12.6/dist/esm';\n\n console.log('Starting to load FFmpeg core from:', baseURL);\n await this.ffmpeg.load({\n coreURL: await toBlobURL(`${baseURL}/ffmpeg-core.js`, 'text/javascript'),\n wasmURL: await toBlobURL(`${baseURL}/ffmpeg-core.wasm`, 'application/wasm'),\n });\n\n this.isLoaded = true;\n console.log('FFmpeg core loaded successfully');\n } catch (e: any) {\n console.error('Failed to load FFmpeg:', e);\n throw e;\n }\n }\n\n async initialize(): Promise<{ success: boolean; message?: string }> {\n try {\n if (this.isLoaded) {\n return { success: true, message: 'Already initialized' };\n }\n await this.loadFFmpeg();\n return { success: true, message: 'Initialization successful' };\n } catch (e: any) {\n return { success: false, message: e.message || 'Initialization failed' };\n }\n }\n\n async compressVideo(options: CompressOptions): Promise<CompressResult> {\n try {\n if (!this.isLoaded) {\n await this.loadFFmpeg();\n }\n\n if (!this.ffmpeg) {\n throw new Error('FFmpeg failed to load');\n }\n\n this.notifyListeners('onProgress', { status: 'started', percent: 0 });\n\n const { sourcePath, quality = 'MEDIUM' } = options;\n const inputFileName = 'input.mp4';\n const outputFileName = 'output.mp4';\n\n // Map quality to CRF, Scale, and Audio Bitrate\n let crf = '28';\n let scale = ''; // default keep resolution\n let audioBitrate = '128k';\n\n switch (quality) {\n case 'VERY_HIGH': // 1080p\n crf = '23';\n scale = '-vf scale=-2:1080';\n audioBitrate = '128k';\n break;\n case 'HIGH': // 720p\n crf = '28';\n scale = '-vf scale=-2:720';\n audioBitrate = '128k';\n break;\n case 'MEDIUM': // 540p\n crf = '30';\n scale = '-vf scale=-2:540';\n audioBitrate = '96k';\n break;\n case 'LOW': // 480p\n crf = '32';\n scale = '-vf scale=-2:480';\n audioBitrate = '96k';\n break;\n case '360P': // 360p\n crf = '28';\n scale = '-vf scale=-2:360';\n audioBitrate = '64k';\n break;\n case 'VERY_LOW': // 240p\n crf = '35';\n scale = '-vf scale=-2:240';\n audioBitrate = '48k';\n break;\n }\n\n console.log('[FFmpeg] Fetching source file...', sourcePath);\n let inputData: Uint8Array;\n try {\n inputData = await fetchFile(sourcePath);\n } catch (err) {\n console.error('[FFmpeg] Error fetching source file:', err);\n throw new Error('Could not fetch source video file');\n }\n\n console.log('[FFmpeg] Writing file to FS...', inputData.byteLength, 'bytes');\n try {\n await this.ffmpeg.writeFile(inputFileName, inputData);\n } catch (err) {\n console.error('[FFmpeg] Error writing to FS:', err);\n throw new Error('Could not write to FFmpeg FS');\n }\n\n // Construct FFmpeg command array\n const args: string[] = ['-i', inputFileName];\n\n // Video codec and compression parameters (superfast provides much better size than ultrafast)\n args.push('-c:v', 'libx264', '-preset', 'superfast', '-crf', crf);\n\n // Resize if needed (split by space)\n if (scale) {\n args.push(...scale.split(' '));\n }\n\n // Audio re-encoding to save space\n args.push('-c:a', 'aac', '-b:a', audioBitrate);\n\n // Output\n args.push(outputFileName);\n\n console.log('[FFmpeg] Executing command:', args.join(' '));\n try {\n const retCode = await this.ffmpeg.exec(args);\n console.log('[FFmpeg] Exec returned code:', retCode);\n } catch (err) {\n console.error('[FFmpeg] Error executing command:', err);\n throw new Error('FFmpeg execution failed');\n }\n\n console.log('[FFmpeg] Reading output file...');\n let data: any;\n try {\n data = await this.ffmpeg.readFile(outputFileName);\n } catch (err) {\n console.error('[FFmpeg] Error reading output file:', err);\n throw new Error('Could not read FFmpeg output');\n }\n\n // Free memory\n await this.ffmpeg.deleteFile(inputFileName);\n await this.ffmpeg.deleteFile(outputFileName);\n\n console.log('[FFmpeg] Creating Blob URL from output...', data.byteLength, 'bytes');\n // Create a blob URL for the resulting file\n const blob = new Blob([data as any], { type: 'video/mp4' });\n const destPath = URL.createObjectURL(blob);\n\n console.log('[FFmpeg] Compression complete, returning path:', destPath);\n return {\n success: true,\n destPath: destPath,\n };\n } catch (error: any) {\n console.error('[FFmpeg] Compression process failed:', error);\n throw error;\n }\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 { fetchFile, toBlobURL } from '@ffmpeg/util';\n\nimport type {\n NativeVideoCompressorPlugin,\n CompressOptions,\n CompressResult,\n CancelOptions,\n CancelResult,\n} from './definitions';\nimport { CANCELLED } from './definitions';\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.\n */\nclass CancelledError extends Error {\n code = CANCELLED;\n constructor() {\n super('Đã hủy nén');\n this.name = 'CancelledError';\n }\n}\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.\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 job `compressVideo` is running right now, for `cancel({ jobId })`. */\n private currentJobId: string | null = null;\n private cancelRequested = false;\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 // Listen to FFmpeg progress and map it to capacitor plugin events\n instance.on('progress', ({ progress }) => {\n // progress is a fraction between 0 and 1\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 // Use standard single-threaded version for best compatibility across browsers\n // without requiring complex SharedArrayBuffer headers configuration\n console.log('Starting to load FFmpeg core from:', CORE_BASE_URL);\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 console.log('FFmpeg core loaded successfully');\n }\n\n /**\n * Kill the worker outright — there is no gentler option.\n *\n * `ffmpeg.exec()` runs *synchronously* inside the worker (see\n * @ffmpeg/ffmpeg's worker.js: `ffmpeg.exec(...args)` is not awaited), so the\n * worker never reaches a message poll while encoding. The AbortSignal that\n * `exec()` accepts only rejects the main-thread promise; the encode keeps\n * burning CPU behind it. `worker.terminate()` is what actually stops it.\n *\n * The cost is the loaded core, which is why the core URLs are 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 there is no instance yet: the job may still be inside\n // loadFFmpeg, which reads this flag before publishing what it built.\n this.cancelRequested = true;\n this.ffmpeg?.terminate();\n this.ffmpeg = null;\n this.isLoaded = false;\n return { cancelled: true };\n }\n\n async initialize(): Promise<{ success: boolean; message?: string }> {\n try {\n if (this.isLoaded) {\n return { success: true, message: 'Already initialized' };\n }\n await this.loadFFmpeg();\n return { success: true, message: 'Initialization successful' };\n } catch (e: any) {\n return { success: false, message: e.message || 'Initialization failed' };\n }\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 exec: terminate() only\n // reaches work that has already got to the worker, so a cancel during the\n // core download or the source fetch has nothing to interrupt and would\n // otherwise 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 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\n // any of the four awaits below can hand back null on a reference the\n // compiler narrowed at this line — surfacing a cancel as a TypeError\n // instead of a CancelledError.\n const ffmpeg = this.ffmpeg;\n\n this.notifyListeners('onProgress', { status: 'started', percent: 0 });\n\n const { sourcePath, quality = 'MEDIUM' } = options;\n const inputFileName = 'input.mp4';\n const outputFileName = 'output.mp4';\n\n // Map quality to CRF, Scale, and Audio Bitrate\n let crf = '28';\n let scale = ''; // default keep resolution\n let audioBitrate = '128k';\n\n switch (quality) {\n case 'VERY_HIGH': // 1080p\n crf = '23';\n scale = '-vf scale=-2:1080';\n audioBitrate = '128k';\n break;\n case 'HIGH': // 720p\n crf = '28';\n scale = '-vf scale=-2:720';\n audioBitrate = '128k';\n break;\n case 'MEDIUM': // 540p\n crf = '30';\n scale = '-vf scale=-2:540';\n audioBitrate = '96k';\n break;\n case 'LOW': // 480p\n crf = '32';\n scale = '-vf scale=-2:480';\n audioBitrate = '96k';\n break;\n case '360P': // 360p\n crf = '28';\n scale = '-vf scale=-2:360';\n audioBitrate = '64k';\n break;\n case 'VERY_LOW': // 240p\n crf = '35';\n scale = '-vf scale=-2:240';\n audioBitrate = '48k';\n break;\n }\n\n console.log('[FFmpeg] Fetching source file...', sourcePath);\n let inputData: Uint8Array;\n try {\n inputData = await fetchFile(sourcePath);\n } catch (err) {\n console.error('[FFmpeg] Error fetching source file:', err);\n throw new Error('Could not fetch source video file');\n }\n throwIfCancelled();\n\n console.log('[FFmpeg] Writing file to FS...', inputData.byteLength, 'bytes');\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 // Construct FFmpeg command array\n const args: string[] = ['-i', inputFileName];\n\n // Video codec and compression parameters (superfast provides much better size than ultrafast)\n args.push('-c:v', 'libx264', '-preset', 'superfast', '-crf', crf);\n\n // Resize if needed (split by space)\n if (scale) {\n args.push(...scale.split(' '));\n }\n\n // Audio re-encoding to save space\n args.push('-c:a', 'aac', '-b:a', audioBitrate);\n\n // Output\n args.push(outputFileName);\n\n console.log('[FFmpeg] Executing command:', args.join(' '));\n try {\n const retCode = await ffmpeg.exec(args);\n console.log('[FFmpeg] Exec returned code:', retCode);\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 console.log('[FFmpeg] Reading output file...');\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 // Free memory\n await ffmpeg.deleteFile(inputFileName);\n await ffmpeg.deleteFile(outputFileName);\n\n console.log('[FFmpeg] Creating Blob URL from output...', data.byteLength, 'bytes');\n // Create a blob URL for the resulting file\n const blob = new Blob([data as any], { type: 'video/mp4' });\n const destPath = URL.createObjectURL(blob);\n\n console.log('[FFmpeg] Compression complete, returning path:', destPath);\n return {\n success: true,\n destPath: destPath,\n };\n } catch (error: any) {\n if (error?.code === CANCELLED) {\n console.log('[FFmpeg] Compression cancelled');\n } else {\n console.error('[FFmpeg] Compression process failed:', error);\n }\n throw error;\n } finally {\n this.currentJobId = null;\n this.cancelRequested = false;\n }\n }\n}\n"]}
|
package/dist/plugin.cjs.js
CHANGED
|
@@ -4,45 +4,126 @@ var core = require('@capacitor/core');
|
|
|
4
4
|
var ffmpeg = require('@ffmpeg/ffmpeg');
|
|
5
5
|
var util = require('@ffmpeg/util');
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* `code` on the error `compressVideo` rejects with when `cancel()` stopped it.
|
|
9
|
+
* Callers should treat it as "the user asked for this" — no error toast, no
|
|
10
|
+
* retry prompt — and tell it apart from a genuine compression failure.
|
|
11
|
+
*/
|
|
12
|
+
const CANCELLED = 'CANCELLED';
|
|
13
|
+
|
|
7
14
|
const NativeVideoCompressor = core.registerPlugin('NativeVideoCompressor', {
|
|
8
15
|
web: () => Promise.resolve().then(function () { return web; }).then((m) => new m.NativeVideoCompressorWeb()),
|
|
9
16
|
});
|
|
10
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Rejection shape for a cancelled compress. `code` matches what `call.reject`
|
|
20
|
+
* puts on the error object on iOS/Android, so one `error.code === 'CANCELLED'`
|
|
21
|
+
* check works on every platform.
|
|
22
|
+
*/
|
|
23
|
+
class CancelledError extends Error {
|
|
24
|
+
constructor() {
|
|
25
|
+
super('Đã hủy nén');
|
|
26
|
+
this.code = CANCELLED;
|
|
27
|
+
this.name = 'CancelledError';
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
const CORE_BASE_URL = 'https://unpkg.com/@ffmpeg/core@0.12.6/dist/esm';
|
|
31
|
+
/**
|
|
32
|
+
* Cancelling means terminating the worker, which drops the loaded core with
|
|
33
|
+
* it — so without this the next compress re-downloads ~30 MB of wasm and
|
|
34
|
+
* "cancel" quietly costs more than it saves. Module scope, not instance: the
|
|
35
|
+
* bytes are identical for every instance and every job.
|
|
36
|
+
*/
|
|
37
|
+
let corePromise = null;
|
|
38
|
+
const loadCoreURLs = () => {
|
|
39
|
+
if (!corePromise) {
|
|
40
|
+
corePromise = Promise.all([
|
|
41
|
+
util.toBlobURL(`${CORE_BASE_URL}/ffmpeg-core.js`, 'text/javascript'),
|
|
42
|
+
util.toBlobURL(`${CORE_BASE_URL}/ffmpeg-core.wasm`, 'application/wasm'),
|
|
43
|
+
])
|
|
44
|
+
.then(([coreURL, wasmURL]) => ({ coreURL, wasmURL }))
|
|
45
|
+
.catch((e) => {
|
|
46
|
+
// Don't cache a failure — a flaky network shouldn't make every later
|
|
47
|
+
// compress in this session fail too.
|
|
48
|
+
corePromise = null;
|
|
49
|
+
throw e;
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
return corePromise;
|
|
53
|
+
};
|
|
11
54
|
class NativeVideoCompressorWeb extends core.WebPlugin {
|
|
12
55
|
constructor() {
|
|
13
56
|
super(...arguments);
|
|
14
57
|
this.ffmpeg = null;
|
|
15
58
|
this.isLoaded = false;
|
|
59
|
+
/** The job `compressVideo` is running right now, for `cancel({ jobId })`. */
|
|
60
|
+
this.currentJobId = null;
|
|
61
|
+
this.cancelRequested = false;
|
|
16
62
|
}
|
|
17
63
|
async loadFFmpeg() {
|
|
18
64
|
if (this.isLoaded && this.ffmpeg)
|
|
19
65
|
return;
|
|
20
66
|
this.notifyListeners('onProgress', { status: 'loading_core', percent: 0 });
|
|
67
|
+
// Built on a local, published to the field only once it is ready and still
|
|
68
|
+
// wanted. Assigning `this.ffmpeg` up front and setting `isLoaded` after the
|
|
69
|
+
// await left a window where a cancel could null the instance and the load,
|
|
70
|
+
// finishing a moment later, would set `isLoaded = true` on nothing at all —
|
|
71
|
+
// after which every compressVideo threw 'FFmpeg failed to load' for the
|
|
72
|
+
// rest of the page's life.
|
|
73
|
+
const instance = new ffmpeg.FFmpeg();
|
|
74
|
+
// Listen to FFmpeg progress and map it to capacitor plugin events
|
|
75
|
+
instance.on('progress', ({ progress }) => {
|
|
76
|
+
// progress is a fraction between 0 and 1
|
|
77
|
+
this.notifyListeners('onProgress', { status: 'progress', percent: Math.round(progress * 100) });
|
|
78
|
+
});
|
|
79
|
+
instance.on('log', ({ message }) => {
|
|
80
|
+
console.log('[FFmpeg Log]', message);
|
|
81
|
+
});
|
|
21
82
|
try {
|
|
22
|
-
this.ffmpeg = new ffmpeg.FFmpeg();
|
|
23
|
-
// Listen to FFmpeg progress and map it to capacitor plugin events
|
|
24
|
-
this.ffmpeg.on('progress', ({ progress }) => {
|
|
25
|
-
// progress is a fraction between 0 and 1
|
|
26
|
-
this.notifyListeners('onProgress', { status: 'progress', percent: Math.round(progress * 100) });
|
|
27
|
-
});
|
|
28
|
-
this.ffmpeg.on('log', ({ message }) => {
|
|
29
|
-
console.log('[FFmpeg Log]', message);
|
|
30
|
-
});
|
|
31
83
|
// Use standard single-threaded version for best compatibility across browsers
|
|
32
84
|
// without requiring complex SharedArrayBuffer headers configuration
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
await this.ffmpeg.load({
|
|
36
|
-
coreURL: await util.toBlobURL(`${baseURL}/ffmpeg-core.js`, 'text/javascript'),
|
|
37
|
-
wasmURL: await util.toBlobURL(`${baseURL}/ffmpeg-core.wasm`, 'application/wasm'),
|
|
38
|
-
});
|
|
39
|
-
this.isLoaded = true;
|
|
40
|
-
console.log('FFmpeg core loaded successfully');
|
|
85
|
+
console.log('Starting to load FFmpeg core from:', CORE_BASE_URL);
|
|
86
|
+
await instance.load(await loadCoreURLs());
|
|
41
87
|
}
|
|
42
88
|
catch (e) {
|
|
89
|
+
instance.terminate();
|
|
43
90
|
console.error('Failed to load FFmpeg:', e);
|
|
44
91
|
throw e;
|
|
45
92
|
}
|
|
93
|
+
// Cancelled while this was loading. Terminate what we just built rather
|
|
94
|
+
// than publish it — the caller asked for none of it.
|
|
95
|
+
if (this.cancelRequested) {
|
|
96
|
+
instance.terminate();
|
|
97
|
+
throw new CancelledError();
|
|
98
|
+
}
|
|
99
|
+
this.ffmpeg = instance;
|
|
100
|
+
this.isLoaded = true;
|
|
101
|
+
console.log('FFmpeg core loaded successfully');
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Kill the worker outright — there is no gentler option.
|
|
105
|
+
*
|
|
106
|
+
* `ffmpeg.exec()` runs *synchronously* inside the worker (see
|
|
107
|
+
* @ffmpeg/ffmpeg's worker.js: `ffmpeg.exec(...args)` is not awaited), so the
|
|
108
|
+
* worker never reaches a message poll while encoding. The AbortSignal that
|
|
109
|
+
* `exec()` accepts only rejects the main-thread promise; the encode keeps
|
|
110
|
+
* burning CPU behind it. `worker.terminate()` is what actually stops it.
|
|
111
|
+
*
|
|
112
|
+
* The cost is the loaded core, which is why the core URLs are cached above.
|
|
113
|
+
*/
|
|
114
|
+
async cancel(options) {
|
|
115
|
+
var _a;
|
|
116
|
+
if (!this.currentJobId)
|
|
117
|
+
return { cancelled: false };
|
|
118
|
+
if ((options === null || options === void 0 ? void 0 : options.jobId) && options.jobId !== this.currentJobId)
|
|
119
|
+
return { cancelled: false };
|
|
120
|
+
// Set even when there is no instance yet: the job may still be inside
|
|
121
|
+
// loadFFmpeg, which reads this flag before publishing what it built.
|
|
122
|
+
this.cancelRequested = true;
|
|
123
|
+
(_a = this.ffmpeg) === null || _a === void 0 ? void 0 : _a.terminate();
|
|
124
|
+
this.ffmpeg = null;
|
|
125
|
+
this.isLoaded = false;
|
|
126
|
+
return { cancelled: true };
|
|
46
127
|
}
|
|
47
128
|
async initialize() {
|
|
48
129
|
try {
|
|
@@ -57,13 +138,30 @@ class NativeVideoCompressorWeb extends core.WebPlugin {
|
|
|
57
138
|
}
|
|
58
139
|
}
|
|
59
140
|
async compressVideo(options) {
|
|
141
|
+
var _a;
|
|
142
|
+
this.currentJobId = (_a = options.jobId) !== null && _a !== void 0 ? _a : 'default';
|
|
143
|
+
this.cancelRequested = false;
|
|
144
|
+
// Checked at every stage boundary, not just around exec: terminate() only
|
|
145
|
+
// reaches work that has already got to the worker, so a cancel during the
|
|
146
|
+
// core download or the source fetch has nothing to interrupt and would
|
|
147
|
+
// otherwise let the compress carry on as if nothing had happened.
|
|
148
|
+
const throwIfCancelled = () => {
|
|
149
|
+
if (this.cancelRequested)
|
|
150
|
+
throw new CancelledError();
|
|
151
|
+
};
|
|
60
152
|
try {
|
|
61
153
|
if (!this.isLoaded) {
|
|
62
154
|
await this.loadFFmpeg();
|
|
63
155
|
}
|
|
156
|
+
throwIfCancelled();
|
|
64
157
|
if (!this.ffmpeg) {
|
|
65
158
|
throw new Error('FFmpeg failed to load');
|
|
66
159
|
}
|
|
160
|
+
// Bound once. `this.ffmpeg` is nulled by cancel(), so reading it after
|
|
161
|
+
// any of the four awaits below can hand back null on a reference the
|
|
162
|
+
// compiler narrowed at this line — surfacing a cancel as a TypeError
|
|
163
|
+
// instead of a CancelledError.
|
|
164
|
+
const ffmpeg = this.ffmpeg;
|
|
67
165
|
this.notifyListeners('onProgress', { status: 'started', percent: 0 });
|
|
68
166
|
const { sourcePath, quality = 'MEDIUM' } = options;
|
|
69
167
|
const inputFileName = 'input.mp4';
|
|
@@ -113,14 +211,18 @@ class NativeVideoCompressorWeb extends core.WebPlugin {
|
|
|
113
211
|
console.error('[FFmpeg] Error fetching source file:', err);
|
|
114
212
|
throw new Error('Could not fetch source video file');
|
|
115
213
|
}
|
|
214
|
+
throwIfCancelled();
|
|
116
215
|
console.log('[FFmpeg] Writing file to FS...', inputData.byteLength, 'bytes');
|
|
117
216
|
try {
|
|
118
|
-
await
|
|
217
|
+
await ffmpeg.writeFile(inputFileName, inputData);
|
|
119
218
|
}
|
|
120
219
|
catch (err) {
|
|
220
|
+
if (this.cancelRequested)
|
|
221
|
+
throw new CancelledError();
|
|
121
222
|
console.error('[FFmpeg] Error writing to FS:', err);
|
|
122
223
|
throw new Error('Could not write to FFmpeg FS');
|
|
123
224
|
}
|
|
225
|
+
throwIfCancelled();
|
|
124
226
|
// Construct FFmpeg command array
|
|
125
227
|
const args = ['-i', inputFileName];
|
|
126
228
|
// Video codec and compression parameters (superfast provides much better size than ultrafast)
|
|
@@ -135,25 +237,32 @@ class NativeVideoCompressorWeb extends core.WebPlugin {
|
|
|
135
237
|
args.push(outputFileName);
|
|
136
238
|
console.log('[FFmpeg] Executing command:', args.join(' '));
|
|
137
239
|
try {
|
|
138
|
-
const retCode = await
|
|
240
|
+
const retCode = await ffmpeg.exec(args);
|
|
139
241
|
console.log('[FFmpeg] Exec returned code:', retCode);
|
|
140
242
|
}
|
|
141
243
|
catch (err) {
|
|
244
|
+
// terminate() rejects every in-flight call, so a cancel arrives here
|
|
245
|
+
// looking like a generic failure unless we name it.
|
|
246
|
+
if (this.cancelRequested)
|
|
247
|
+
throw new CancelledError();
|
|
142
248
|
console.error('[FFmpeg] Error executing command:', err);
|
|
143
249
|
throw new Error('FFmpeg execution failed');
|
|
144
250
|
}
|
|
251
|
+
throwIfCancelled();
|
|
145
252
|
console.log('[FFmpeg] Reading output file...');
|
|
146
253
|
let data;
|
|
147
254
|
try {
|
|
148
|
-
data = await
|
|
255
|
+
data = await ffmpeg.readFile(outputFileName);
|
|
149
256
|
}
|
|
150
257
|
catch (err) {
|
|
258
|
+
if (this.cancelRequested)
|
|
259
|
+
throw new CancelledError();
|
|
151
260
|
console.error('[FFmpeg] Error reading output file:', err);
|
|
152
261
|
throw new Error('Could not read FFmpeg output');
|
|
153
262
|
}
|
|
154
263
|
// Free memory
|
|
155
|
-
await
|
|
156
|
-
await
|
|
264
|
+
await ffmpeg.deleteFile(inputFileName);
|
|
265
|
+
await ffmpeg.deleteFile(outputFileName);
|
|
157
266
|
console.log('[FFmpeg] Creating Blob URL from output...', data.byteLength, 'bytes');
|
|
158
267
|
// Create a blob URL for the resulting file
|
|
159
268
|
const blob = new Blob([data], { type: 'video/mp4' });
|
|
@@ -165,9 +274,18 @@ class NativeVideoCompressorWeb extends core.WebPlugin {
|
|
|
165
274
|
};
|
|
166
275
|
}
|
|
167
276
|
catch (error) {
|
|
168
|
-
|
|
277
|
+
if ((error === null || error === void 0 ? void 0 : error.code) === CANCELLED) {
|
|
278
|
+
console.log('[FFmpeg] Compression cancelled');
|
|
279
|
+
}
|
|
280
|
+
else {
|
|
281
|
+
console.error('[FFmpeg] Compression process failed:', error);
|
|
282
|
+
}
|
|
169
283
|
throw error;
|
|
170
284
|
}
|
|
285
|
+
finally {
|
|
286
|
+
this.currentJobId = null;
|
|
287
|
+
this.cancelRequested = false;
|
|
288
|
+
}
|
|
171
289
|
}
|
|
172
290
|
}
|
|
173
291
|
|
|
@@ -176,5 +294,6 @@ var web = /*#__PURE__*/Object.freeze({
|
|
|
176
294
|
NativeVideoCompressorWeb: NativeVideoCompressorWeb
|
|
177
295
|
});
|
|
178
296
|
|
|
297
|
+
exports.CANCELLED = CANCELLED;
|
|
179
298
|
exports.NativeVideoCompressor = NativeVideoCompressor;
|
|
180
299
|
//# sourceMappingURL=plugin.cjs.js.map
|
package/dist/plugin.cjs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.cjs.js","sources":["esm/index.js","esm/web.js"],"sourcesContent":["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 { fetchFile, toBlobURL } from '@ffmpeg/util';\nexport class NativeVideoCompressorWeb extends WebPlugin {\n constructor() {\n super(...arguments);\n this.ffmpeg = null;\n this.isLoaded = false;\n }\n async loadFFmpeg() {\n if (this.isLoaded && this.ffmpeg)\n return;\n this.notifyListeners('onProgress', { status: 'loading_core', percent: 0 });\n try {\n this.ffmpeg = new FFmpeg();\n // Listen to FFmpeg progress and map it to capacitor plugin events\n this.ffmpeg.on('progress', ({ progress }) => {\n // progress is a fraction between 0 and 1\n this.notifyListeners('onProgress', { status: 'progress', percent: Math.round(progress * 100) });\n });\n this.ffmpeg.on('log', ({ message }) => {\n console.log('[FFmpeg Log]', message);\n });\n // Use standard single-threaded version for best compatibility across browsers\n // without requiring complex SharedArrayBuffer headers configuration\n const baseURL = 'https://unpkg.com/@ffmpeg/core@0.12.6/dist/esm';\n console.log('Starting to load FFmpeg core from:', baseURL);\n await this.ffmpeg.load({\n coreURL: await toBlobURL(`${baseURL}/ffmpeg-core.js`, 'text/javascript'),\n wasmURL: await toBlobURL(`${baseURL}/ffmpeg-core.wasm`, 'application/wasm'),\n });\n this.isLoaded = true;\n console.log('FFmpeg core loaded successfully');\n }\n catch (e) {\n console.error('Failed to load FFmpeg:', e);\n throw e;\n }\n }\n async initialize() {\n try {\n if (this.isLoaded) {\n return { success: true, message: 'Already initialized' };\n }\n await this.loadFFmpeg();\n return { success: true, message: 'Initialization successful' };\n }\n catch (e) {\n return { success: false, message: e.message || 'Initialization failed' };\n }\n }\n async compressVideo(options) {\n try {\n if (!this.isLoaded) {\n await this.loadFFmpeg();\n }\n if (!this.ffmpeg) {\n throw new Error('FFmpeg failed to load');\n }\n this.notifyListeners('onProgress', { status: 'started', percent: 0 });\n const { sourcePath, quality = 'MEDIUM' } = options;\n const inputFileName = 'input.mp4';\n const outputFileName = 'output.mp4';\n // Map quality to CRF, Scale, and Audio Bitrate\n let crf = '28';\n let scale = ''; // default keep resolution\n let audioBitrate = '128k';\n switch (quality) {\n case 'VERY_HIGH': // 1080p\n crf = '23';\n scale = '-vf scale=-2:1080';\n audioBitrate = '128k';\n break;\n case 'HIGH': // 720p\n crf = '28';\n scale = '-vf scale=-2:720';\n audioBitrate = '128k';\n break;\n case 'MEDIUM': // 540p\n crf = '30';\n scale = '-vf scale=-2:540';\n audioBitrate = '96k';\n break;\n case 'LOW': // 480p\n crf = '32';\n scale = '-vf scale=-2:480';\n audioBitrate = '96k';\n break;\n case '360P': // 360p\n crf = '28';\n scale = '-vf scale=-2:360';\n audioBitrate = '64k';\n break;\n case 'VERY_LOW': // 240p\n crf = '35';\n scale = '-vf scale=-2:240';\n audioBitrate = '48k';\n break;\n }\n console.log('[FFmpeg] Fetching source file...', sourcePath);\n let inputData;\n try {\n inputData = await fetchFile(sourcePath);\n }\n catch (err) {\n console.error('[FFmpeg] Error fetching source file:', err);\n throw new Error('Could not fetch source video file');\n }\n console.log('[FFmpeg] Writing file to FS...', inputData.byteLength, 'bytes');\n try {\n await this.ffmpeg.writeFile(inputFileName, inputData);\n }\n catch (err) {\n console.error('[FFmpeg] Error writing to FS:', err);\n throw new Error('Could not write to FFmpeg FS');\n }\n // Construct FFmpeg command array\n const args = ['-i', inputFileName];\n // Video codec and compression parameters (superfast provides much better size than ultrafast)\n args.push('-c:v', 'libx264', '-preset', 'superfast', '-crf', crf);\n // Resize if needed (split by space)\n if (scale) {\n args.push(...scale.split(' '));\n }\n // Audio re-encoding to save space\n args.push('-c:a', 'aac', '-b:a', audioBitrate);\n // Output\n args.push(outputFileName);\n console.log('[FFmpeg] Executing command:', args.join(' '));\n try {\n const retCode = await this.ffmpeg.exec(args);\n console.log('[FFmpeg] Exec returned code:', retCode);\n }\n catch (err) {\n console.error('[FFmpeg] Error executing command:', err);\n throw new Error('FFmpeg execution failed');\n }\n console.log('[FFmpeg] Reading output file...');\n let data;\n try {\n data = await this.ffmpeg.readFile(outputFileName);\n }\n catch (err) {\n console.error('[FFmpeg] Error reading output file:', err);\n throw new Error('Could not read FFmpeg output');\n }\n // Free memory\n await this.ffmpeg.deleteFile(inputFileName);\n await this.ffmpeg.deleteFile(outputFileName);\n console.log('[FFmpeg] Creating Blob URL from output...', data.byteLength, 'bytes');\n // Create a blob URL for the resulting file\n const blob = new Blob([data], { type: 'video/mp4' });\n const destPath = URL.createObjectURL(blob);\n console.log('[FFmpeg] Compression complete, returning path:', destPath);\n return {\n success: true,\n destPath: destPath,\n };\n }\n catch (error) {\n console.error('[FFmpeg] Compression process failed:', error);\n throw error;\n }\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","WebPlugin","FFmpeg","toBlobURL","fetchFile"],"mappings":";;;;;;AACK,MAAC,qBAAqB,GAAGA,mBAAc,CAAC,uBAAuB,EAAE;AACtE,IAAI,GAAG,EAAE,MAAM,mDAAe,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,wBAAwB,EAAE,CAAC;AAC5E,CAAC;;ACAM,MAAM,wBAAwB,SAASC,cAAS,CAAC;AACxD,IAAI,WAAW,GAAG;AAClB,QAAQ,KAAK,CAAC,GAAG,SAAS,CAAC;AAC3B,QAAQ,IAAI,CAAC,MAAM,GAAG,IAAI;AAC1B,QAAQ,IAAI,CAAC,QAAQ,GAAG,KAAK;AAC7B,IAAI;AACJ,IAAI,MAAM,UAAU,GAAG;AACvB,QAAQ,IAAI,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,MAAM;AACxC,YAAY;AACZ,QAAQ,IAAI,CAAC,eAAe,CAAC,YAAY,EAAE,EAAE,MAAM,EAAE,cAAc,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;AAClF,QAAQ,IAAI;AACZ,YAAY,IAAI,CAAC,MAAM,GAAG,IAAIC,aAAM,EAAE;AACtC;AACA,YAAY,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,UAAU,EAAE,CAAC,EAAE,QAAQ,EAAE,KAAK;AACzD;AACA,gBAAgB,IAAI,CAAC,eAAe,CAAC,YAAY,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,GAAG,CAAC,EAAE,CAAC;AAC/G,YAAY,CAAC,CAAC;AACd,YAAY,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,KAAK;AACnD,gBAAgB,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,OAAO,CAAC;AACpD,YAAY,CAAC,CAAC;AACd;AACA;AACA,YAAY,MAAM,OAAO,GAAG,gDAAgD;AAC5E,YAAY,OAAO,CAAC,GAAG,CAAC,oCAAoC,EAAE,OAAO,CAAC;AACtE,YAAY,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;AACnC,gBAAgB,OAAO,EAAE,MAAMC,cAAS,CAAC,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,EAAE,iBAAiB,CAAC;AACxF,gBAAgB,OAAO,EAAE,MAAMA,cAAS,CAAC,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,EAAE,kBAAkB,CAAC;AAC3F,aAAa,CAAC;AACd,YAAY,IAAI,CAAC,QAAQ,GAAG,IAAI;AAChC,YAAY,OAAO,CAAC,GAAG,CAAC,iCAAiC,CAAC;AAC1D,QAAQ;AACR,QAAQ,OAAO,CAAC,EAAE;AAClB,YAAY,OAAO,CAAC,KAAK,CAAC,wBAAwB,EAAE,CAAC,CAAC;AACtD,YAAY,MAAM,CAAC;AACnB,QAAQ;AACR,IAAI;AACJ,IAAI,MAAM,UAAU,GAAG;AACvB,QAAQ,IAAI;AACZ,YAAY,IAAI,IAAI,CAAC,QAAQ,EAAE;AAC/B,gBAAgB,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,qBAAqB,EAAE;AACxE,YAAY;AACZ,YAAY,MAAM,IAAI,CAAC,UAAU,EAAE;AACnC,YAAY,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,2BAA2B,EAAE;AAC1E,QAAQ;AACR,QAAQ,OAAO,CAAC,EAAE;AAClB,YAAY,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,IAAI,uBAAuB,EAAE;AACpF,QAAQ;AACR,IAAI;AACJ,IAAI,MAAM,aAAa,CAAC,OAAO,EAAE;AACjC,QAAQ,IAAI;AACZ,YAAY,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE;AAChC,gBAAgB,MAAM,IAAI,CAAC,UAAU,EAAE;AACvC,YAAY;AACZ,YAAY,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE;AAC9B,gBAAgB,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC;AACxD,YAAY;AACZ,YAAY,IAAI,CAAC,eAAe,CAAC,YAAY,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;AACjF,YAAY,MAAM,EAAE,UAAU,EAAE,OAAO,GAAG,QAAQ,EAAE,GAAG,OAAO;AAC9D,YAAY,MAAM,aAAa,GAAG,WAAW;AAC7C,YAAY,MAAM,cAAc,GAAG,YAAY;AAC/C;AACA,YAAY,IAAI,GAAG,GAAG,IAAI;AAC1B,YAAY,IAAI,KAAK,GAAG,EAAE,CAAC;AAC3B,YAAY,IAAI,YAAY,GAAG,MAAM;AACrC,YAAY,QAAQ,OAAO;AAC3B,gBAAgB,KAAK,WAAW;AAChC,oBAAoB,GAAG,GAAG,IAAI;AAC9B,oBAAoB,KAAK,GAAG,mBAAmB;AAC/C,oBAAoB,YAAY,GAAG,MAAM;AACzC,oBAAoB;AACpB,gBAAgB,KAAK,MAAM;AAC3B,oBAAoB,GAAG,GAAG,IAAI;AAC9B,oBAAoB,KAAK,GAAG,kBAAkB;AAC9C,oBAAoB,YAAY,GAAG,MAAM;AACzC,oBAAoB;AACpB,gBAAgB,KAAK,QAAQ;AAC7B,oBAAoB,GAAG,GAAG,IAAI;AAC9B,oBAAoB,KAAK,GAAG,kBAAkB;AAC9C,oBAAoB,YAAY,GAAG,KAAK;AACxC,oBAAoB;AACpB,gBAAgB,KAAK,KAAK;AAC1B,oBAAoB,GAAG,GAAG,IAAI;AAC9B,oBAAoB,KAAK,GAAG,kBAAkB;AAC9C,oBAAoB,YAAY,GAAG,KAAK;AACxC,oBAAoB;AACpB,gBAAgB,KAAK,MAAM;AAC3B,oBAAoB,GAAG,GAAG,IAAI;AAC9B,oBAAoB,KAAK,GAAG,kBAAkB;AAC9C,oBAAoB,YAAY,GAAG,KAAK;AACxC,oBAAoB;AACpB,gBAAgB,KAAK,UAAU;AAC/B,oBAAoB,GAAG,GAAG,IAAI;AAC9B,oBAAoB,KAAK,GAAG,kBAAkB;AAC9C,oBAAoB,YAAY,GAAG,KAAK;AACxC,oBAAoB;AACpB;AACA,YAAY,OAAO,CAAC,GAAG,CAAC,kCAAkC,EAAE,UAAU,CAAC;AACvE,YAAY,IAAI,SAAS;AACzB,YAAY,IAAI;AAChB,gBAAgB,SAAS,GAAG,MAAMC,cAAS,CAAC,UAAU,CAAC;AACvD,YAAY;AACZ,YAAY,OAAO,GAAG,EAAE;AACxB,gBAAgB,OAAO,CAAC,KAAK,CAAC,sCAAsC,EAAE,GAAG,CAAC;AAC1E,gBAAgB,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC;AACpE,YAAY;AACZ,YAAY,OAAO,CAAC,GAAG,CAAC,gCAAgC,EAAE,SAAS,CAAC,UAAU,EAAE,OAAO,CAAC;AACxF,YAAY,IAAI;AAChB,gBAAgB,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,aAAa,EAAE,SAAS,CAAC;AACrE,YAAY;AACZ,YAAY,OAAO,GAAG,EAAE;AACxB,gBAAgB,OAAO,CAAC,KAAK,CAAC,+BAA+B,EAAE,GAAG,CAAC;AACnE,gBAAgB,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC;AAC/D,YAAY;AACZ;AACA,YAAY,MAAM,IAAI,GAAG,CAAC,IAAI,EAAE,aAAa,CAAC;AAC9C;AACA,YAAY,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,CAAC;AAC7E;AACA,YAAY,IAAI,KAAK,EAAE;AACvB,gBAAgB,IAAI,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;AAC9C,YAAY;AACZ;AACA,YAAY,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,CAAC;AAC1D;AACA,YAAY,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC;AACrC,YAAY,OAAO,CAAC,GAAG,CAAC,6BAA6B,EAAE,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACtE,YAAY,IAAI;AAChB,gBAAgB,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;AAC5D,gBAAgB,OAAO,CAAC,GAAG,CAAC,8BAA8B,EAAE,OAAO,CAAC;AACpE,YAAY;AACZ,YAAY,OAAO,GAAG,EAAE;AACxB,gBAAgB,OAAO,CAAC,KAAK,CAAC,mCAAmC,EAAE,GAAG,CAAC;AACvE,gBAAgB,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC;AAC1D,YAAY;AACZ,YAAY,OAAO,CAAC,GAAG,CAAC,iCAAiC,CAAC;AAC1D,YAAY,IAAI,IAAI;AACpB,YAAY,IAAI;AAChB,gBAAgB,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC;AACjE,YAAY;AACZ,YAAY,OAAO,GAAG,EAAE;AACxB,gBAAgB,OAAO,CAAC,KAAK,CAAC,qCAAqC,EAAE,GAAG,CAAC;AACzE,gBAAgB,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC;AAC/D,YAAY;AACZ;AACA,YAAY,MAAM,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,aAAa,CAAC;AACvD,YAAY,MAAM,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,cAAc,CAAC;AACxD,YAAY,OAAO,CAAC,GAAG,CAAC,2CAA2C,EAAE,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC;AAC9F;AACA,YAAY,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;AAChE,YAAY,MAAM,QAAQ,GAAG,GAAG,CAAC,eAAe,CAAC,IAAI,CAAC;AACtD,YAAY,OAAO,CAAC,GAAG,CAAC,gDAAgD,EAAE,QAAQ,CAAC;AACnF,YAAY,OAAO;AACnB,gBAAgB,OAAO,EAAE,IAAI;AAC7B,gBAAgB,QAAQ,EAAE,QAAQ;AAClC,aAAa;AACb,QAAQ;AACR,QAAQ,OAAO,KAAK,EAAE;AACtB,YAAY,OAAO,CAAC,KAAK,CAAC,sCAAsC,EAAE,KAAK,CAAC;AACxE,YAAY,MAAM,KAAK;AACvB,QAAQ;AACR,IAAI;AACJ;;;;;;;;;"}
|
|
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 { fetchFile, toBlobURL } from '@ffmpeg/util';\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.\n */\nclass CancelledError extends Error {\n constructor() {\n super('Đã hủy nén');\n this.code = CANCELLED;\n this.name = 'CancelledError';\n }\n}\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.\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 job `compressVideo` is running right now, for `cancel({ jobId })`. */\n this.currentJobId = null;\n this.cancelRequested = false;\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 // Listen to FFmpeg progress and map it to capacitor plugin events\n instance.on('progress', ({ progress }) => {\n // progress is a fraction between 0 and 1\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 // Use standard single-threaded version for best compatibility across browsers\n // without requiring complex SharedArrayBuffer headers configuration\n console.log('Starting to load FFmpeg core from:', CORE_BASE_URL);\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 console.log('FFmpeg core loaded successfully');\n }\n /**\n * Kill the worker outright — there is no gentler option.\n *\n * `ffmpeg.exec()` runs *synchronously* inside the worker (see\n * @ffmpeg/ffmpeg's worker.js: `ffmpeg.exec(...args)` is not awaited), so the\n * worker never reaches a message poll while encoding. The AbortSignal that\n * `exec()` accepts only rejects the main-thread promise; the encode keeps\n * burning CPU behind it. `worker.terminate()` is what actually stops it.\n *\n * The cost is the loaded core, which is why the core URLs are 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 there is no instance yet: the job may still be inside\n // loadFFmpeg, which reads this flag before publishing what it built.\n this.cancelRequested = true;\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 initialize() {\n try {\n if (this.isLoaded) {\n return { success: true, message: 'Already initialized' };\n }\n await this.loadFFmpeg();\n return { success: true, message: 'Initialization successful' };\n }\n catch (e) {\n return { success: false, message: e.message || 'Initialization failed' };\n }\n }\n async compressVideo(options) {\n var _a;\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 exec: terminate() only\n // reaches work that has already got to the worker, so a cancel during the\n // core download or the source fetch has nothing to interrupt and would\n // otherwise 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 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\n // any of the four awaits below can hand back null on a reference the\n // compiler narrowed at this line — surfacing a cancel as a TypeError\n // instead of a CancelledError.\n const ffmpeg = this.ffmpeg;\n this.notifyListeners('onProgress', { status: 'started', percent: 0 });\n const { sourcePath, quality = 'MEDIUM' } = options;\n const inputFileName = 'input.mp4';\n const outputFileName = 'output.mp4';\n // Map quality to CRF, Scale, and Audio Bitrate\n let crf = '28';\n let scale = ''; // default keep resolution\n let audioBitrate = '128k';\n switch (quality) {\n case 'VERY_HIGH': // 1080p\n crf = '23';\n scale = '-vf scale=-2:1080';\n audioBitrate = '128k';\n break;\n case 'HIGH': // 720p\n crf = '28';\n scale = '-vf scale=-2:720';\n audioBitrate = '128k';\n break;\n case 'MEDIUM': // 540p\n crf = '30';\n scale = '-vf scale=-2:540';\n audioBitrate = '96k';\n break;\n case 'LOW': // 480p\n crf = '32';\n scale = '-vf scale=-2:480';\n audioBitrate = '96k';\n break;\n case '360P': // 360p\n crf = '28';\n scale = '-vf scale=-2:360';\n audioBitrate = '64k';\n break;\n case 'VERY_LOW': // 240p\n crf = '35';\n scale = '-vf scale=-2:240';\n audioBitrate = '48k';\n break;\n }\n console.log('[FFmpeg] Fetching source file...', sourcePath);\n let inputData;\n try {\n inputData = await fetchFile(sourcePath);\n }\n catch (err) {\n console.error('[FFmpeg] Error fetching source file:', err);\n throw new Error('Could not fetch source video file');\n }\n throwIfCancelled();\n console.log('[FFmpeg] Writing file to FS...', inputData.byteLength, 'bytes');\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 // Construct FFmpeg command array\n const args = ['-i', inputFileName];\n // Video codec and compression parameters (superfast provides much better size than ultrafast)\n args.push('-c:v', 'libx264', '-preset', 'superfast', '-crf', crf);\n // Resize if needed (split by space)\n if (scale) {\n args.push(...scale.split(' '));\n }\n // Audio re-encoding to save space\n args.push('-c:a', 'aac', '-b:a', audioBitrate);\n // Output\n args.push(outputFileName);\n console.log('[FFmpeg] Executing command:', args.join(' '));\n try {\n const retCode = await ffmpeg.exec(args);\n console.log('[FFmpeg] Exec returned code:', retCode);\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 console.log('[FFmpeg] Reading output file...');\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 // Free memory\n await ffmpeg.deleteFile(inputFileName);\n await ffmpeg.deleteFile(outputFileName);\n console.log('[FFmpeg] Creating Blob URL from output...', data.byteLength, 'bytes');\n // Create a blob URL for the resulting file\n const blob = new Blob([data], { type: 'video/mp4' });\n const destPath = URL.createObjectURL(blob);\n console.log('[FFmpeg] Compression complete, returning path:', destPath);\n return {\n success: true,\n destPath: destPath,\n };\n }\n catch (error) {\n if ((error === null || error === void 0 ? void 0 : error.code) === CANCELLED) {\n console.log('[FFmpeg] Compression cancelled');\n }\n else {\n console.error('[FFmpeg] Compression process failed:', error);\n }\n throw error;\n }\n finally {\n this.currentJobId = null;\n this.cancelRequested = false;\n }\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","toBlobURL","WebPlugin","FFmpeg","fetchFile"],"mappings":"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"}
|