@capacitor/camera 8.1.0-test.250320251731 → 8.2.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/CapacitorCamera.podspec +3 -2
- package/Package.swift +4 -2
- package/README.md +419 -67
- package/android/build.gradle +4 -4
- package/android/src/main/AndroidManifest.xml +6 -0
- package/android/src/main/java/com/capacitorjs/plugins/camera/CameraPlugin.kt +3 -2
- package/android/src/main/java/com/capacitorjs/plugins/camera/IonCameraFlow.kt +111 -70
- package/android/src/main/java/com/capacitorjs/plugins/camera/IonCameraSettings.kt +1 -2
- package/android/src/main/java/com/capacitorjs/plugins/camera/IonEditableMode.kt +16 -0
- package/android/src/main/java/com/capacitorjs/plugins/camera/IonGallerySettings.kt +1 -2
- package/dist/docs.json +18 -50
- package/dist/esm/definitions.d.ts +135 -38
- package/dist/esm/definitions.js +120 -0
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/web.d.ts +13 -2
- package/dist/esm/web.js +280 -39
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +399 -38
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +399 -38
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/CameraPlugin/CameraPlugin.swift +193 -9
- package/package.json +10 -5
- package/android/libs/IONCameraLib-debug.aar +0 -0
- package/android/src/main/java/com/capacitorjs/plugins/camera/IonVideoSettings.kt +0 -7
package/dist/plugin.js
CHANGED
|
@@ -51,10 +51,138 @@ var capacitorCamera = (function (exports, core) {
|
|
|
51
51
|
EncodingType[EncodingType["JPEG"] = 0] = "JPEG";
|
|
52
52
|
EncodingType[EncodingType["PNG"] = 1] = "PNG";
|
|
53
53
|
})(exports.EncodingType || (exports.EncodingType = {}));
|
|
54
|
+
/**
|
|
55
|
+
* Error codes returned by the Camera plugin.
|
|
56
|
+
* These values match the `code` field on rejected promises.
|
|
57
|
+
*
|
|
58
|
+
* @since 8.2.0
|
|
59
|
+
*/
|
|
60
|
+
exports.CameraErrorCode = void 0;
|
|
61
|
+
(function (CameraErrorCode) {
|
|
62
|
+
// Permissions
|
|
63
|
+
/**
|
|
64
|
+
* Camera access was denied by the user.
|
|
65
|
+
*/
|
|
66
|
+
CameraErrorCode["CameraPermissionDenied"] = "OS-PLUG-CAMR-0003";
|
|
67
|
+
/**
|
|
68
|
+
* Photo library / gallery access was denied by the user.
|
|
69
|
+
*/
|
|
70
|
+
CameraErrorCode["GalleryPermissionDenied"] = "OS-PLUG-CAMR-0005";
|
|
71
|
+
/**
|
|
72
|
+
* No camera hardware is available on the device.
|
|
73
|
+
*/
|
|
74
|
+
CameraErrorCode["NoCameraAvailable"] = "OS-PLUG-CAMR-0007";
|
|
75
|
+
// Take Photo
|
|
76
|
+
/**
|
|
77
|
+
* The user cancelled the take photo action.
|
|
78
|
+
*/
|
|
79
|
+
CameraErrorCode["TakePhotoCancelled"] = "OS-PLUG-CAMR-0006";
|
|
80
|
+
/**
|
|
81
|
+
* Failed to take photo.
|
|
82
|
+
*/
|
|
83
|
+
CameraErrorCode["TakePhotoFailed"] = "OS-PLUG-CAMR-0010";
|
|
84
|
+
/**
|
|
85
|
+
* The take photo action received invalid arguments.
|
|
86
|
+
* @platform ios
|
|
87
|
+
*/
|
|
88
|
+
CameraErrorCode["TakePhotoInvalidArguments"] = "OS-PLUG-CAMR-0014";
|
|
89
|
+
// Edit Photo
|
|
90
|
+
/**
|
|
91
|
+
* The selected file contains invalid image data.
|
|
92
|
+
* @platform ios
|
|
93
|
+
*/
|
|
94
|
+
CameraErrorCode["InvalidImageData"] = "OS-PLUG-CAMR-0008";
|
|
95
|
+
/**
|
|
96
|
+
* Failed to edit image.
|
|
97
|
+
*/
|
|
98
|
+
CameraErrorCode["EditPhotoFailed"] = "OS-PLUG-CAMR-0009";
|
|
99
|
+
/**
|
|
100
|
+
* The user cancelled the edit photo action.
|
|
101
|
+
*/
|
|
102
|
+
CameraErrorCode["EditPhotoCancelled"] = "OS-PLUG-CAMR-0013";
|
|
103
|
+
/**
|
|
104
|
+
* The URI parameter for editing is empty.
|
|
105
|
+
* @platform android
|
|
106
|
+
*/
|
|
107
|
+
CameraErrorCode["EditPhotoEmptyUri"] = "OS-PLUG-CAMR-0024";
|
|
108
|
+
// Choose from Gallery
|
|
109
|
+
/**
|
|
110
|
+
* Failed to retrieve an image from the gallery.
|
|
111
|
+
*/
|
|
112
|
+
CameraErrorCode["ImageNotFound"] = "OS-PLUG-CAMR-0011";
|
|
113
|
+
/**
|
|
114
|
+
* Failed to process the selected image.
|
|
115
|
+
*/
|
|
116
|
+
CameraErrorCode["ProcessImageFailed"] = "OS-PLUG-CAMR-0012";
|
|
117
|
+
/**
|
|
118
|
+
* Failed to choose media from the gallery.
|
|
119
|
+
*/
|
|
120
|
+
CameraErrorCode["ChooseMediaFailed"] = "OS-PLUG-CAMR-0018";
|
|
121
|
+
/**
|
|
122
|
+
* The user cancelled choosing media from the gallery.
|
|
123
|
+
*/
|
|
124
|
+
CameraErrorCode["ChooseMediaCancelled"] = "OS-PLUG-CAMR-0020";
|
|
125
|
+
/**
|
|
126
|
+
* Failed to retrieve the media file path.
|
|
127
|
+
* @platform android
|
|
128
|
+
*/
|
|
129
|
+
CameraErrorCode["MediaPathError"] = "OS-PLUG-CAMR-0021";
|
|
130
|
+
/**
|
|
131
|
+
* Failed to retrieve an image from the provided URI.
|
|
132
|
+
*/
|
|
133
|
+
CameraErrorCode["FetchImageFromUriFailed"] = "OS-PLUG-CAMR-0028";
|
|
134
|
+
// Record Video
|
|
135
|
+
/**
|
|
136
|
+
* Failed to record video.
|
|
137
|
+
*/
|
|
138
|
+
CameraErrorCode["RecordVideoFailed"] = "OS-PLUG-CAMR-0016";
|
|
139
|
+
/**
|
|
140
|
+
* The user cancelled the video recording.
|
|
141
|
+
*/
|
|
142
|
+
CameraErrorCode["RecordVideoCancelled"] = "OS-PLUG-CAMR-0017";
|
|
143
|
+
/**
|
|
144
|
+
* Failed to retrieve a video from the gallery.
|
|
145
|
+
* @platform ios
|
|
146
|
+
*/
|
|
147
|
+
CameraErrorCode["VideoNotFound"] = "OS-PLUG-CAMR-0025";
|
|
148
|
+
// Play Video
|
|
149
|
+
/**
|
|
150
|
+
* Failed to play video.
|
|
151
|
+
*/
|
|
152
|
+
CameraErrorCode["PlayVideoFailed"] = "OS-PLUG-CAMR-0023";
|
|
153
|
+
// General
|
|
154
|
+
/**
|
|
155
|
+
* Failed to encode the media result.
|
|
156
|
+
* @platform ios
|
|
157
|
+
*/
|
|
158
|
+
CameraErrorCode["EncodeResultFailed"] = "OS-PLUG-CAMR-0019";
|
|
159
|
+
/**
|
|
160
|
+
* The selected file does not exist.
|
|
161
|
+
*/
|
|
162
|
+
CameraErrorCode["FileNotFound"] = "OS-PLUG-CAMR-0027";
|
|
163
|
+
/**
|
|
164
|
+
* Invalid argument provided to a plugin method.
|
|
165
|
+
* @platform android
|
|
166
|
+
*/
|
|
167
|
+
CameraErrorCode["InvalidArgument"] = "OS-PLUG-CAMR-0031";
|
|
168
|
+
/**
|
|
169
|
+
* A general plugin error occurred.
|
|
170
|
+
* @platform ios
|
|
171
|
+
*/
|
|
172
|
+
CameraErrorCode["GeneralError"] = "OS-PLUG-CAMR-0026";
|
|
173
|
+
})(exports.CameraErrorCode || (exports.CameraErrorCode = {}));
|
|
54
174
|
|
|
55
175
|
class CameraWeb extends core.WebPlugin {
|
|
56
|
-
async takePhoto(
|
|
57
|
-
|
|
176
|
+
async takePhoto(options) {
|
|
177
|
+
// eslint-disable-next-line no-async-promise-executor
|
|
178
|
+
return new Promise(async (resolve, reject) => {
|
|
179
|
+
if (options.webUseInput) {
|
|
180
|
+
this.takePhotoCameraInputExperience(options, resolve, reject);
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
this.takePhotoCameraExperience(options, resolve, reject);
|
|
184
|
+
}
|
|
185
|
+
});
|
|
58
186
|
}
|
|
59
187
|
async recordVideo(_options) {
|
|
60
188
|
throw this.unimplemented('recordVideo is not implemented on Web.');
|
|
@@ -62,8 +190,11 @@ var capacitorCamera = (function (exports, core) {
|
|
|
62
190
|
async playVideo(_options) {
|
|
63
191
|
throw this.unimplemented('playVideo is not implemented on Web.');
|
|
64
192
|
}
|
|
65
|
-
async chooseFromGallery(
|
|
66
|
-
|
|
193
|
+
async chooseFromGallery(options) {
|
|
194
|
+
// eslint-disable-next-line no-async-promise-executor
|
|
195
|
+
return new Promise(async (resolve, reject) => {
|
|
196
|
+
this.galleryInputExperience(options, resolve, reject);
|
|
197
|
+
});
|
|
67
198
|
}
|
|
68
199
|
async editPhoto(_options) {
|
|
69
200
|
throw this.unimplemented('editPhoto is not implemented on Web.');
|
|
@@ -111,36 +242,7 @@ var capacitorCamera = (function (exports, core) {
|
|
|
111
242
|
});
|
|
112
243
|
}
|
|
113
244
|
async cameraExperience(options, resolve, reject) {
|
|
114
|
-
|
|
115
|
-
const cameraModal = document.createElement('pwa-camera-modal');
|
|
116
|
-
cameraModal.facingMode = options.direction === exports.CameraDirection.Front ? 'user' : 'environment';
|
|
117
|
-
document.body.appendChild(cameraModal);
|
|
118
|
-
try {
|
|
119
|
-
await cameraModal.componentOnReady();
|
|
120
|
-
cameraModal.addEventListener('onPhoto', async (e) => {
|
|
121
|
-
const photo = e.detail;
|
|
122
|
-
if (photo === null) {
|
|
123
|
-
reject(new core.CapacitorException('User cancelled photos app'));
|
|
124
|
-
}
|
|
125
|
-
else if (photo instanceof Error) {
|
|
126
|
-
reject(photo);
|
|
127
|
-
}
|
|
128
|
-
else {
|
|
129
|
-
resolve(await this._getCameraPhoto(photo, options));
|
|
130
|
-
}
|
|
131
|
-
cameraModal.dismiss();
|
|
132
|
-
document.body.removeChild(cameraModal);
|
|
133
|
-
});
|
|
134
|
-
cameraModal.present();
|
|
135
|
-
}
|
|
136
|
-
catch (e) {
|
|
137
|
-
this.fileInputExperience(options, resolve, reject);
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
else {
|
|
141
|
-
console.error(`Unable to load PWA Element 'pwa-camera-modal'. See the docs: https://capacitorjs.com/docs/web/pwa-elements.`);
|
|
142
|
-
this.fileInputExperience(options, resolve, reject);
|
|
143
|
-
}
|
|
245
|
+
await this._setupPWACameraModal(options.direction, (photo) => this._getCameraPhoto(photo, options), () => this.fileInputExperience(options, resolve, reject), resolve, reject);
|
|
144
246
|
}
|
|
145
247
|
fileInputExperience(options, resolve, reject) {
|
|
146
248
|
let input = document.querySelector('#_capacitor-camera-input');
|
|
@@ -253,11 +355,11 @@ var capacitorCamera = (function (exports, core) {
|
|
|
253
355
|
_getCameraPhoto(photo, options) {
|
|
254
356
|
return new Promise((resolve, reject) => {
|
|
255
357
|
const reader = new FileReader();
|
|
256
|
-
const format =
|
|
358
|
+
const format = this._getFileFormat(photo);
|
|
257
359
|
if (options.resultType === 'uri') {
|
|
258
360
|
resolve({
|
|
259
361
|
webPath: URL.createObjectURL(photo),
|
|
260
|
-
format
|
|
362
|
+
format,
|
|
261
363
|
saved: false,
|
|
262
364
|
});
|
|
263
365
|
}
|
|
@@ -268,14 +370,14 @@ var capacitorCamera = (function (exports, core) {
|
|
|
268
370
|
if (options.resultType === 'dataUrl') {
|
|
269
371
|
resolve({
|
|
270
372
|
dataUrl: r,
|
|
271
|
-
format
|
|
373
|
+
format,
|
|
272
374
|
saved: false,
|
|
273
375
|
});
|
|
274
376
|
}
|
|
275
377
|
else {
|
|
276
378
|
resolve({
|
|
277
379
|
base64String: r.split(',')[1],
|
|
278
|
-
format
|
|
380
|
+
format,
|
|
279
381
|
saved: false,
|
|
280
382
|
});
|
|
281
383
|
}
|
|
@@ -286,6 +388,265 @@ var capacitorCamera = (function (exports, core) {
|
|
|
286
388
|
}
|
|
287
389
|
});
|
|
288
390
|
}
|
|
391
|
+
async takePhotoCameraExperience(options, resolve, reject) {
|
|
392
|
+
await this._setupPWACameraModal(options.cameraDirection, (photo) => { var _a; return this._buildPhotoMediaResult(photo, (_a = options.includeMetadata) !== null && _a !== void 0 ? _a : false); }, () => this.takePhotoCameraInputExperience(options, resolve, reject), resolve, reject);
|
|
393
|
+
}
|
|
394
|
+
takePhotoCameraInputExperience(options, resolve, reject) {
|
|
395
|
+
const input = this._createFileInput('_capacitor-camera-input-takephoto');
|
|
396
|
+
const cleanup = () => {
|
|
397
|
+
var _a;
|
|
398
|
+
(_a = input.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(input);
|
|
399
|
+
};
|
|
400
|
+
input.onchange = async (_e) => {
|
|
401
|
+
var _a;
|
|
402
|
+
if (!this._validateFileInput(input, reject, cleanup)) {
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
const file = input.files[0];
|
|
406
|
+
resolve(await this._buildPhotoMediaResult(file, (_a = options.includeMetadata) !== null && _a !== void 0 ? _a : false));
|
|
407
|
+
cleanup();
|
|
408
|
+
};
|
|
409
|
+
input.oncancel = () => {
|
|
410
|
+
reject(new core.CapacitorException('User cancelled photos app'));
|
|
411
|
+
cleanup();
|
|
412
|
+
};
|
|
413
|
+
input.accept = 'image/*';
|
|
414
|
+
if (options.cameraDirection === exports.CameraDirection.Front) {
|
|
415
|
+
input.capture = 'user';
|
|
416
|
+
}
|
|
417
|
+
else {
|
|
418
|
+
// CameraDirection.Rear
|
|
419
|
+
input.capture = 'environment';
|
|
420
|
+
}
|
|
421
|
+
input.click();
|
|
422
|
+
}
|
|
423
|
+
galleryInputExperience(options, resolve, reject) {
|
|
424
|
+
var _a, _b;
|
|
425
|
+
const input = this._createFileInput('_capacitor-camera-input-gallery');
|
|
426
|
+
input.multiple = (_a = options.allowMultipleSelection) !== null && _a !== void 0 ? _a : false;
|
|
427
|
+
const cleanup = () => {
|
|
428
|
+
var _a;
|
|
429
|
+
(_a = input.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(input);
|
|
430
|
+
};
|
|
431
|
+
input.onchange = async (_e) => {
|
|
432
|
+
var _a;
|
|
433
|
+
if (!this._validateFileInput(input, reject, cleanup)) {
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
const results = [];
|
|
437
|
+
// eslint-disable-next-line @typescript-eslint/prefer-for-of
|
|
438
|
+
for (let i = 0; i < input.files.length; i++) {
|
|
439
|
+
const file = input.files[i];
|
|
440
|
+
if (file.type.startsWith('image/')) {
|
|
441
|
+
results.push(await this._buildPhotoMediaResult(file, (_a = options.includeMetadata) !== null && _a !== void 0 ? _a : false));
|
|
442
|
+
}
|
|
443
|
+
else if (file.type.startsWith('video/')) {
|
|
444
|
+
const format = this._getFileFormat(file);
|
|
445
|
+
let thumbnail;
|
|
446
|
+
let resolution;
|
|
447
|
+
let duration;
|
|
448
|
+
try {
|
|
449
|
+
const videoInfo = await this._getVideoMetadata(file);
|
|
450
|
+
thumbnail = videoInfo.thumbnail;
|
|
451
|
+
if (options.includeMetadata) {
|
|
452
|
+
resolution = videoInfo.resolution;
|
|
453
|
+
duration = videoInfo.duration;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
catch (e) {
|
|
457
|
+
console.warn('Failed to get video metadata:', e);
|
|
458
|
+
}
|
|
459
|
+
const result = {
|
|
460
|
+
type: exports.MediaType.Video,
|
|
461
|
+
thumbnail,
|
|
462
|
+
webPath: URL.createObjectURL(file),
|
|
463
|
+
saved: false,
|
|
464
|
+
};
|
|
465
|
+
if (options.includeMetadata) {
|
|
466
|
+
result.metadata = {
|
|
467
|
+
format,
|
|
468
|
+
resolution,
|
|
469
|
+
size: file.size,
|
|
470
|
+
creationDate: new Date(file.lastModified).toISOString(),
|
|
471
|
+
duration,
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
results.push(result);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
resolve({ results });
|
|
478
|
+
cleanup();
|
|
479
|
+
};
|
|
480
|
+
input.oncancel = () => {
|
|
481
|
+
reject(new core.CapacitorException('User cancelled photos app'));
|
|
482
|
+
cleanup();
|
|
483
|
+
};
|
|
484
|
+
// Set accept attribute based on mediaType
|
|
485
|
+
const mediaType = (_b = options.mediaType) !== null && _b !== void 0 ? _b : exports.MediaTypeSelection.Photo;
|
|
486
|
+
if (mediaType === exports.MediaTypeSelection.Photo) {
|
|
487
|
+
input.accept = 'image/*';
|
|
488
|
+
}
|
|
489
|
+
else if (mediaType === exports.MediaTypeSelection.Video) {
|
|
490
|
+
input.accept = 'video/*';
|
|
491
|
+
}
|
|
492
|
+
else {
|
|
493
|
+
// MediaTypeSelection.All
|
|
494
|
+
input.accept = 'image/*,video/*';
|
|
495
|
+
}
|
|
496
|
+
input.click();
|
|
497
|
+
}
|
|
498
|
+
_getFileFormat(file) {
|
|
499
|
+
if (file.type === 'image/png') {
|
|
500
|
+
return 'png';
|
|
501
|
+
}
|
|
502
|
+
else if (file.type === 'image/gif') {
|
|
503
|
+
return 'gif';
|
|
504
|
+
}
|
|
505
|
+
else if (file.type.startsWith('video/')) {
|
|
506
|
+
return file.type.split('/')[1];
|
|
507
|
+
}
|
|
508
|
+
else if (file.type.startsWith('image/')) {
|
|
509
|
+
return 'jpeg';
|
|
510
|
+
}
|
|
511
|
+
return file.type.split('/')[1] || 'jpeg';
|
|
512
|
+
}
|
|
513
|
+
async _buildPhotoMediaResult(file, includeMetadata) {
|
|
514
|
+
const format = this._getFileFormat(file);
|
|
515
|
+
const thumbnail = await this._getBase64FromFile(file);
|
|
516
|
+
const result = {
|
|
517
|
+
type: exports.MediaType.Photo,
|
|
518
|
+
thumbnail,
|
|
519
|
+
webPath: URL.createObjectURL(file),
|
|
520
|
+
saved: false,
|
|
521
|
+
};
|
|
522
|
+
if (includeMetadata) {
|
|
523
|
+
const resolution = await this._getImageResolution(file);
|
|
524
|
+
result.metadata = {
|
|
525
|
+
format,
|
|
526
|
+
resolution,
|
|
527
|
+
size: file.size,
|
|
528
|
+
creationDate: 'lastModified' in file ? new Date(file.lastModified).toISOString() : new Date().toISOString(),
|
|
529
|
+
};
|
|
530
|
+
}
|
|
531
|
+
return result;
|
|
532
|
+
}
|
|
533
|
+
_validateFileInput(input, reject, cleanup) {
|
|
534
|
+
if (!input.files || input.files.length === 0) {
|
|
535
|
+
const message = input.multiple ? 'No files selected' : 'No file selected';
|
|
536
|
+
reject(new core.CapacitorException(message));
|
|
537
|
+
cleanup();
|
|
538
|
+
return false;
|
|
539
|
+
}
|
|
540
|
+
return true;
|
|
541
|
+
}
|
|
542
|
+
async _setupPWACameraModal(cameraDirection, onPhotoCallback, fallbackCallback, resolve, reject) {
|
|
543
|
+
if (customElements.get('pwa-camera-modal')) {
|
|
544
|
+
const cameraModal = document.createElement('pwa-camera-modal');
|
|
545
|
+
cameraModal.facingMode = cameraDirection === exports.CameraDirection.Front ? 'user' : 'environment';
|
|
546
|
+
document.body.appendChild(cameraModal);
|
|
547
|
+
try {
|
|
548
|
+
await cameraModal.componentOnReady();
|
|
549
|
+
cameraModal.addEventListener('onPhoto', async (e) => {
|
|
550
|
+
const photo = e.detail;
|
|
551
|
+
if (photo === null) {
|
|
552
|
+
reject(new core.CapacitorException('User cancelled photos app'));
|
|
553
|
+
}
|
|
554
|
+
else if (photo instanceof Error) {
|
|
555
|
+
reject(photo);
|
|
556
|
+
}
|
|
557
|
+
else {
|
|
558
|
+
resolve(await onPhotoCallback(photo));
|
|
559
|
+
}
|
|
560
|
+
cameraModal.dismiss();
|
|
561
|
+
document.body.removeChild(cameraModal);
|
|
562
|
+
});
|
|
563
|
+
cameraModal.present();
|
|
564
|
+
}
|
|
565
|
+
catch (e) {
|
|
566
|
+
fallbackCallback();
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
else {
|
|
570
|
+
console.error(`Unable to load PWA Element 'pwa-camera-modal'. See the docs: https://capacitorjs.com/docs/web/pwa-elements.`);
|
|
571
|
+
fallbackCallback();
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
_createFileInput(id) {
|
|
575
|
+
let input = document.querySelector(`#${id}`);
|
|
576
|
+
if (!input) {
|
|
577
|
+
input = document.createElement('input');
|
|
578
|
+
input.id = id;
|
|
579
|
+
input.type = 'file';
|
|
580
|
+
input.hidden = true;
|
|
581
|
+
document.body.appendChild(input);
|
|
582
|
+
}
|
|
583
|
+
return input;
|
|
584
|
+
}
|
|
585
|
+
async _getImageResolution(image) {
|
|
586
|
+
try {
|
|
587
|
+
const bitmap = await createImageBitmap(image);
|
|
588
|
+
const resolution = `${bitmap.width}x${bitmap.height}`;
|
|
589
|
+
bitmap.close();
|
|
590
|
+
return resolution;
|
|
591
|
+
}
|
|
592
|
+
catch (e) {
|
|
593
|
+
console.warn('Failed to get image resolution:', e);
|
|
594
|
+
return undefined;
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
_getBase64FromFile(file) {
|
|
598
|
+
return new Promise((resolve, reject) => {
|
|
599
|
+
const reader = new FileReader();
|
|
600
|
+
reader.onloadend = () => {
|
|
601
|
+
const dataUrl = reader.result;
|
|
602
|
+
const base64 = dataUrl.split(',')[1];
|
|
603
|
+
resolve(base64);
|
|
604
|
+
};
|
|
605
|
+
reader.onerror = (e) => {
|
|
606
|
+
reject(e);
|
|
607
|
+
};
|
|
608
|
+
reader.readAsDataURL(file);
|
|
609
|
+
});
|
|
610
|
+
}
|
|
611
|
+
_getVideoMetadata(videoFile) {
|
|
612
|
+
return new Promise((resolve) => {
|
|
613
|
+
const video = document.createElement('video');
|
|
614
|
+
video.preload = 'metadata';
|
|
615
|
+
video.muted = true;
|
|
616
|
+
video.onloadedmetadata = () => {
|
|
617
|
+
// Seek to 1 second or 10% of duration to capture thumbnail
|
|
618
|
+
const seekTime = Math.min(1, video.duration * 0.1);
|
|
619
|
+
video.currentTime = seekTime;
|
|
620
|
+
};
|
|
621
|
+
video.onseeked = () => {
|
|
622
|
+
const result = {
|
|
623
|
+
resolution: `${video.videoWidth}x${video.videoHeight}`,
|
|
624
|
+
duration: video.duration,
|
|
625
|
+
};
|
|
626
|
+
try {
|
|
627
|
+
const canvas = document.createElement('canvas');
|
|
628
|
+
canvas.width = video.videoWidth;
|
|
629
|
+
canvas.height = video.videoHeight;
|
|
630
|
+
const ctx = canvas.getContext('2d');
|
|
631
|
+
if (ctx) {
|
|
632
|
+
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
|
|
633
|
+
result.thumbnail = canvas.toDataURL('image/jpeg', 0.8).split(',')[1];
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
catch (e) {
|
|
637
|
+
console.warn('Failed to generate video thumbnail:', e);
|
|
638
|
+
}
|
|
639
|
+
URL.revokeObjectURL(video.src);
|
|
640
|
+
resolve(result);
|
|
641
|
+
};
|
|
642
|
+
video.onerror = () => {
|
|
643
|
+
// Clean up and return defaults
|
|
644
|
+
URL.revokeObjectURL(video.src);
|
|
645
|
+
resolve({});
|
|
646
|
+
};
|
|
647
|
+
video.src = URL.createObjectURL(videoFile);
|
|
648
|
+
});
|
|
649
|
+
}
|
|
289
650
|
async checkPermissions() {
|
|
290
651
|
if (typeof navigator === 'undefined' || !navigator.permissions) {
|
|
291
652
|
throw this.unavailable('Permissions API not available in this browser');
|