@honem/native-video-compressor 0.1.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.
@@ -0,0 +1,488 @@
1
+ package com.honem.nativevideocompressor;
2
+
3
+ import android.content.Context;
4
+ import android.media.MediaCodec;
5
+ import android.media.MediaCodecInfo;
6
+ import android.media.MediaExtractor;
7
+ import android.media.MediaFormat;
8
+ import android.media.MediaMuxer;
9
+ import android.net.Uri;
10
+ import android.os.Build;
11
+ import android.util.Log;
12
+
13
+ import java.io.File;
14
+ import java.io.FileInputStream;
15
+ import java.io.IOException;
16
+ import java.nio.ByteBuffer;
17
+ import java.util.concurrent.atomic.AtomicInteger;
18
+
19
+ class CompressionResult {
20
+ String outputPath;
21
+ long originalSize;
22
+ long compressedSize;
23
+ }
24
+
25
+ interface CompressionCallback {
26
+ void onComplete(CompressionResult result, String error);
27
+ }
28
+
29
+ public class VideoCompressor {
30
+ private static final String TAG = "VideoCompressor";
31
+ private static final int TIMEOUT_USEC = 10000;
32
+ private static final String COMPRESSED_PREFIX = "compressed_";
33
+
34
+ public void compressVideo(
35
+ Context context,
36
+ String inputPath,
37
+ String outputPath,
38
+ String quality,
39
+ Integer bitrate,
40
+ Integer width,
41
+ Integer height,
42
+ String format,
43
+ CompressionCallback callback
44
+ ) {
45
+ try {
46
+ File inputFile = resolveFilePath(context, inputPath);
47
+ if (inputFile == null || !inputFile.exists()) {
48
+ callback.onComplete(null, "Input file not found: " + inputPath);
49
+ return;
50
+ }
51
+
52
+ long originalSize = inputFile.length();
53
+
54
+ String outputFormat;
55
+ if (format != null && !format.isEmpty()) {
56
+ outputFormat = format;
57
+ } else {
58
+ String inputName = inputFile.getName();
59
+ int lastDot = inputName.lastIndexOf('.');
60
+ if (lastDot > 0 && lastDot < inputName.length() - 1) {
61
+ String inputExtension = inputName.substring(lastDot + 1).toLowerCase();
62
+ switch (inputExtension) {
63
+ case "mov":
64
+ outputFormat = "mov";
65
+ break;
66
+ case "m4v":
67
+ outputFormat = "m4v";
68
+ break;
69
+ case "3gp":
70
+ outputFormat = "3gp";
71
+ break;
72
+ case "webm":
73
+ outputFormat = "webm";
74
+ break;
75
+ case "mp4":
76
+ default:
77
+ outputFormat = "mp4";
78
+ break;
79
+ }
80
+ } else {
81
+ outputFormat = "mp4";
82
+ }
83
+ }
84
+
85
+ File outputFile;
86
+ if (outputPath != null) {
87
+ outputFile = resolveFilePath(context, outputPath);
88
+ } else {
89
+ String extension = getFileExtension(outputFormat);
90
+ String uniqueName = COMPRESSED_PREFIX + System.currentTimeMillis() + "." + extension;
91
+ File cacheDir = context.getCacheDir();
92
+ outputFile = new File(cacheDir, uniqueName);
93
+ }
94
+
95
+ MediaExtractor extractor = new MediaExtractor();
96
+ try {
97
+ extractor.setDataSource(inputFile.getAbsolutePath());
98
+
99
+ int videoTrackIndex = -1;
100
+ int audioTrackIndex = -1;
101
+ MediaFormat videoFormat = null;
102
+ MediaFormat audioFormat = null;
103
+
104
+ for (int i = 0; i < extractor.getTrackCount(); i++) {
105
+ MediaFormat mediaFormat = extractor.getTrackFormat(i);
106
+ String mime = mediaFormat.getString(MediaFormat.KEY_MIME);
107
+ if (mime.startsWith("video/") && videoTrackIndex == -1) {
108
+ videoTrackIndex = i;
109
+ videoFormat = mediaFormat;
110
+ } else if (mime.startsWith("audio/") && audioTrackIndex == -1) {
111
+ audioTrackIndex = i;
112
+ audioFormat = mediaFormat;
113
+ }
114
+ }
115
+
116
+ if (videoTrackIndex == -1) {
117
+ callback.onComplete(null, "No video track found in input file");
118
+ return;
119
+ }
120
+
121
+ // MediaFormat width/height may need to be swapped if rotation is present
122
+ int originalWidth = videoFormat.getInteger(MediaFormat.KEY_WIDTH);
123
+ int originalHeight = videoFormat.getInteger(MediaFormat.KEY_HEIGHT);
124
+
125
+ int rotation = 0;
126
+ if (videoFormat.containsKey(MediaFormat.KEY_ROTATION)) {
127
+ rotation = videoFormat.getInteger(MediaFormat.KEY_ROTATION);
128
+ }
129
+
130
+ if (rotation == 90 || rotation == 270) {
131
+ int temp = originalWidth;
132
+ originalWidth = originalHeight;
133
+ originalHeight = temp;
134
+ }
135
+
136
+ double originalAspectRatio = (double) originalWidth / originalHeight;
137
+
138
+ int[] targetDimensions = calculateTargetDimensions(
139
+ originalWidth,
140
+ originalHeight,
141
+ originalAspectRatio,
142
+ quality,
143
+ bitrate,
144
+ width,
145
+ height
146
+ );
147
+
148
+ int targetWidth = targetDimensions[0];
149
+ int targetHeight = targetDimensions[1];
150
+ int targetBitrate = targetDimensions[2];
151
+
152
+ int muxerFormat = getMuxerFormat(outputFormat);
153
+ MediaMuxer muxer = new MediaMuxer(outputFile.getAbsolutePath(), muxerFormat);
154
+
155
+ String videoMime = "video/avc"; // H.264
156
+ MediaFormat outputVideoFormat = MediaFormat.createVideoFormat(videoMime, targetWidth, targetHeight);
157
+ outputVideoFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
158
+ outputVideoFormat.setInteger(MediaFormat.KEY_BIT_RATE, targetBitrate);
159
+ outputVideoFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 30);
160
+ outputVideoFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);
161
+
162
+ MediaCodec videoEncoder = MediaCodec.createEncoderByType(videoMime);
163
+ videoEncoder.configure(outputVideoFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
164
+
165
+ MediaCodec audioEncoder = null;
166
+ int audioOutputTrackIndex = -1;
167
+ if (audioTrackIndex != -1 && audioFormat != null) {
168
+ String audioMime = audioFormat.getString(MediaFormat.KEY_MIME);
169
+ int sampleRate = audioFormat.containsKey(MediaFormat.KEY_SAMPLE_RATE)
170
+ ? audioFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE) : 44100;
171
+ int channelCount = audioFormat.containsKey(MediaFormat.KEY_CHANNEL_COUNT)
172
+ ? audioFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT) : 2;
173
+
174
+ MediaFormat outputAudioFormat = MediaFormat.createAudioFormat(audioMime, sampleRate, channelCount);
175
+ outputAudioFormat.setInteger(MediaFormat.KEY_BIT_RATE, 128_000);
176
+ outputAudioFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);
177
+
178
+ audioEncoder = MediaCodec.createEncoderByType(audioMime);
179
+ audioEncoder.configure(outputAudioFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
180
+ }
181
+
182
+ videoEncoder.start();
183
+ if (audioEncoder != null) {
184
+ audioEncoder.start();
185
+ }
186
+
187
+ extractor.selectTrack(videoTrackIndex);
188
+ extractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
189
+
190
+ boolean videoInputEOS = false;
191
+ boolean videoOutputEOS = false;
192
+ int videoOutputTrackIndex = -1;
193
+ long videoStartTime = -1;
194
+
195
+ while (!videoOutputEOS) {
196
+ if (!videoInputEOS) {
197
+ int inputBufferIndex = videoEncoder.dequeueInputBuffer(TIMEOUT_USEC);
198
+ if (inputBufferIndex >= 0) {
199
+ ByteBuffer inputBuffer = videoEncoder.getInputBuffer(inputBufferIndex);
200
+ if (inputBuffer != null) {
201
+ int sampleSize = extractor.readSampleData(inputBuffer, 0);
202
+ long presentationTimeUs = extractor.getSampleTime();
203
+
204
+ if (sampleSize < 0) {
205
+ videoEncoder.queueInputBuffer(
206
+ inputBufferIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM
207
+ );
208
+ videoInputEOS = true;
209
+ } else {
210
+ videoEncoder.queueInputBuffer(
211
+ inputBufferIndex, 0, sampleSize, presentationTimeUs, 0
212
+ );
213
+ extractor.advance();
214
+ }
215
+ }
216
+ }
217
+ }
218
+
219
+ MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
220
+ int outputBufferIndex = videoEncoder.dequeueOutputBuffer(bufferInfo, TIMEOUT_USEC);
221
+
222
+ if (outputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {
223
+ // No output available yet
224
+ } else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
225
+ MediaFormat newFormat = videoEncoder.getOutputFormat();
226
+ videoOutputTrackIndex = muxer.addTrack(newFormat);
227
+ muxer.start();
228
+ } else if (outputBufferIndex >= 0) {
229
+ ByteBuffer outputBuffer = videoEncoder.getOutputBuffer(outputBufferIndex);
230
+
231
+ if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
232
+ bufferInfo.size = 0;
233
+ }
234
+
235
+ if (bufferInfo.size != 0) {
236
+ if (videoStartTime == -1) {
237
+ videoStartTime = bufferInfo.presentationTimeUs;
238
+ }
239
+ bufferInfo.presentationTimeUs -= videoStartTime;
240
+
241
+ outputBuffer.position(bufferInfo.offset);
242
+ outputBuffer.limit(bufferInfo.offset + bufferInfo.size);
243
+ muxer.writeSampleData(videoOutputTrackIndex, outputBuffer, bufferInfo);
244
+ }
245
+
246
+ videoEncoder.releaseOutputBuffer(outputBufferIndex, false);
247
+
248
+ if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
249
+ videoOutputEOS = true;
250
+ }
251
+ }
252
+ }
253
+
254
+ if (audioTrackIndex != -1 && audioEncoder != null) {
255
+ extractor.selectTrack(audioTrackIndex);
256
+ extractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
257
+
258
+ boolean audioInputEOS = false;
259
+ boolean audioOutputEOS = false;
260
+ long audioStartTime = -1;
261
+
262
+ while (!audioOutputEOS) {
263
+ if (!audioInputEOS) {
264
+ int inputBufferIndex = audioEncoder.dequeueInputBuffer(TIMEOUT_USEC);
265
+ if (inputBufferIndex >= 0) {
266
+ ByteBuffer inputBuffer = audioEncoder.getInputBuffer(inputBufferIndex);
267
+ if (inputBuffer != null) {
268
+ int sampleSize = extractor.readSampleData(inputBuffer, 0);
269
+ long presentationTimeUs = extractor.getSampleTime();
270
+
271
+ if (sampleSize < 0) {
272
+ audioEncoder.queueInputBuffer(
273
+ inputBufferIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM
274
+ );
275
+ audioInputEOS = true;
276
+ } else {
277
+ audioEncoder.queueInputBuffer(
278
+ inputBufferIndex, 0, sampleSize, presentationTimeUs, 0
279
+ );
280
+ extractor.advance();
281
+ }
282
+ }
283
+ }
284
+ }
285
+
286
+ MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
287
+ int outputBufferIndex = audioEncoder.dequeueOutputBuffer(bufferInfo, TIMEOUT_USEC);
288
+
289
+ if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
290
+ MediaFormat newFormat = audioEncoder.getOutputFormat();
291
+ audioOutputTrackIndex = muxer.addTrack(newFormat);
292
+ } else if (outputBufferIndex >= 0) {
293
+ ByteBuffer outputBuffer = audioEncoder.getOutputBuffer(outputBufferIndex);
294
+
295
+ if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
296
+ bufferInfo.size = 0;
297
+ }
298
+
299
+ if (bufferInfo.size != 0 && audioOutputTrackIndex >= 0) {
300
+ if (audioStartTime == -1) {
301
+ audioStartTime = bufferInfo.presentationTimeUs;
302
+ }
303
+ bufferInfo.presentationTimeUs -= audioStartTime;
304
+
305
+ outputBuffer.position(bufferInfo.offset);
306
+ outputBuffer.limit(bufferInfo.offset + bufferInfo.size);
307
+ muxer.writeSampleData(audioOutputTrackIndex, outputBuffer, bufferInfo);
308
+ }
309
+
310
+ audioEncoder.releaseOutputBuffer(outputBufferIndex, false);
311
+
312
+ if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
313
+ audioOutputEOS = true;
314
+ }
315
+ }
316
+ }
317
+ }
318
+
319
+ videoEncoder.stop();
320
+ videoEncoder.release();
321
+ if (audioEncoder != null) {
322
+ audioEncoder.stop();
323
+ audioEncoder.release();
324
+ }
325
+ muxer.stop();
326
+ muxer.release();
327
+ extractor.release();
328
+
329
+ long compressedSize = outputFile.length();
330
+
331
+ CompressionResult result = new CompressionResult();
332
+ result.outputPath = outputFile.getAbsolutePath();
333
+ result.originalSize = originalSize;
334
+ result.compressedSize = compressedSize;
335
+
336
+ callback.onComplete(result, null);
337
+
338
+ } catch (Exception e) {
339
+ Log.e(TAG, "Compression failed", e);
340
+ callback.onComplete(null, "Compression failed: " + e.getMessage());
341
+ } finally {
342
+ extractor.release();
343
+ }
344
+
345
+ } catch (Exception e) {
346
+ Log.e(TAG, "Error during compression", e);
347
+ callback.onComplete(null, "Error: " + e.getMessage());
348
+ }
349
+ }
350
+
351
+ public boolean deleteFile(Context context, String path) {
352
+ try {
353
+ File file = resolveFilePath(context, path);
354
+ if (file != null && file.exists()) {
355
+ return file.delete();
356
+ }
357
+ return false;
358
+ } catch (Exception e) {
359
+ Log.e(TAG, "Failed to delete file: " + path, e);
360
+ return false;
361
+ }
362
+ }
363
+
364
+ private File resolveFilePath(Context context, String path) {
365
+ if (path.startsWith("file://")) {
366
+ return new File(Uri.parse(path).getPath());
367
+ }
368
+
369
+ if (path.startsWith("/")) {
370
+ return new File(path);
371
+ }
372
+
373
+ // Capacitor file paths: capacitor://localhost/_capacitor_file_/...
374
+ if (path.contains("_capacitor_file_")) {
375
+ String[] parts = path.split("_capacitor_file_");
376
+ if (parts.length > 1) {
377
+ File filesDir = context.getFilesDir();
378
+ return new File(filesDir, parts[1]);
379
+ }
380
+ }
381
+
382
+ File filesDir = context.getFilesDir();
383
+ return new File(filesDir, path);
384
+ }
385
+
386
+ private String getFileExtension(String format) {
387
+ switch (format.toLowerCase()) {
388
+ case "3gp":
389
+ return "3gp";
390
+ case "webm":
391
+ return "webm";
392
+ case "mp4":
393
+ default:
394
+ return "mp4";
395
+ }
396
+ }
397
+
398
+ private int getMuxerFormat(String format) {
399
+ switch (format.toLowerCase()) {
400
+ case "webm":
401
+ return MediaMuxer.OutputFormat.MUXER_OUTPUT_WEBM;
402
+ case "3gp":
403
+ return MediaMuxer.OutputFormat.MUXER_OUTPUT_3GPP;
404
+ case "mp4":
405
+ default:
406
+ return MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4;
407
+ }
408
+ }
409
+
410
+ /** Returns [targetWidth, targetHeight, targetBitrate] */
411
+ private int[] calculateTargetDimensions(
412
+ int originalWidth,
413
+ int originalHeight,
414
+ double originalAspectRatio,
415
+ String quality,
416
+ Integer bitrate,
417
+ Integer customWidth,
418
+ Integer customHeight
419
+ ) {
420
+ int targetWidth;
421
+ int targetHeight;
422
+ int targetBitrate;
423
+ Integer maxWidth = null;
424
+ Integer maxHeight = null;
425
+
426
+ switch (quality) {
427
+ case "low":
428
+ targetBitrate = 800_000;
429
+ maxWidth = 854;
430
+ maxHeight = 480;
431
+ break;
432
+ case "high":
433
+ targetBitrate = bitrate != null ? bitrate : 8_000_000;
434
+ maxWidth = null;
435
+ maxHeight = null;
436
+ break;
437
+ case "custom":
438
+ targetBitrate = bitrate != null ? bitrate : 2_000_000;
439
+ maxWidth = null;
440
+ maxHeight = null;
441
+ break;
442
+ default: // "medium"
443
+ targetBitrate = 1_500_000;
444
+ maxWidth = 1280;
445
+ maxHeight = 720;
446
+ break;
447
+ }
448
+
449
+ if (customWidth != null && customHeight != null) {
450
+ targetWidth = customWidth;
451
+ targetHeight = customHeight;
452
+ return new int[]{targetWidth, targetHeight, targetBitrate};
453
+ }
454
+
455
+ if (customWidth != null) {
456
+ int calculatedHeight = (int) (customWidth / originalAspectRatio);
457
+ targetWidth = Math.min(customWidth, originalWidth);
458
+ targetHeight = Math.min(calculatedHeight, originalHeight);
459
+ return new int[]{targetWidth, targetHeight, targetBitrate};
460
+ }
461
+
462
+ if (customHeight != null) {
463
+ int calculatedWidth = (int) (customHeight * originalAspectRatio);
464
+ targetWidth = Math.min(calculatedWidth, originalWidth);
465
+ targetHeight = Math.min(customHeight, originalHeight);
466
+ return new int[]{targetWidth, targetHeight, targetBitrate};
467
+ }
468
+
469
+ targetWidth = originalWidth;
470
+ targetHeight = originalHeight;
471
+
472
+ if (maxWidth != null && originalWidth > maxWidth) {
473
+ targetWidth = maxWidth;
474
+ targetHeight = (int) (maxWidth / originalAspectRatio);
475
+ }
476
+
477
+ if (maxHeight != null && targetHeight > maxHeight) {
478
+ targetHeight = maxHeight;
479
+ targetWidth = (int) (maxHeight * originalAspectRatio);
480
+ }
481
+
482
+ targetWidth = Math.min(targetWidth, originalWidth);
483
+ targetHeight = Math.min(targetHeight, originalHeight);
484
+
485
+ return new int[]{targetWidth, targetHeight, targetBitrate};
486
+ }
487
+ }
488
+
@@ -0,0 +1,72 @@
1
+ package com.honem.nativevideocompressor;
2
+
3
+ import com.getcapacitor.JSObject;
4
+ import com.getcapacitor.Plugin;
5
+ import com.getcapacitor.PluginCall;
6
+ import com.getcapacitor.PluginMethod;
7
+ import com.getcapacitor.annotation.CapacitorPlugin;
8
+
9
+ @CapacitorPlugin(name = "VideoCompressor")
10
+ public class VideoCompressorPlugin extends Plugin {
11
+
12
+ private final VideoCompressor implementation = new VideoCompressor();
13
+
14
+ @PluginMethod
15
+ public void compressVideo(PluginCall call) {
16
+ String inputPath = call.getString("inputPath");
17
+ if (inputPath == null) {
18
+ call.reject("inputPath is required");
19
+ return;
20
+ }
21
+
22
+ String outputPath = call.getString("outputPath");
23
+ String quality = call.getString("quality", "medium");
24
+ Integer bitrate = call.getInt("bitrate");
25
+ Integer width = call.getInt("width");
26
+ Integer height = call.getInt("height");
27
+ String format = call.getString("format");
28
+
29
+ long startTime = System.currentTimeMillis();
30
+
31
+ implementation.compressVideo(
32
+ getContext(),
33
+ inputPath,
34
+ outputPath,
35
+ quality,
36
+ bitrate,
37
+ width,
38
+ height,
39
+ format,
40
+ (result, error) -> {
41
+ if (error != null) {
42
+ call.reject(error);
43
+ return;
44
+ }
45
+
46
+ long duration = System.currentTimeMillis() - startTime;
47
+ JSObject response = new JSObject();
48
+ response.put("outputPath", result.outputPath);
49
+ response.put("originalSize", result.originalSize);
50
+ response.put("compressedSize", result.compressedSize);
51
+ response.put("compressionRatio", (double) result.originalSize / result.compressedSize);
52
+ response.put("duration", (int) duration);
53
+ call.resolve(response);
54
+ }
55
+ );
56
+ }
57
+
58
+ @PluginMethod
59
+ public void deleteFile(PluginCall call) {
60
+ String path = call.getString("path");
61
+ if (path == null) {
62
+ call.reject("path is required");
63
+ return;
64
+ }
65
+
66
+ boolean success = implementation.deleteFile(getContext(), path);
67
+ JSObject response = new JSObject();
68
+ response.put("success", success);
69
+ call.resolve(response);
70
+ }
71
+ }
72
+
@@ -0,0 +1,76 @@
1
+ export interface CompressionOptions {
2
+ /**
3
+ * Path to the input video file
4
+ */
5
+ inputPath: string;
6
+ /**
7
+ * Optional path for the output file. If not provided, a temp file will be created.
8
+ */
9
+ outputPath?: string;
10
+ /**
11
+ * Quality preset: 'low', 'medium', 'high', or 'custom'
12
+ * @default 'medium'
13
+ */
14
+ quality?: 'low' | 'medium' | 'high' | 'custom';
15
+ /**
16
+ * Video bitrate in bits per second (only used when quality is 'custom')
17
+ */
18
+ bitrate?: number;
19
+ /**
20
+ * Output video width in pixels (only used when quality is 'custom')
21
+ */
22
+ width?: number;
23
+ /**
24
+ * Output video height in pixels (only used when quality is 'custom')
25
+ */
26
+ height?: number;
27
+ /**
28
+ * Output video format
29
+ * @default 'mp4'
30
+ */
31
+ format?: 'mp4' | 'mov' | 'm4v' | '3gp' | 'webm';
32
+ }
33
+ export interface CompressionResult {
34
+ /**
35
+ * Path to the compressed video file (temp file if outputPath not provided)
36
+ */
37
+ outputPath: string;
38
+ /**
39
+ * Original file size in bytes
40
+ */
41
+ originalSize: number;
42
+ /**
43
+ * Compressed file size in bytes
44
+ */
45
+ compressedSize: number;
46
+ /**
47
+ * Compression ratio (originalSize / compressedSize)
48
+ */
49
+ compressionRatio: number;
50
+ /**
51
+ * Duration of the compression process in milliseconds
52
+ */
53
+ duration: number;
54
+ }
55
+ export interface DeleteFileOptions {
56
+ /**
57
+ * Path to the file to delete
58
+ */
59
+ path: string;
60
+ }
61
+ export interface DeleteFileResult {
62
+ /**
63
+ * Whether the file was successfully deleted
64
+ */
65
+ success: boolean;
66
+ }
67
+ export interface VideoCompressorPlugin {
68
+ /**
69
+ * Compress a video file
70
+ */
71
+ compressVideo(options: CompressionOptions): Promise<CompressionResult>;
72
+ /**
73
+ * Delete a file (typically used to clean up temp compressed files after upload)
74
+ */
75
+ deleteFile(options: DeleteFileOptions): Promise<DeleteFileResult>;
76
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,4 @@
1
+ import type { VideoCompressorPlugin } from './definitions';
2
+ declare const VideoCompressor: VideoCompressorPlugin;
3
+ export * from './definitions';
4
+ export { VideoCompressor };
package/dist/index.js ADDED
@@ -0,0 +1,11 @@
1
+ import { registerPlugin } from '@capacitor/core';
2
+ // Register plugin with platform-specific implementations
3
+ // For Electron, the plugin will use the Electron implementation automatically
4
+ // when running in an Electron environment (via Capacitor's plugin bridge)
5
+ const VideoCompressor = registerPlugin('VideoCompressor', {
6
+ web: () => import('./web').then(m => new m.VideoCompressorWeb()),
7
+ // Electron implementation extends WebPlugin and works with Capacitor's standard plugin system
8
+ // It will be automatically used when the plugin is called in an Electron environment
9
+ });
10
+ export * from './definitions';
11
+ export { VideoCompressor };
package/dist/web.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ import { WebPlugin } from '@capacitor/core';
2
+ import type { VideoCompressorPlugin, CompressionOptions, CompressionResult, DeleteFileOptions, DeleteFileResult } from './definitions';
3
+ export declare class VideoCompressorWeb extends WebPlugin implements VideoCompressorPlugin {
4
+ compressVideo(_options: CompressionOptions): Promise<CompressionResult>;
5
+ deleteFile(_options: DeleteFileOptions): Promise<DeleteFileResult>;
6
+ }
package/dist/web.js ADDED
@@ -0,0 +1,9 @@
1
+ import { WebPlugin } from '@capacitor/core';
2
+ export class VideoCompressorWeb extends WebPlugin {
3
+ async compressVideo(_options) {
4
+ throw new Error('Video compression is not supported in web browsers. Please use a native platform (iOS, Android, or Electron).');
5
+ }
6
+ async deleteFile(_options) {
7
+ throw new Error('File deletion is not supported in web browsers. Please use a native platform (iOS, Android, or Electron).');
8
+ }
9
+ }