@authme/util 2.8.31 → 2.8.33
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/index.cjs +124 -153
- package/index.js +124 -153
- package/package.json +1 -1
- package/src/ui/camera.d.ts +1 -1
- package/src/ui/popup.d.ts +2 -2
package/index.cjs
CHANGED
|
@@ -29,6 +29,7 @@ require('core-js/modules/es.parse-int.js');
|
|
|
29
29
|
var Lottie = require('lottie-web');
|
|
30
30
|
require('core-js/modules/es.number.to-fixed.js');
|
|
31
31
|
require('core-js/modules/es.array.sort.js');
|
|
32
|
+
require('core-js/modules/esnext.global-this.js');
|
|
32
33
|
require('core-js/modules/es.string.trim.js');
|
|
33
34
|
require('core-js/modules/es.string.starts-with.js');
|
|
34
35
|
require('core-js/modules/es.symbol.description.js');
|
|
@@ -1095,7 +1096,6 @@ const uiThemeDirection = (panel, hintStyle) => {
|
|
|
1095
1096
|
panel.style.borderRadius = `${hintStyle.cornerRadius || 20}px`;
|
|
1096
1097
|
panel.style.backgroundColor = hintStyle.backgroundColor;
|
|
1097
1098
|
panel.style.opacity = hintStyle.backgroundOpacity;
|
|
1098
|
-
panel.style.display = 'none';
|
|
1099
1099
|
panel.style.flexDirection = 'row';
|
|
1100
1100
|
panel.style.alignItems = 'center';
|
|
1101
1101
|
panel.style.justifyContent = 'center';
|
|
@@ -1363,7 +1363,7 @@ function checkOnlineStatus(msg, buttonText) {
|
|
|
1363
1363
|
});
|
|
1364
1364
|
}
|
|
1365
1365
|
|
|
1366
|
-
function showPopup(title, content, showButton = true,
|
|
1366
|
+
function showPopup(title, content, showButton = true, buttonText, callback) {
|
|
1367
1367
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1368
1368
|
const target = document.querySelector('.authme-container');
|
|
1369
1369
|
const popupBackground = document.createElement('div');
|
|
@@ -1378,7 +1378,7 @@ function showPopup(title, content, showButton = true, button, callback) {
|
|
|
1378
1378
|
popupContent.textContent = content;
|
|
1379
1379
|
const popupConfirmBtn = document.createElement('button');
|
|
1380
1380
|
popupConfirmBtn.className = 'popup-btn';
|
|
1381
|
-
popupConfirmBtn.textContent = '確定';
|
|
1381
|
+
popupConfirmBtn.textContent = buttonText || '確定';
|
|
1382
1382
|
popupPanel.appendChild(popupTitle);
|
|
1383
1383
|
popupPanel.appendChild(popupContent);
|
|
1384
1384
|
if (showButton) {
|
|
@@ -1401,14 +1401,14 @@ function hidePopup() {
|
|
|
1401
1401
|
popupBackground.remove();
|
|
1402
1402
|
}
|
|
1403
1403
|
}
|
|
1404
|
-
function asyncShowPopup(title, content, showButton = true,
|
|
1404
|
+
function asyncShowPopup(title, content, showButton = true, buttonText) {
|
|
1405
1405
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1406
1406
|
return new Promise((res, rej) => {
|
|
1407
1407
|
const callback = () => {
|
|
1408
1408
|
res(true);
|
|
1409
1409
|
hidePopup();
|
|
1410
1410
|
};
|
|
1411
|
-
showPopup(title, content, showButton,
|
|
1411
|
+
showPopup(title, content, showButton, buttonText, callback);
|
|
1412
1412
|
});
|
|
1413
1413
|
});
|
|
1414
1414
|
}
|
|
@@ -1421,6 +1421,7 @@ var BROWSER_CAMERA_ERRORS;
|
|
|
1421
1421
|
BROWSER_CAMERA_ERRORS["NOT_FOUND_ERROR"] = "NotFoundError";
|
|
1422
1422
|
BROWSER_CAMERA_ERRORS["NOT_READABLE_ERROR"] = "NotReadableError";
|
|
1423
1423
|
BROWSER_CAMERA_ERRORS["ABORT_ERROR"] = "AbortError";
|
|
1424
|
+
BROWSER_CAMERA_ERRORS["LOW_RESOLUTION"] = "LowResolutionError";
|
|
1424
1425
|
})(BROWSER_CAMERA_ERRORS || (BROWSER_CAMERA_ERRORS = {}));
|
|
1425
1426
|
let stream;
|
|
1426
1427
|
const videoConstraintsFactory = (isPC, facingMode) => {
|
|
@@ -1448,50 +1449,6 @@ const videoConstraintsFactory = (isPC, facingMode) => {
|
|
|
1448
1449
|
}
|
|
1449
1450
|
};
|
|
1450
1451
|
};
|
|
1451
|
-
/**
|
|
1452
|
-
* 等待camera完全就緒
|
|
1453
|
-
* 用於解決外接camera初始化延遲問題
|
|
1454
|
-
* @param videoTrack - MediaStreamTrack 實例
|
|
1455
|
-
* @param maxWaitMs - 最大等待時間(毫秒)
|
|
1456
|
-
* @returns Promise<boolean> - true表示camera已就緒,false表示超時
|
|
1457
|
-
*/
|
|
1458
|
-
function waitForCameraReady(videoTrack, maxWaitMs = 100) {
|
|
1459
|
-
var _a, _b, _c, _d;
|
|
1460
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1461
|
-
const startTime = Date.now();
|
|
1462
|
-
const checkInterval = 50; // 每50ms檢查一次
|
|
1463
|
-
const minStableTime = 50; // 最小穩定時間:即使檢測到就緒,也要等待一小段時間
|
|
1464
|
-
while (Date.now() - startTime < maxWaitMs) {
|
|
1465
|
-
const _settings = videoTrack.getSettings();
|
|
1466
|
-
const capabilities = (_a = videoTrack.getCapabilities) === null || _a === void 0 ? void 0 : _a.call(videoTrack);
|
|
1467
|
-
// 檢查camera是否已提供完整資訊
|
|
1468
|
-
// 1. 必須有基本的寬高設定
|
|
1469
|
-
// 2. 如果支援 getCapabilities,確認有最大解析度資訊
|
|
1470
|
-
const hasBasicSettings = _settings.width && _settings.height;
|
|
1471
|
-
const hasCapabilities = !capabilities || ((_b = capabilities === null || capabilities === void 0 ? void 0 : capabilities.width) === null || _b === void 0 ? void 0 : _b.max) && ((_c = capabilities === null || capabilities === void 0 ? void 0 : capabilities.height) === null || _c === void 0 ? void 0 : _c.max);
|
|
1472
|
-
if (hasBasicSettings && hasCapabilities) {
|
|
1473
|
-
const elapsedTime = Date.now() - startTime;
|
|
1474
|
-
// ⚠️ 關鍵修正:即使檢測到就緒,也要確保經過最小等待時間
|
|
1475
|
-
// 這對外接相機特別重要,它們需要額外的硬體初始化時間
|
|
1476
|
-
if (elapsedTime < minStableTime) {
|
|
1477
|
-
const remainingTime = minStableTime - elapsedTime;
|
|
1478
|
-
console.log(`⏳ Camera基本就緒,額外等待 ${remainingTime}ms 確保穩定 (當前: ${_settings.width}x${_settings.height})`);
|
|
1479
|
-
yield new Promise(resolve => setTimeout(resolve, remainingTime));
|
|
1480
|
-
}
|
|
1481
|
-
console.log(`✅ Camera就緒 (耗時: ${Date.now() - startTime}ms, 解析度: ${_settings.width}x${_settings.height})`);
|
|
1482
|
-
return true;
|
|
1483
|
-
}
|
|
1484
|
-
yield new Promise(resolve => setTimeout(resolve, checkInterval));
|
|
1485
|
-
}
|
|
1486
|
-
const settings = videoTrack.getSettings();
|
|
1487
|
-
console.warn(`⚠️ Camera未在時限內完全就緒 (${maxWaitMs}ms), 當前狀態:`, {
|
|
1488
|
-
width: settings.width,
|
|
1489
|
-
height: settings.height,
|
|
1490
|
-
hasCapabilities: !!((_d = videoTrack.getCapabilities) === null || _d === void 0 ? void 0 : _d.call(videoTrack))
|
|
1491
|
-
});
|
|
1492
|
-
return false;
|
|
1493
|
-
});
|
|
1494
|
-
}
|
|
1495
1452
|
function inferFacingModeFromLabel(label) {
|
|
1496
1453
|
const pattern = /rear|back|rück|arrière|trasera|trás|traseira|posteriore|后|後|背|задней|الخلفية|후|arka|achterzijde|หลัง|baksidan|bagside|sau|bak|tylny|takakamera|belakang|אחורית|πίσω|spate|hátsó|zadní|darrere|zadná|задня|stražnja|belakang|बैक/i;
|
|
1497
1454
|
if (pattern.test(label !== null && label !== void 0 ? label : '')) {
|
|
@@ -1545,8 +1502,53 @@ function arrayFromAsync(asyncIterable) {
|
|
|
1545
1502
|
return result;
|
|
1546
1503
|
});
|
|
1547
1504
|
}
|
|
1548
|
-
function
|
|
1549
|
-
var _a
|
|
1505
|
+
function upgradeResolution(videoTrack) {
|
|
1506
|
+
var _a;
|
|
1507
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1508
|
+
const targetConstraints = {
|
|
1509
|
+
width: {
|
|
1510
|
+
ideal: 1920,
|
|
1511
|
+
max: 3840
|
|
1512
|
+
},
|
|
1513
|
+
height: {
|
|
1514
|
+
ideal: 1080,
|
|
1515
|
+
max: 2160
|
|
1516
|
+
}
|
|
1517
|
+
};
|
|
1518
|
+
const minAcceptableWidth = 1280; // 最低可接受的解析度寬度
|
|
1519
|
+
const maxAttempts = 5; // 最多嘗試10次
|
|
1520
|
+
const retryDelay = 2000; // 每次重試間隔1秒
|
|
1521
|
+
console.log('🚀 開始提升解析度...');
|
|
1522
|
+
const startTime = Date.now();
|
|
1523
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
1524
|
+
try {
|
|
1525
|
+
yield videoTrack.applyConstraints(targetConstraints);
|
|
1526
|
+
const settings = videoTrack.getSettings();
|
|
1527
|
+
const currentWidth = (_a = settings.width) !== null && _a !== void 0 ? _a : 0;
|
|
1528
|
+
console.log(`嘗試 ${attempt}/${maxAttempts} - 解析度: ${currentWidth}x${settings.height}`);
|
|
1529
|
+
// 檢查是否達到可接受的解析度
|
|
1530
|
+
if (currentWidth >= minAcceptableWidth) {
|
|
1531
|
+
const elapsedTime = Date.now() - startTime;
|
|
1532
|
+
console.log(`✅ 解析度提升成功!最終: ${currentWidth}x${settings.height} (耗時: ${elapsedTime}ms)`);
|
|
1533
|
+
return; // 成功,退出
|
|
1534
|
+
}
|
|
1535
|
+
// 解析度還不夠,繼續重試
|
|
1536
|
+
console.log(`⚠️ 解析度 ${currentWidth} < ${minAcceptableWidth},繼續重試...`);
|
|
1537
|
+
} catch (e) {
|
|
1538
|
+
console.warn(`⚠️ 第 ${attempt} 次嘗試失敗:`, e);
|
|
1539
|
+
}
|
|
1540
|
+
// 如果不是最後一次嘗試,等待後重試
|
|
1541
|
+
if (attempt < maxAttempts) {
|
|
1542
|
+
yield new Promise(resolve => setTimeout(resolve, retryDelay));
|
|
1543
|
+
}
|
|
1544
|
+
}
|
|
1545
|
+
// 所有嘗試都失敗
|
|
1546
|
+
const finalSettings = videoTrack.getSettings();
|
|
1547
|
+
console.warn(`⚠️ 達到最大重試次數,使用當前解析度: ${finalSettings.width}x${finalSettings.height}`);
|
|
1548
|
+
});
|
|
1549
|
+
}
|
|
1550
|
+
function switchCamera(deviceId, video, translate) {
|
|
1551
|
+
var _a, _b;
|
|
1550
1552
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1551
1553
|
const startTime = performance.now();
|
|
1552
1554
|
try {
|
|
@@ -1562,103 +1564,39 @@ function switchCamera(deviceId, video) {
|
|
|
1562
1564
|
};
|
|
1563
1565
|
const constraint = localStorage.getItem('camera_constraint');
|
|
1564
1566
|
stream = yield navigator.mediaDevices.getUserMedia(basicConstraints);
|
|
1565
|
-
// 第二階段:等待就緒策略 + 漸進式重試提升解析度
|
|
1566
1567
|
if (!constraint && stream) {
|
|
1567
1568
|
const videoTrack = stream.getVideoTracks()[0];
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
const beforeSettings = videoTrack.getSettings();
|
|
1592
|
-
yield videoTrack.applyConstraints(targetConstraints);
|
|
1593
|
-
// ⚠️ 關鍵:驗證解析度是否真的改變
|
|
1594
|
-
const afterSettings = videoTrack.getSettings();
|
|
1595
|
-
const afterWidth = (_a = afterSettings.width) !== null && _a !== void 0 ? _a : 0;
|
|
1596
|
-
const beforeWidth = (_b = beforeSettings.width) !== null && _b !== void 0 ? _b : 0;
|
|
1597
|
-
// 成功條件:
|
|
1598
|
-
// 1. 如果初始解析度 < 最低標準 → 必須提升且達到最低標準
|
|
1599
|
-
// 2. 如果初始解析度 >= 最低標準 → 只要有提升就算成功
|
|
1600
|
-
const needsImprovement = beforeWidth < minAcceptableWidth;
|
|
1601
|
-
const resolutionImproved = needsImprovement ? afterWidth > beforeWidth && afterWidth >= minAcceptableWidth : afterWidth > beforeWidth;
|
|
1602
|
-
if (resolutionImproved) {
|
|
1603
|
-
console.log(`✅ 解析度提升成功 (嘗試 ${attempt + 1}/${maxRetries})`, {
|
|
1604
|
-
before: `${beforeWidth}x${beforeSettings.height}`,
|
|
1605
|
-
after: `${afterWidth}x${afterSettings.height}`,
|
|
1606
|
-
deviceLabel: videoTrack.label
|
|
1607
|
-
});
|
|
1608
|
-
break; // 真正成功才跳出
|
|
1609
|
-
} else {
|
|
1610
|
-
// applyConstraints 沒報錯,但解析度沒變 → 視為失敗,觸發重試
|
|
1611
|
-
console.warn(`⚠️ 解析度未改善 (嘗試 ${attempt + 1}/${maxRetries})`, {
|
|
1612
|
-
before: `${beforeWidth}x${beforeSettings.height}`,
|
|
1613
|
-
after: `${afterWidth}x${afterSettings.height}`,
|
|
1614
|
-
expected: needsImprovement ? `>= ${minAcceptableWidth}` : '任何提升'
|
|
1615
|
-
});
|
|
1616
|
-
if (attempt === maxRetries - 1) {
|
|
1617
|
-
console.warn(`⚠️ 達到最大重試次數,使用當前解析度 (${afterWidth}x${afterSettings.height})`);
|
|
1618
|
-
} else {
|
|
1619
|
-
// 繼續重試
|
|
1620
|
-
continue;
|
|
1621
|
-
}
|
|
1622
|
-
}
|
|
1623
|
-
} catch (error) {
|
|
1624
|
-
if (attempt === maxRetries - 1) {
|
|
1625
|
-
// 最後一次嘗試失敗,記錄警告但不影響功能
|
|
1626
|
-
const settings = videoTrack.getSettings();
|
|
1627
|
-
console.warn(`⚠️ 無法提升解析度,使用基本設定 (${settings.width}x${settings.height})`, error);
|
|
1628
|
-
} else {
|
|
1629
|
-
console.log(`⏳ 解析度提升嘗試 ${attempt + 1} 失敗,準備重試...`, error);
|
|
1630
|
-
}
|
|
1631
|
-
}
|
|
1632
|
-
}
|
|
1633
|
-
} else {
|
|
1634
|
-
// Camera未完全就緒,但仍嘗試套用約束
|
|
1635
|
-
console.warn('⚠️ Camera未完全就緒,嘗試套用解析度約束...');
|
|
1636
|
-
try {
|
|
1637
|
-
yield videoTrack.applyConstraints({
|
|
1638
|
-
width: {
|
|
1639
|
-
ideal: 1920,
|
|
1640
|
-
max: 3840
|
|
1641
|
-
},
|
|
1642
|
-
height: {
|
|
1643
|
-
ideal: 1080,
|
|
1644
|
-
max: 2160
|
|
1645
|
-
}
|
|
1646
|
-
});
|
|
1647
|
-
} catch (error) {
|
|
1648
|
-
console.warn('⚠️ 解析度提升失敗,使用預設解析度', error);
|
|
1649
|
-
}
|
|
1650
|
-
}
|
|
1651
|
-
// 🆕 記錄最終解析度資訊
|
|
1652
|
-
const finalSettings = videoTrack.getSettings();
|
|
1653
|
-
const initTime = (performance.now() - startTime).toFixed(2);
|
|
1654
|
-
console.log('📹 Camera初始化完成', {
|
|
1655
|
-
deviceId,
|
|
1656
|
-
deviceLabel: videoTrack.label,
|
|
1657
|
-
resolution: `${finalSettings.width}x${finalSettings.height}`,
|
|
1658
|
-
frameRate: finalSettings.frameRate,
|
|
1659
|
-
facingMode: finalSettings.facingMode,
|
|
1660
|
-
initTime: `${initTime}ms`
|
|
1569
|
+
yield upgradeResolution(videoTrack); // ✅ 加上 await 等待解析度提升完成
|
|
1570
|
+
// 🆕 記錄最終解析度資訊
|
|
1571
|
+
const finalSettings = videoTrack.getSettings();
|
|
1572
|
+
const initTime = (performance.now() - startTime).toFixed(2);
|
|
1573
|
+
console.log('📹 Camera初始化完成', {
|
|
1574
|
+
deviceId,
|
|
1575
|
+
deviceLabel: videoTrack.label,
|
|
1576
|
+
resolution: `${finalSettings.width}x${finalSettings.height}`,
|
|
1577
|
+
frameRate: finalSettings.frameRate,
|
|
1578
|
+
facingMode: finalSettings.facingMode,
|
|
1579
|
+
initTime: `${initTime}ms`
|
|
1580
|
+
});
|
|
1581
|
+
}
|
|
1582
|
+
// 檢查最終解析度是否達到最低要求
|
|
1583
|
+
const currentTrack = stream.getVideoTracks()[0];
|
|
1584
|
+
if (currentTrack) {
|
|
1585
|
+
const currentSettings = currentTrack.getSettings();
|
|
1586
|
+
const minRequiredWidth = 1280;
|
|
1587
|
+
if (currentSettings.width && currentSettings.width < minRequiredWidth) {
|
|
1588
|
+
console.error('❌ Camera解析度不足', {
|
|
1589
|
+
current: `${currentSettings.width}x${currentSettings.height}`,
|
|
1590
|
+
required: `${minRequiredWidth}px minimum width`,
|
|
1591
|
+
deviceId
|
|
1661
1592
|
});
|
|
1593
|
+
// 關閉當前相機流
|
|
1594
|
+
stream.getTracks().forEach(track => track.stop());
|
|
1595
|
+
// 顯示彈窗並等待用戶點擊
|
|
1596
|
+
if (translate) {
|
|
1597
|
+
yield asyncShowPopup('相機解析度不足', '相機解析度不足,請重新嘗試或更換裝置', true, '重新嘗試');
|
|
1598
|
+
}
|
|
1599
|
+
throw BROWSER_CAMERA_ERRORS.LOW_RESOLUTION;
|
|
1662
1600
|
}
|
|
1663
1601
|
}
|
|
1664
1602
|
// try {
|
|
@@ -1704,6 +1642,10 @@ function switchCamera(deviceId, video) {
|
|
|
1704
1642
|
video.srcObject = stream;
|
|
1705
1643
|
}, 10);
|
|
1706
1644
|
} catch (e) {
|
|
1645
|
+
// 如果是低解析度錯誤,直接拋出不包裝,讓外層處理重試
|
|
1646
|
+
if (e === BROWSER_CAMERA_ERRORS.LOW_RESOLUTION) {
|
|
1647
|
+
throw e;
|
|
1648
|
+
}
|
|
1707
1649
|
// 🆕 增強錯誤日誌
|
|
1708
1650
|
const initTime = (performance.now() - startTime).toFixed(2);
|
|
1709
1651
|
console.error('❌ Camera切換失敗', {
|
|
@@ -1713,13 +1655,13 @@ function switchCamera(deviceId, video) {
|
|
|
1713
1655
|
errorMessage: e === null || e === void 0 ? void 0 : e.message,
|
|
1714
1656
|
initTime: `${initTime}ms`,
|
|
1715
1657
|
streamExists: !!stream,
|
|
1716
|
-
trackCount: (
|
|
1658
|
+
trackCount: (_b = (_a = stream === null || stream === void 0 ? void 0 : stream.getTracks()) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0
|
|
1717
1659
|
});
|
|
1718
1660
|
throw new AuthmeError(exports.ErrorCode.CAMERA_NOT_SUPPORT, e);
|
|
1719
1661
|
}
|
|
1720
1662
|
});
|
|
1721
1663
|
}
|
|
1722
|
-
function _requestCamera(video, facingMode) {
|
|
1664
|
+
function _requestCamera(video, facingMode, translate) {
|
|
1723
1665
|
var _a, _b, _c, _d, _e, _f;
|
|
1724
1666
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1725
1667
|
const requestStartTime = performance.now();
|
|
@@ -1818,7 +1760,28 @@ function _requestCamera(video, facingMode) {
|
|
|
1818
1760
|
if (stream) {
|
|
1819
1761
|
stream.getTracks().forEach(track => track.stop());
|
|
1820
1762
|
}
|
|
1821
|
-
|
|
1763
|
+
// 嘗試開啟相機,如果解析度不足會持續重試
|
|
1764
|
+
let retryCount = 0;
|
|
1765
|
+
const MAX_RETRIES = 5;
|
|
1766
|
+
while (retryCount < MAX_RETRIES) {
|
|
1767
|
+
try {
|
|
1768
|
+
yield switchCamera(deviceId, video, translate);
|
|
1769
|
+
break; // 成功就跳出循環
|
|
1770
|
+
} catch (error) {
|
|
1771
|
+
if (error === BROWSER_CAMERA_ERRORS.LOW_RESOLUTION) {
|
|
1772
|
+
retryCount++;
|
|
1773
|
+
if (retryCount >= MAX_RETRIES) {
|
|
1774
|
+
console.error(`❌ 達到最大重試次數 (${MAX_RETRIES}),相機解析度持續不足`);
|
|
1775
|
+
throw error;
|
|
1776
|
+
}
|
|
1777
|
+
console.log(`🔄 解析度不足,第 ${retryCount} 次重試...`);
|
|
1778
|
+
// 等待一下再重試
|
|
1779
|
+
yield new Promise(resolve => setTimeout(resolve, 500));
|
|
1780
|
+
} else {
|
|
1781
|
+
throw error; // 其他錯誤直接拋出
|
|
1782
|
+
}
|
|
1783
|
+
}
|
|
1784
|
+
}
|
|
1822
1785
|
// 🆕 記錄整個請求流程的總結資訊
|
|
1823
1786
|
const totalTime = (performance.now() - requestStartTime).toFixed(2);
|
|
1824
1787
|
console.log('🎉 Camera請求流程完成', {
|
|
@@ -1866,7 +1829,7 @@ function requestCamera({
|
|
|
1866
1829
|
try {
|
|
1867
1830
|
//for ios 17.4.1 hack, if you call getUserMedia too early will only get not allowed.
|
|
1868
1831
|
yield sleep(1000);
|
|
1869
|
-
return yield _requestCamera(video, facingMode);
|
|
1832
|
+
return yield _requestCamera(video, facingMode, translate);
|
|
1870
1833
|
} catch (error) {
|
|
1871
1834
|
if ((error === null || error === void 0 ? void 0 : error.name) === BROWSER_CAMERA_ERRORS.NOT_ALLOWED_ERROR) {
|
|
1872
1835
|
perm = yield navigator.permissions.query({
|
|
@@ -1877,9 +1840,9 @@ function requestCamera({
|
|
|
1877
1840
|
}
|
|
1878
1841
|
}
|
|
1879
1842
|
}
|
|
1880
|
-
return yield _requestCamera(video, facingMode);
|
|
1843
|
+
return yield _requestCamera(video, facingMode, translate);
|
|
1881
1844
|
} else {
|
|
1882
|
-
return yield _requestCamera(video, facingMode);
|
|
1845
|
+
return yield _requestCamera(video, facingMode, translate);
|
|
1883
1846
|
}
|
|
1884
1847
|
} catch (error) {
|
|
1885
1848
|
if (error === BROWSER_CAMERA_ERRORS.NOT_SUPPORT) {
|
|
@@ -1903,6 +1866,14 @@ function requestCamera({
|
|
|
1903
1866
|
*/
|
|
1904
1867
|
throw new AuthmeError(exports.ErrorCode.CAMERA_NOT_SUPPORT, error);
|
|
1905
1868
|
}
|
|
1869
|
+
if (error === BROWSER_CAMERA_ERRORS.LOW_RESOLUTION) {
|
|
1870
|
+
// 顯示彈窗並等待用戶點擊後重新載入
|
|
1871
|
+
yield asyncShowPopup(translate('sdk.general.error.cameraLowResolution'), translate('sdk.general.error.cameraLowResolution') + ',請重新嘗試或更換裝置', true);
|
|
1872
|
+
// 用戶點擊確定後,重新載入頁面
|
|
1873
|
+
globalThis.location.reload();
|
|
1874
|
+
// 不會執行到這裡,因為頁面已重新載入
|
|
1875
|
+
throw new AuthmeError(exports.ErrorCode.CAMERA_NOT_SUPPORT, error);
|
|
1876
|
+
}
|
|
1906
1877
|
if (isOverconstrainedError(error)) {
|
|
1907
1878
|
showMessage(translate('sdk.general.error.cameraLowResolution'));
|
|
1908
1879
|
throw new AuthmeError(exports.ErrorCode.CAMERA_NOT_SUPPORT, error);
|
|
@@ -3397,8 +3368,8 @@ const themeConfigDefault = {
|
|
|
3397
3368
|
};
|
|
3398
3369
|
|
|
3399
3370
|
var name = "authme/sdk";
|
|
3400
|
-
var version$1 = "2.8.
|
|
3401
|
-
var date = "2025-11-
|
|
3371
|
+
var version$1 = "2.8.33";
|
|
3372
|
+
var date = "2025-11-21T09:00:22+0000";
|
|
3402
3373
|
var packageInfo = {
|
|
3403
3374
|
name: name,
|
|
3404
3375
|
version: version$1,
|
package/index.js
CHANGED
|
@@ -25,6 +25,7 @@ import 'core-js/modules/es.parse-int.js';
|
|
|
25
25
|
import Lottie from 'lottie-web';
|
|
26
26
|
import 'core-js/modules/es.number.to-fixed.js';
|
|
27
27
|
import 'core-js/modules/es.array.sort.js';
|
|
28
|
+
import 'core-js/modules/esnext.global-this.js';
|
|
28
29
|
import 'core-js/modules/es.string.trim.js';
|
|
29
30
|
import 'core-js/modules/es.string.starts-with.js';
|
|
30
31
|
import 'core-js/modules/es.symbol.description.js';
|
|
@@ -1085,7 +1086,6 @@ const uiThemeDirection = (panel, hintStyle) => {
|
|
|
1085
1086
|
panel.style.borderRadius = `${hintStyle.cornerRadius || 20}px`;
|
|
1086
1087
|
panel.style.backgroundColor = hintStyle.backgroundColor;
|
|
1087
1088
|
panel.style.opacity = hintStyle.backgroundOpacity;
|
|
1088
|
-
panel.style.display = 'none';
|
|
1089
1089
|
panel.style.flexDirection = 'row';
|
|
1090
1090
|
panel.style.alignItems = 'center';
|
|
1091
1091
|
panel.style.justifyContent = 'center';
|
|
@@ -1353,7 +1353,7 @@ function checkOnlineStatus(msg, buttonText) {
|
|
|
1353
1353
|
});
|
|
1354
1354
|
}
|
|
1355
1355
|
|
|
1356
|
-
function showPopup(title, content, showButton = true,
|
|
1356
|
+
function showPopup(title, content, showButton = true, buttonText, callback) {
|
|
1357
1357
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1358
1358
|
const target = document.querySelector('.authme-container');
|
|
1359
1359
|
const popupBackground = document.createElement('div');
|
|
@@ -1368,7 +1368,7 @@ function showPopup(title, content, showButton = true, button, callback) {
|
|
|
1368
1368
|
popupContent.textContent = content;
|
|
1369
1369
|
const popupConfirmBtn = document.createElement('button');
|
|
1370
1370
|
popupConfirmBtn.className = 'popup-btn';
|
|
1371
|
-
popupConfirmBtn.textContent = '確定';
|
|
1371
|
+
popupConfirmBtn.textContent = buttonText || '確定';
|
|
1372
1372
|
popupPanel.appendChild(popupTitle);
|
|
1373
1373
|
popupPanel.appendChild(popupContent);
|
|
1374
1374
|
if (showButton) {
|
|
@@ -1391,14 +1391,14 @@ function hidePopup() {
|
|
|
1391
1391
|
popupBackground.remove();
|
|
1392
1392
|
}
|
|
1393
1393
|
}
|
|
1394
|
-
function asyncShowPopup(title, content, showButton = true,
|
|
1394
|
+
function asyncShowPopup(title, content, showButton = true, buttonText) {
|
|
1395
1395
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1396
1396
|
return new Promise((res, rej) => {
|
|
1397
1397
|
const callback = () => {
|
|
1398
1398
|
res(true);
|
|
1399
1399
|
hidePopup();
|
|
1400
1400
|
};
|
|
1401
|
-
showPopup(title, content, showButton,
|
|
1401
|
+
showPopup(title, content, showButton, buttonText, callback);
|
|
1402
1402
|
});
|
|
1403
1403
|
});
|
|
1404
1404
|
}
|
|
@@ -1411,6 +1411,7 @@ var BROWSER_CAMERA_ERRORS;
|
|
|
1411
1411
|
BROWSER_CAMERA_ERRORS["NOT_FOUND_ERROR"] = "NotFoundError";
|
|
1412
1412
|
BROWSER_CAMERA_ERRORS["NOT_READABLE_ERROR"] = "NotReadableError";
|
|
1413
1413
|
BROWSER_CAMERA_ERRORS["ABORT_ERROR"] = "AbortError";
|
|
1414
|
+
BROWSER_CAMERA_ERRORS["LOW_RESOLUTION"] = "LowResolutionError";
|
|
1414
1415
|
})(BROWSER_CAMERA_ERRORS || (BROWSER_CAMERA_ERRORS = {}));
|
|
1415
1416
|
let stream;
|
|
1416
1417
|
const videoConstraintsFactory = (isPC, facingMode) => {
|
|
@@ -1438,50 +1439,6 @@ const videoConstraintsFactory = (isPC, facingMode) => {
|
|
|
1438
1439
|
}
|
|
1439
1440
|
};
|
|
1440
1441
|
};
|
|
1441
|
-
/**
|
|
1442
|
-
* 等待camera完全就緒
|
|
1443
|
-
* 用於解決外接camera初始化延遲問題
|
|
1444
|
-
* @param videoTrack - MediaStreamTrack 實例
|
|
1445
|
-
* @param maxWaitMs - 最大等待時間(毫秒)
|
|
1446
|
-
* @returns Promise<boolean> - true表示camera已就緒,false表示超時
|
|
1447
|
-
*/
|
|
1448
|
-
function waitForCameraReady(videoTrack, maxWaitMs = 100) {
|
|
1449
|
-
var _a, _b, _c, _d;
|
|
1450
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1451
|
-
const startTime = Date.now();
|
|
1452
|
-
const checkInterval = 50; // 每50ms檢查一次
|
|
1453
|
-
const minStableTime = 50; // 最小穩定時間:即使檢測到就緒,也要等待一小段時間
|
|
1454
|
-
while (Date.now() - startTime < maxWaitMs) {
|
|
1455
|
-
const _settings = videoTrack.getSettings();
|
|
1456
|
-
const capabilities = (_a = videoTrack.getCapabilities) === null || _a === void 0 ? void 0 : _a.call(videoTrack);
|
|
1457
|
-
// 檢查camera是否已提供完整資訊
|
|
1458
|
-
// 1. 必須有基本的寬高設定
|
|
1459
|
-
// 2. 如果支援 getCapabilities,確認有最大解析度資訊
|
|
1460
|
-
const hasBasicSettings = _settings.width && _settings.height;
|
|
1461
|
-
const hasCapabilities = !capabilities || ((_b = capabilities === null || capabilities === void 0 ? void 0 : capabilities.width) === null || _b === void 0 ? void 0 : _b.max) && ((_c = capabilities === null || capabilities === void 0 ? void 0 : capabilities.height) === null || _c === void 0 ? void 0 : _c.max);
|
|
1462
|
-
if (hasBasicSettings && hasCapabilities) {
|
|
1463
|
-
const elapsedTime = Date.now() - startTime;
|
|
1464
|
-
// ⚠️ 關鍵修正:即使檢測到就緒,也要確保經過最小等待時間
|
|
1465
|
-
// 這對外接相機特別重要,它們需要額外的硬體初始化時間
|
|
1466
|
-
if (elapsedTime < minStableTime) {
|
|
1467
|
-
const remainingTime = minStableTime - elapsedTime;
|
|
1468
|
-
console.log(`⏳ Camera基本就緒,額外等待 ${remainingTime}ms 確保穩定 (當前: ${_settings.width}x${_settings.height})`);
|
|
1469
|
-
yield new Promise(resolve => setTimeout(resolve, remainingTime));
|
|
1470
|
-
}
|
|
1471
|
-
console.log(`✅ Camera就緒 (耗時: ${Date.now() - startTime}ms, 解析度: ${_settings.width}x${_settings.height})`);
|
|
1472
|
-
return true;
|
|
1473
|
-
}
|
|
1474
|
-
yield new Promise(resolve => setTimeout(resolve, checkInterval));
|
|
1475
|
-
}
|
|
1476
|
-
const settings = videoTrack.getSettings();
|
|
1477
|
-
console.warn(`⚠️ Camera未在時限內完全就緒 (${maxWaitMs}ms), 當前狀態:`, {
|
|
1478
|
-
width: settings.width,
|
|
1479
|
-
height: settings.height,
|
|
1480
|
-
hasCapabilities: !!((_d = videoTrack.getCapabilities) === null || _d === void 0 ? void 0 : _d.call(videoTrack))
|
|
1481
|
-
});
|
|
1482
|
-
return false;
|
|
1483
|
-
});
|
|
1484
|
-
}
|
|
1485
1442
|
function inferFacingModeFromLabel(label) {
|
|
1486
1443
|
const pattern = /rear|back|rück|arrière|trasera|trás|traseira|posteriore|后|後|背|задней|الخلفية|후|arka|achterzijde|หลัง|baksidan|bagside|sau|bak|tylny|takakamera|belakang|אחורית|πίσω|spate|hátsó|zadní|darrere|zadná|задня|stražnja|belakang|बैक/i;
|
|
1487
1444
|
if (pattern.test(label !== null && label !== void 0 ? label : '')) {
|
|
@@ -1535,8 +1492,53 @@ function arrayFromAsync(asyncIterable) {
|
|
|
1535
1492
|
return result;
|
|
1536
1493
|
});
|
|
1537
1494
|
}
|
|
1538
|
-
function
|
|
1539
|
-
var _a
|
|
1495
|
+
function upgradeResolution(videoTrack) {
|
|
1496
|
+
var _a;
|
|
1497
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1498
|
+
const targetConstraints = {
|
|
1499
|
+
width: {
|
|
1500
|
+
ideal: 1920,
|
|
1501
|
+
max: 3840
|
|
1502
|
+
},
|
|
1503
|
+
height: {
|
|
1504
|
+
ideal: 1080,
|
|
1505
|
+
max: 2160
|
|
1506
|
+
}
|
|
1507
|
+
};
|
|
1508
|
+
const minAcceptableWidth = 1280; // 最低可接受的解析度寬度
|
|
1509
|
+
const maxAttempts = 5; // 最多嘗試10次
|
|
1510
|
+
const retryDelay = 2000; // 每次重試間隔1秒
|
|
1511
|
+
console.log('🚀 開始提升解析度...');
|
|
1512
|
+
const startTime = Date.now();
|
|
1513
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
1514
|
+
try {
|
|
1515
|
+
yield videoTrack.applyConstraints(targetConstraints);
|
|
1516
|
+
const settings = videoTrack.getSettings();
|
|
1517
|
+
const currentWidth = (_a = settings.width) !== null && _a !== void 0 ? _a : 0;
|
|
1518
|
+
console.log(`嘗試 ${attempt}/${maxAttempts} - 解析度: ${currentWidth}x${settings.height}`);
|
|
1519
|
+
// 檢查是否達到可接受的解析度
|
|
1520
|
+
if (currentWidth >= minAcceptableWidth) {
|
|
1521
|
+
const elapsedTime = Date.now() - startTime;
|
|
1522
|
+
console.log(`✅ 解析度提升成功!最終: ${currentWidth}x${settings.height} (耗時: ${elapsedTime}ms)`);
|
|
1523
|
+
return; // 成功,退出
|
|
1524
|
+
}
|
|
1525
|
+
// 解析度還不夠,繼續重試
|
|
1526
|
+
console.log(`⚠️ 解析度 ${currentWidth} < ${minAcceptableWidth},繼續重試...`);
|
|
1527
|
+
} catch (e) {
|
|
1528
|
+
console.warn(`⚠️ 第 ${attempt} 次嘗試失敗:`, e);
|
|
1529
|
+
}
|
|
1530
|
+
// 如果不是最後一次嘗試,等待後重試
|
|
1531
|
+
if (attempt < maxAttempts) {
|
|
1532
|
+
yield new Promise(resolve => setTimeout(resolve, retryDelay));
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
// 所有嘗試都失敗
|
|
1536
|
+
const finalSettings = videoTrack.getSettings();
|
|
1537
|
+
console.warn(`⚠️ 達到最大重試次數,使用當前解析度: ${finalSettings.width}x${finalSettings.height}`);
|
|
1538
|
+
});
|
|
1539
|
+
}
|
|
1540
|
+
function switchCamera(deviceId, video, translate) {
|
|
1541
|
+
var _a, _b;
|
|
1540
1542
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1541
1543
|
const startTime = performance.now();
|
|
1542
1544
|
try {
|
|
@@ -1552,103 +1554,39 @@ function switchCamera(deviceId, video) {
|
|
|
1552
1554
|
};
|
|
1553
1555
|
const constraint = localStorage.getItem('camera_constraint');
|
|
1554
1556
|
stream = yield navigator.mediaDevices.getUserMedia(basicConstraints);
|
|
1555
|
-
// 第二階段:等待就緒策略 + 漸進式重試提升解析度
|
|
1556
1557
|
if (!constraint && stream) {
|
|
1557
1558
|
const videoTrack = stream.getVideoTracks()[0];
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
const beforeSettings = videoTrack.getSettings();
|
|
1582
|
-
yield videoTrack.applyConstraints(targetConstraints);
|
|
1583
|
-
// ⚠️ 關鍵:驗證解析度是否真的改變
|
|
1584
|
-
const afterSettings = videoTrack.getSettings();
|
|
1585
|
-
const afterWidth = (_a = afterSettings.width) !== null && _a !== void 0 ? _a : 0;
|
|
1586
|
-
const beforeWidth = (_b = beforeSettings.width) !== null && _b !== void 0 ? _b : 0;
|
|
1587
|
-
// 成功條件:
|
|
1588
|
-
// 1. 如果初始解析度 < 最低標準 → 必須提升且達到最低標準
|
|
1589
|
-
// 2. 如果初始解析度 >= 最低標準 → 只要有提升就算成功
|
|
1590
|
-
const needsImprovement = beforeWidth < minAcceptableWidth;
|
|
1591
|
-
const resolutionImproved = needsImprovement ? afterWidth > beforeWidth && afterWidth >= minAcceptableWidth : afterWidth > beforeWidth;
|
|
1592
|
-
if (resolutionImproved) {
|
|
1593
|
-
console.log(`✅ 解析度提升成功 (嘗試 ${attempt + 1}/${maxRetries})`, {
|
|
1594
|
-
before: `${beforeWidth}x${beforeSettings.height}`,
|
|
1595
|
-
after: `${afterWidth}x${afterSettings.height}`,
|
|
1596
|
-
deviceLabel: videoTrack.label
|
|
1597
|
-
});
|
|
1598
|
-
break; // 真正成功才跳出
|
|
1599
|
-
} else {
|
|
1600
|
-
// applyConstraints 沒報錯,但解析度沒變 → 視為失敗,觸發重試
|
|
1601
|
-
console.warn(`⚠️ 解析度未改善 (嘗試 ${attempt + 1}/${maxRetries})`, {
|
|
1602
|
-
before: `${beforeWidth}x${beforeSettings.height}`,
|
|
1603
|
-
after: `${afterWidth}x${afterSettings.height}`,
|
|
1604
|
-
expected: needsImprovement ? `>= ${minAcceptableWidth}` : '任何提升'
|
|
1605
|
-
});
|
|
1606
|
-
if (attempt === maxRetries - 1) {
|
|
1607
|
-
console.warn(`⚠️ 達到最大重試次數,使用當前解析度 (${afterWidth}x${afterSettings.height})`);
|
|
1608
|
-
} else {
|
|
1609
|
-
// 繼續重試
|
|
1610
|
-
continue;
|
|
1611
|
-
}
|
|
1612
|
-
}
|
|
1613
|
-
} catch (error) {
|
|
1614
|
-
if (attempt === maxRetries - 1) {
|
|
1615
|
-
// 最後一次嘗試失敗,記錄警告但不影響功能
|
|
1616
|
-
const settings = videoTrack.getSettings();
|
|
1617
|
-
console.warn(`⚠️ 無法提升解析度,使用基本設定 (${settings.width}x${settings.height})`, error);
|
|
1618
|
-
} else {
|
|
1619
|
-
console.log(`⏳ 解析度提升嘗試 ${attempt + 1} 失敗,準備重試...`, error);
|
|
1620
|
-
}
|
|
1621
|
-
}
|
|
1622
|
-
}
|
|
1623
|
-
} else {
|
|
1624
|
-
// Camera未完全就緒,但仍嘗試套用約束
|
|
1625
|
-
console.warn('⚠️ Camera未完全就緒,嘗試套用解析度約束...');
|
|
1626
|
-
try {
|
|
1627
|
-
yield videoTrack.applyConstraints({
|
|
1628
|
-
width: {
|
|
1629
|
-
ideal: 1920,
|
|
1630
|
-
max: 3840
|
|
1631
|
-
},
|
|
1632
|
-
height: {
|
|
1633
|
-
ideal: 1080,
|
|
1634
|
-
max: 2160
|
|
1635
|
-
}
|
|
1636
|
-
});
|
|
1637
|
-
} catch (error) {
|
|
1638
|
-
console.warn('⚠️ 解析度提升失敗,使用預設解析度', error);
|
|
1639
|
-
}
|
|
1640
|
-
}
|
|
1641
|
-
// 🆕 記錄最終解析度資訊
|
|
1642
|
-
const finalSettings = videoTrack.getSettings();
|
|
1643
|
-
const initTime = (performance.now() - startTime).toFixed(2);
|
|
1644
|
-
console.log('📹 Camera初始化完成', {
|
|
1645
|
-
deviceId,
|
|
1646
|
-
deviceLabel: videoTrack.label,
|
|
1647
|
-
resolution: `${finalSettings.width}x${finalSettings.height}`,
|
|
1648
|
-
frameRate: finalSettings.frameRate,
|
|
1649
|
-
facingMode: finalSettings.facingMode,
|
|
1650
|
-
initTime: `${initTime}ms`
|
|
1559
|
+
yield upgradeResolution(videoTrack); // ✅ 加上 await 等待解析度提升完成
|
|
1560
|
+
// 🆕 記錄最終解析度資訊
|
|
1561
|
+
const finalSettings = videoTrack.getSettings();
|
|
1562
|
+
const initTime = (performance.now() - startTime).toFixed(2);
|
|
1563
|
+
console.log('📹 Camera初始化完成', {
|
|
1564
|
+
deviceId,
|
|
1565
|
+
deviceLabel: videoTrack.label,
|
|
1566
|
+
resolution: `${finalSettings.width}x${finalSettings.height}`,
|
|
1567
|
+
frameRate: finalSettings.frameRate,
|
|
1568
|
+
facingMode: finalSettings.facingMode,
|
|
1569
|
+
initTime: `${initTime}ms`
|
|
1570
|
+
});
|
|
1571
|
+
}
|
|
1572
|
+
// 檢查最終解析度是否達到最低要求
|
|
1573
|
+
const currentTrack = stream.getVideoTracks()[0];
|
|
1574
|
+
if (currentTrack) {
|
|
1575
|
+
const currentSettings = currentTrack.getSettings();
|
|
1576
|
+
const minRequiredWidth = 1280;
|
|
1577
|
+
if (currentSettings.width && currentSettings.width < minRequiredWidth) {
|
|
1578
|
+
console.error('❌ Camera解析度不足', {
|
|
1579
|
+
current: `${currentSettings.width}x${currentSettings.height}`,
|
|
1580
|
+
required: `${minRequiredWidth}px minimum width`,
|
|
1581
|
+
deviceId
|
|
1651
1582
|
});
|
|
1583
|
+
// 關閉當前相機流
|
|
1584
|
+
stream.getTracks().forEach(track => track.stop());
|
|
1585
|
+
// 顯示彈窗並等待用戶點擊
|
|
1586
|
+
if (translate) {
|
|
1587
|
+
yield asyncShowPopup('相機解析度不足', '相機解析度不足,請重新嘗試或更換裝置', true, '重新嘗試');
|
|
1588
|
+
}
|
|
1589
|
+
throw BROWSER_CAMERA_ERRORS.LOW_RESOLUTION;
|
|
1652
1590
|
}
|
|
1653
1591
|
}
|
|
1654
1592
|
// try {
|
|
@@ -1694,6 +1632,10 @@ function switchCamera(deviceId, video) {
|
|
|
1694
1632
|
video.srcObject = stream;
|
|
1695
1633
|
}, 10);
|
|
1696
1634
|
} catch (e) {
|
|
1635
|
+
// 如果是低解析度錯誤,直接拋出不包裝,讓外層處理重試
|
|
1636
|
+
if (e === BROWSER_CAMERA_ERRORS.LOW_RESOLUTION) {
|
|
1637
|
+
throw e;
|
|
1638
|
+
}
|
|
1697
1639
|
// 🆕 增強錯誤日誌
|
|
1698
1640
|
const initTime = (performance.now() - startTime).toFixed(2);
|
|
1699
1641
|
console.error('❌ Camera切換失敗', {
|
|
@@ -1703,13 +1645,13 @@ function switchCamera(deviceId, video) {
|
|
|
1703
1645
|
errorMessage: e === null || e === void 0 ? void 0 : e.message,
|
|
1704
1646
|
initTime: `${initTime}ms`,
|
|
1705
1647
|
streamExists: !!stream,
|
|
1706
|
-
trackCount: (
|
|
1648
|
+
trackCount: (_b = (_a = stream === null || stream === void 0 ? void 0 : stream.getTracks()) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0
|
|
1707
1649
|
});
|
|
1708
1650
|
throw new AuthmeError(ErrorCode.CAMERA_NOT_SUPPORT, e);
|
|
1709
1651
|
}
|
|
1710
1652
|
});
|
|
1711
1653
|
}
|
|
1712
|
-
function _requestCamera(video, facingMode) {
|
|
1654
|
+
function _requestCamera(video, facingMode, translate) {
|
|
1713
1655
|
var _a, _b, _c, _d, _e, _f;
|
|
1714
1656
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1715
1657
|
const requestStartTime = performance.now();
|
|
@@ -1808,7 +1750,28 @@ function _requestCamera(video, facingMode) {
|
|
|
1808
1750
|
if (stream) {
|
|
1809
1751
|
stream.getTracks().forEach(track => track.stop());
|
|
1810
1752
|
}
|
|
1811
|
-
|
|
1753
|
+
// 嘗試開啟相機,如果解析度不足會持續重試
|
|
1754
|
+
let retryCount = 0;
|
|
1755
|
+
const MAX_RETRIES = 5;
|
|
1756
|
+
while (retryCount < MAX_RETRIES) {
|
|
1757
|
+
try {
|
|
1758
|
+
yield switchCamera(deviceId, video, translate);
|
|
1759
|
+
break; // 成功就跳出循環
|
|
1760
|
+
} catch (error) {
|
|
1761
|
+
if (error === BROWSER_CAMERA_ERRORS.LOW_RESOLUTION) {
|
|
1762
|
+
retryCount++;
|
|
1763
|
+
if (retryCount >= MAX_RETRIES) {
|
|
1764
|
+
console.error(`❌ 達到最大重試次數 (${MAX_RETRIES}),相機解析度持續不足`);
|
|
1765
|
+
throw error;
|
|
1766
|
+
}
|
|
1767
|
+
console.log(`🔄 解析度不足,第 ${retryCount} 次重試...`);
|
|
1768
|
+
// 等待一下再重試
|
|
1769
|
+
yield new Promise(resolve => setTimeout(resolve, 500));
|
|
1770
|
+
} else {
|
|
1771
|
+
throw error; // 其他錯誤直接拋出
|
|
1772
|
+
}
|
|
1773
|
+
}
|
|
1774
|
+
}
|
|
1812
1775
|
// 🆕 記錄整個請求流程的總結資訊
|
|
1813
1776
|
const totalTime = (performance.now() - requestStartTime).toFixed(2);
|
|
1814
1777
|
console.log('🎉 Camera請求流程完成', {
|
|
@@ -1856,7 +1819,7 @@ function requestCamera({
|
|
|
1856
1819
|
try {
|
|
1857
1820
|
//for ios 17.4.1 hack, if you call getUserMedia too early will only get not allowed.
|
|
1858
1821
|
yield sleep(1000);
|
|
1859
|
-
return yield _requestCamera(video, facingMode);
|
|
1822
|
+
return yield _requestCamera(video, facingMode, translate);
|
|
1860
1823
|
} catch (error) {
|
|
1861
1824
|
if ((error === null || error === void 0 ? void 0 : error.name) === BROWSER_CAMERA_ERRORS.NOT_ALLOWED_ERROR) {
|
|
1862
1825
|
perm = yield navigator.permissions.query({
|
|
@@ -1867,9 +1830,9 @@ function requestCamera({
|
|
|
1867
1830
|
}
|
|
1868
1831
|
}
|
|
1869
1832
|
}
|
|
1870
|
-
return yield _requestCamera(video, facingMode);
|
|
1833
|
+
return yield _requestCamera(video, facingMode, translate);
|
|
1871
1834
|
} else {
|
|
1872
|
-
return yield _requestCamera(video, facingMode);
|
|
1835
|
+
return yield _requestCamera(video, facingMode, translate);
|
|
1873
1836
|
}
|
|
1874
1837
|
} catch (error) {
|
|
1875
1838
|
if (error === BROWSER_CAMERA_ERRORS.NOT_SUPPORT) {
|
|
@@ -1893,6 +1856,14 @@ function requestCamera({
|
|
|
1893
1856
|
*/
|
|
1894
1857
|
throw new AuthmeError(ErrorCode.CAMERA_NOT_SUPPORT, error);
|
|
1895
1858
|
}
|
|
1859
|
+
if (error === BROWSER_CAMERA_ERRORS.LOW_RESOLUTION) {
|
|
1860
|
+
// 顯示彈窗並等待用戶點擊後重新載入
|
|
1861
|
+
yield asyncShowPopup(translate('sdk.general.error.cameraLowResolution'), translate('sdk.general.error.cameraLowResolution') + ',請重新嘗試或更換裝置', true);
|
|
1862
|
+
// 用戶點擊確定後,重新載入頁面
|
|
1863
|
+
globalThis.location.reload();
|
|
1864
|
+
// 不會執行到這裡,因為頁面已重新載入
|
|
1865
|
+
throw new AuthmeError(ErrorCode.CAMERA_NOT_SUPPORT, error);
|
|
1866
|
+
}
|
|
1896
1867
|
if (isOverconstrainedError(error)) {
|
|
1897
1868
|
showMessage(translate('sdk.general.error.cameraLowResolution'));
|
|
1898
1869
|
throw new AuthmeError(ErrorCode.CAMERA_NOT_SUPPORT, error);
|
|
@@ -3387,8 +3358,8 @@ const themeConfigDefault = {
|
|
|
3387
3358
|
};
|
|
3388
3359
|
|
|
3389
3360
|
var name = "authme/sdk";
|
|
3390
|
-
var version$1 = "2.8.
|
|
3391
|
-
var date = "2025-11-
|
|
3361
|
+
var version$1 = "2.8.33";
|
|
3362
|
+
var date = "2025-11-21T09:00:22+0000";
|
|
3392
3363
|
var packageInfo = {
|
|
3393
3364
|
name: name,
|
|
3394
3365
|
version: version$1,
|
package/package.json
CHANGED
package/src/ui/camera.d.ts
CHANGED
|
@@ -39,7 +39,7 @@ export declare const videoConstraintsFactory: (isPC: boolean, facingMode: 'user'
|
|
|
39
39
|
facingMode: "user" | "environment";
|
|
40
40
|
};
|
|
41
41
|
};
|
|
42
|
-
export declare function switchCamera(deviceId: string, video: HTMLVideoElement): Promise<void>;
|
|
42
|
+
export declare function switchCamera(deviceId: string, video: HTMLVideoElement, translate?: (key: string) => string): Promise<void>;
|
|
43
43
|
export declare function requestCamera({ video, facingMode, translate, showMessage, }: {
|
|
44
44
|
video: HTMLVideoElement;
|
|
45
45
|
facingMode: 'front' | 'back';
|
package/src/ui/popup.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export declare function showPopup(title: string, content: string, showButton?: boolean,
|
|
1
|
+
export declare function showPopup(title: string, content: string, showButton?: boolean, buttonText?: string, callback?: (e: Event) => void): Promise<void>;
|
|
2
2
|
export declare function hidePopup(): void;
|
|
3
|
-
export declare function asyncShowPopup(title: string, content: string, showButton?: boolean,
|
|
3
|
+
export declare function asyncShowPopup(title: string, content: string, showButton?: boolean, buttonText?: string): Promise<boolean>;
|