@authme/util 2.8.29 → 2.8.31
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 +168 -14
- package/index.js +168 -14
- package/package.json +1 -1
package/index.cjs
CHANGED
|
@@ -27,6 +27,7 @@ var fileSaver = require('file-saver');
|
|
|
27
27
|
var JSZip = require('jszip');
|
|
28
28
|
require('core-js/modules/es.parse-int.js');
|
|
29
29
|
var Lottie = require('lottie-web');
|
|
30
|
+
require('core-js/modules/es.number.to-fixed.js');
|
|
30
31
|
require('core-js/modules/es.array.sort.js');
|
|
31
32
|
require('core-js/modules/es.string.trim.js');
|
|
32
33
|
require('core-js/modules/es.string.starts-with.js');
|
|
@@ -1447,6 +1448,50 @@ const videoConstraintsFactory = (isPC, facingMode) => {
|
|
|
1447
1448
|
}
|
|
1448
1449
|
};
|
|
1449
1450
|
};
|
|
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
|
+
}
|
|
1450
1495
|
function inferFacingModeFromLabel(label) {
|
|
1451
1496
|
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;
|
|
1452
1497
|
if (pattern.test(label !== null && label !== void 0 ? label : '')) {
|
|
@@ -1501,7 +1546,9 @@ function arrayFromAsync(asyncIterable) {
|
|
|
1501
1546
|
});
|
|
1502
1547
|
}
|
|
1503
1548
|
function switchCamera(deviceId, video) {
|
|
1549
|
+
var _a, _b, _c, _d;
|
|
1504
1550
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1551
|
+
const startTime = performance.now();
|
|
1505
1552
|
try {
|
|
1506
1553
|
if (stream) {
|
|
1507
1554
|
stream.getTracks().forEach(track => track.stop());
|
|
@@ -1515,13 +1562,15 @@ function switchCamera(deviceId, video) {
|
|
|
1515
1562
|
};
|
|
1516
1563
|
const constraint = localStorage.getItem('camera_constraint');
|
|
1517
1564
|
stream = yield navigator.mediaDevices.getUserMedia(basicConstraints);
|
|
1518
|
-
//
|
|
1565
|
+
// 第二階段:等待就緒策略 + 漸進式重試提升解析度
|
|
1519
1566
|
if (!constraint && stream) {
|
|
1520
1567
|
const videoTrack = stream.getVideoTracks()[0];
|
|
1521
1568
|
if (videoTrack && videoTrack.applyConstraints) {
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1569
|
+
// 🆕 策略 1: 等待camera完全就緒(特別針對外接camera)
|
|
1570
|
+
const isReady = yield waitForCameraReady(videoTrack, 2000);
|
|
1571
|
+
if (isReady) {
|
|
1572
|
+
// 🆕 策略 2: 漸進式重試邏輯 + 解析度驗證
|
|
1573
|
+
const targetConstraints = {
|
|
1525
1574
|
width: {
|
|
1526
1575
|
ideal: 1920,
|
|
1527
1576
|
max: 3840
|
|
@@ -1530,10 +1579,86 @@ function switchCamera(deviceId, video) {
|
|
|
1530
1579
|
ideal: 1080,
|
|
1531
1580
|
max: 2160
|
|
1532
1581
|
}
|
|
1533
|
-
}
|
|
1534
|
-
|
|
1535
|
-
//
|
|
1582
|
+
};
|
|
1583
|
+
const maxRetries = 3;
|
|
1584
|
+
const delays = [0, 100, 300]; // 第一次立即嘗試,後續間隔遞增
|
|
1585
|
+
const minAcceptableWidth = 1280; // 最低可接受的解析度寬度
|
|
1586
|
+
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
1587
|
+
try {
|
|
1588
|
+
if (delays[attempt] > 0) {
|
|
1589
|
+
yield new Promise(resolve => setTimeout(resolve, delays[attempt]));
|
|
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
|
+
}
|
|
1536
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`
|
|
1661
|
+
});
|
|
1537
1662
|
}
|
|
1538
1663
|
}
|
|
1539
1664
|
// try {
|
|
@@ -1579,12 +1704,28 @@ function switchCamera(deviceId, video) {
|
|
|
1579
1704
|
video.srcObject = stream;
|
|
1580
1705
|
}, 10);
|
|
1581
1706
|
} catch (e) {
|
|
1707
|
+
// 🆕 增強錯誤日誌
|
|
1708
|
+
const initTime = (performance.now() - startTime).toFixed(2);
|
|
1709
|
+
console.error('❌ Camera切換失敗', {
|
|
1710
|
+
deviceId,
|
|
1711
|
+
error: e,
|
|
1712
|
+
errorName: e === null || e === void 0 ? void 0 : e.name,
|
|
1713
|
+
errorMessage: e === null || e === void 0 ? void 0 : e.message,
|
|
1714
|
+
initTime: `${initTime}ms`,
|
|
1715
|
+
streamExists: !!stream,
|
|
1716
|
+
trackCount: (_d = (_c = stream === null || stream === void 0 ? void 0 : stream.getTracks()) === null || _c === void 0 ? void 0 : _c.length) !== null && _d !== void 0 ? _d : 0
|
|
1717
|
+
});
|
|
1582
1718
|
throw new AuthmeError(exports.ErrorCode.CAMERA_NOT_SUPPORT, e);
|
|
1583
1719
|
}
|
|
1584
1720
|
});
|
|
1585
1721
|
}
|
|
1586
1722
|
function _requestCamera(video, facingMode) {
|
|
1723
|
+
var _a, _b, _c, _d, _e, _f;
|
|
1587
1724
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1725
|
+
const requestStartTime = performance.now();
|
|
1726
|
+
console.log('🎬 開始camera請求流程', {
|
|
1727
|
+
facingMode
|
|
1728
|
+
});
|
|
1588
1729
|
if (!('mediaDevices' in navigator)) {
|
|
1589
1730
|
throw BROWSER_CAMERA_ERRORS.NOT_SUPPORT;
|
|
1590
1731
|
}
|
|
@@ -1607,7 +1748,7 @@ function _requestCamera(video, facingMode) {
|
|
|
1607
1748
|
// 輪詢取得攝影機 解析度等資訊
|
|
1608
1749
|
const videoDevices = (yield navigator.mediaDevices.enumerateDevices()).filter(device => device.kind === 'videoinput');
|
|
1609
1750
|
const deviceMetas = yield arrayFromAsync(asyncMap(device => __awaiter(this, void 0, void 0, function* () {
|
|
1610
|
-
var
|
|
1751
|
+
var _g, _h, _j, _k;
|
|
1611
1752
|
const stream = yield navigator.mediaDevices.getUserMedia({
|
|
1612
1753
|
video: {
|
|
1613
1754
|
deviceId: {
|
|
@@ -1615,10 +1756,10 @@ function _requestCamera(video, facingMode) {
|
|
|
1615
1756
|
}
|
|
1616
1757
|
}
|
|
1617
1758
|
});
|
|
1618
|
-
const track = (
|
|
1619
|
-
const capabilities = ((
|
|
1620
|
-
const widthMax = (
|
|
1621
|
-
const heightMax = (
|
|
1759
|
+
const track = (_g = stream.getVideoTracks()) === null || _g === void 0 ? void 0 : _g[0];
|
|
1760
|
+
const capabilities = ((_h = track === null || track === void 0 ? void 0 : track.getCapabilities) !== null && _h !== void 0 ? _h : () => undefined).bind(track)(); // firefox 沒有 getCapabilities 可以用。
|
|
1761
|
+
const widthMax = (_j = capabilities === null || capabilities === void 0 ? void 0 : capabilities.width) === null || _j === void 0 ? void 0 : _j.max;
|
|
1762
|
+
const heightMax = (_k = capabilities === null || capabilities === void 0 ? void 0 : capabilities.height) === null || _k === void 0 ? void 0 : _k.max;
|
|
1622
1763
|
const resolution = widthMax && heightMax ? widthMax * heightMax : 0;
|
|
1623
1764
|
const facingModeFromLabel = inferFacingModeFromLabel(device === null || device === void 0 ? void 0 : device.label);
|
|
1624
1765
|
const facingModeFromapabilities = inferFacingModeFromCapabilities(capabilities);
|
|
@@ -1678,6 +1819,19 @@ function _requestCamera(video, facingMode) {
|
|
|
1678
1819
|
stream.getTracks().forEach(track => track.stop());
|
|
1679
1820
|
}
|
|
1680
1821
|
yield switchCamera(deviceId, video);
|
|
1822
|
+
// 🆕 記錄整個請求流程的總結資訊
|
|
1823
|
+
const totalTime = (performance.now() - requestStartTime).toFixed(2);
|
|
1824
|
+
console.log('🎉 Camera請求流程完成', {
|
|
1825
|
+
totalTime: `${totalTime}ms`,
|
|
1826
|
+
selectedDevice: {
|
|
1827
|
+
deviceId,
|
|
1828
|
+
label: firstDevice.device.label,
|
|
1829
|
+
facingMode: firstDevice.meta.facingMode,
|
|
1830
|
+
resolution: `${(_c = (_b = (_a = firstDevice.meta.capabilities) === null || _a === void 0 ? void 0 : _a.width) === null || _b === void 0 ? void 0 : _b.max) !== null && _c !== void 0 ? _c : 0}x${(_f = (_e = (_d = firstDevice.meta.capabilities) === null || _d === void 0 ? void 0 : _d.height) === null || _e === void 0 ? void 0 : _e.max) !== null && _f !== void 0 ? _f : 0}`
|
|
1831
|
+
},
|
|
1832
|
+
availableDevices: deviceMetas.length,
|
|
1833
|
+
isSamsung
|
|
1834
|
+
});
|
|
1681
1835
|
return {
|
|
1682
1836
|
facingMode: firstDevice.meta.facingMode,
|
|
1683
1837
|
deviceMetas: deviceMetas
|
|
@@ -3243,8 +3397,8 @@ const themeConfigDefault = {
|
|
|
3243
3397
|
};
|
|
3244
3398
|
|
|
3245
3399
|
var name = "authme/sdk";
|
|
3246
|
-
var version$1 = "2.8.
|
|
3247
|
-
var date = "2025-
|
|
3400
|
+
var version$1 = "2.8.31";
|
|
3401
|
+
var date = "2025-11-05T04:02:33+0000";
|
|
3248
3402
|
var packageInfo = {
|
|
3249
3403
|
name: name,
|
|
3250
3404
|
version: version$1,
|
package/index.js
CHANGED
|
@@ -23,6 +23,7 @@ import { saveAs } from 'file-saver';
|
|
|
23
23
|
import JSZip from 'jszip';
|
|
24
24
|
import 'core-js/modules/es.parse-int.js';
|
|
25
25
|
import Lottie from 'lottie-web';
|
|
26
|
+
import 'core-js/modules/es.number.to-fixed.js';
|
|
26
27
|
import 'core-js/modules/es.array.sort.js';
|
|
27
28
|
import 'core-js/modules/es.string.trim.js';
|
|
28
29
|
import 'core-js/modules/es.string.starts-with.js';
|
|
@@ -1437,6 +1438,50 @@ const videoConstraintsFactory = (isPC, facingMode) => {
|
|
|
1437
1438
|
}
|
|
1438
1439
|
};
|
|
1439
1440
|
};
|
|
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
|
+
}
|
|
1440
1485
|
function inferFacingModeFromLabel(label) {
|
|
1441
1486
|
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;
|
|
1442
1487
|
if (pattern.test(label !== null && label !== void 0 ? label : '')) {
|
|
@@ -1491,7 +1536,9 @@ function arrayFromAsync(asyncIterable) {
|
|
|
1491
1536
|
});
|
|
1492
1537
|
}
|
|
1493
1538
|
function switchCamera(deviceId, video) {
|
|
1539
|
+
var _a, _b, _c, _d;
|
|
1494
1540
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1541
|
+
const startTime = performance.now();
|
|
1495
1542
|
try {
|
|
1496
1543
|
if (stream) {
|
|
1497
1544
|
stream.getTracks().forEach(track => track.stop());
|
|
@@ -1505,13 +1552,15 @@ function switchCamera(deviceId, video) {
|
|
|
1505
1552
|
};
|
|
1506
1553
|
const constraint = localStorage.getItem('camera_constraint');
|
|
1507
1554
|
stream = yield navigator.mediaDevices.getUserMedia(basicConstraints);
|
|
1508
|
-
//
|
|
1555
|
+
// 第二階段:等待就緒策略 + 漸進式重試提升解析度
|
|
1509
1556
|
if (!constraint && stream) {
|
|
1510
1557
|
const videoTrack = stream.getVideoTracks()[0];
|
|
1511
1558
|
if (videoTrack && videoTrack.applyConstraints) {
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1559
|
+
// 🆕 策略 1: 等待camera完全就緒(特別針對外接camera)
|
|
1560
|
+
const isReady = yield waitForCameraReady(videoTrack, 2000);
|
|
1561
|
+
if (isReady) {
|
|
1562
|
+
// 🆕 策略 2: 漸進式重試邏輯 + 解析度驗證
|
|
1563
|
+
const targetConstraints = {
|
|
1515
1564
|
width: {
|
|
1516
1565
|
ideal: 1920,
|
|
1517
1566
|
max: 3840
|
|
@@ -1520,10 +1569,86 @@ function switchCamera(deviceId, video) {
|
|
|
1520
1569
|
ideal: 1080,
|
|
1521
1570
|
max: 2160
|
|
1522
1571
|
}
|
|
1523
|
-
}
|
|
1524
|
-
|
|
1525
|
-
//
|
|
1572
|
+
};
|
|
1573
|
+
const maxRetries = 3;
|
|
1574
|
+
const delays = [0, 100, 300]; // 第一次立即嘗試,後續間隔遞增
|
|
1575
|
+
const minAcceptableWidth = 1280; // 最低可接受的解析度寬度
|
|
1576
|
+
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
1577
|
+
try {
|
|
1578
|
+
if (delays[attempt] > 0) {
|
|
1579
|
+
yield new Promise(resolve => setTimeout(resolve, delays[attempt]));
|
|
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
|
+
}
|
|
1526
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`
|
|
1651
|
+
});
|
|
1527
1652
|
}
|
|
1528
1653
|
}
|
|
1529
1654
|
// try {
|
|
@@ -1569,12 +1694,28 @@ function switchCamera(deviceId, video) {
|
|
|
1569
1694
|
video.srcObject = stream;
|
|
1570
1695
|
}, 10);
|
|
1571
1696
|
} catch (e) {
|
|
1697
|
+
// 🆕 增強錯誤日誌
|
|
1698
|
+
const initTime = (performance.now() - startTime).toFixed(2);
|
|
1699
|
+
console.error('❌ Camera切換失敗', {
|
|
1700
|
+
deviceId,
|
|
1701
|
+
error: e,
|
|
1702
|
+
errorName: e === null || e === void 0 ? void 0 : e.name,
|
|
1703
|
+
errorMessage: e === null || e === void 0 ? void 0 : e.message,
|
|
1704
|
+
initTime: `${initTime}ms`,
|
|
1705
|
+
streamExists: !!stream,
|
|
1706
|
+
trackCount: (_d = (_c = stream === null || stream === void 0 ? void 0 : stream.getTracks()) === null || _c === void 0 ? void 0 : _c.length) !== null && _d !== void 0 ? _d : 0
|
|
1707
|
+
});
|
|
1572
1708
|
throw new AuthmeError(ErrorCode.CAMERA_NOT_SUPPORT, e);
|
|
1573
1709
|
}
|
|
1574
1710
|
});
|
|
1575
1711
|
}
|
|
1576
1712
|
function _requestCamera(video, facingMode) {
|
|
1713
|
+
var _a, _b, _c, _d, _e, _f;
|
|
1577
1714
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1715
|
+
const requestStartTime = performance.now();
|
|
1716
|
+
console.log('🎬 開始camera請求流程', {
|
|
1717
|
+
facingMode
|
|
1718
|
+
});
|
|
1578
1719
|
if (!('mediaDevices' in navigator)) {
|
|
1579
1720
|
throw BROWSER_CAMERA_ERRORS.NOT_SUPPORT;
|
|
1580
1721
|
}
|
|
@@ -1597,7 +1738,7 @@ function _requestCamera(video, facingMode) {
|
|
|
1597
1738
|
// 輪詢取得攝影機 解析度等資訊
|
|
1598
1739
|
const videoDevices = (yield navigator.mediaDevices.enumerateDevices()).filter(device => device.kind === 'videoinput');
|
|
1599
1740
|
const deviceMetas = yield arrayFromAsync(asyncMap(device => __awaiter(this, void 0, void 0, function* () {
|
|
1600
|
-
var
|
|
1741
|
+
var _g, _h, _j, _k;
|
|
1601
1742
|
const stream = yield navigator.mediaDevices.getUserMedia({
|
|
1602
1743
|
video: {
|
|
1603
1744
|
deviceId: {
|
|
@@ -1605,10 +1746,10 @@ function _requestCamera(video, facingMode) {
|
|
|
1605
1746
|
}
|
|
1606
1747
|
}
|
|
1607
1748
|
});
|
|
1608
|
-
const track = (
|
|
1609
|
-
const capabilities = ((
|
|
1610
|
-
const widthMax = (
|
|
1611
|
-
const heightMax = (
|
|
1749
|
+
const track = (_g = stream.getVideoTracks()) === null || _g === void 0 ? void 0 : _g[0];
|
|
1750
|
+
const capabilities = ((_h = track === null || track === void 0 ? void 0 : track.getCapabilities) !== null && _h !== void 0 ? _h : () => undefined).bind(track)(); // firefox 沒有 getCapabilities 可以用。
|
|
1751
|
+
const widthMax = (_j = capabilities === null || capabilities === void 0 ? void 0 : capabilities.width) === null || _j === void 0 ? void 0 : _j.max;
|
|
1752
|
+
const heightMax = (_k = capabilities === null || capabilities === void 0 ? void 0 : capabilities.height) === null || _k === void 0 ? void 0 : _k.max;
|
|
1612
1753
|
const resolution = widthMax && heightMax ? widthMax * heightMax : 0;
|
|
1613
1754
|
const facingModeFromLabel = inferFacingModeFromLabel(device === null || device === void 0 ? void 0 : device.label);
|
|
1614
1755
|
const facingModeFromapabilities = inferFacingModeFromCapabilities(capabilities);
|
|
@@ -1668,6 +1809,19 @@ function _requestCamera(video, facingMode) {
|
|
|
1668
1809
|
stream.getTracks().forEach(track => track.stop());
|
|
1669
1810
|
}
|
|
1670
1811
|
yield switchCamera(deviceId, video);
|
|
1812
|
+
// 🆕 記錄整個請求流程的總結資訊
|
|
1813
|
+
const totalTime = (performance.now() - requestStartTime).toFixed(2);
|
|
1814
|
+
console.log('🎉 Camera請求流程完成', {
|
|
1815
|
+
totalTime: `${totalTime}ms`,
|
|
1816
|
+
selectedDevice: {
|
|
1817
|
+
deviceId,
|
|
1818
|
+
label: firstDevice.device.label,
|
|
1819
|
+
facingMode: firstDevice.meta.facingMode,
|
|
1820
|
+
resolution: `${(_c = (_b = (_a = firstDevice.meta.capabilities) === null || _a === void 0 ? void 0 : _a.width) === null || _b === void 0 ? void 0 : _b.max) !== null && _c !== void 0 ? _c : 0}x${(_f = (_e = (_d = firstDevice.meta.capabilities) === null || _d === void 0 ? void 0 : _d.height) === null || _e === void 0 ? void 0 : _e.max) !== null && _f !== void 0 ? _f : 0}`
|
|
1821
|
+
},
|
|
1822
|
+
availableDevices: deviceMetas.length,
|
|
1823
|
+
isSamsung
|
|
1824
|
+
});
|
|
1671
1825
|
return {
|
|
1672
1826
|
facingMode: firstDevice.meta.facingMode,
|
|
1673
1827
|
deviceMetas: deviceMetas
|
|
@@ -3233,8 +3387,8 @@ const themeConfigDefault = {
|
|
|
3233
3387
|
};
|
|
3234
3388
|
|
|
3235
3389
|
var name = "authme/sdk";
|
|
3236
|
-
var version$1 = "2.8.
|
|
3237
|
-
var date = "2025-
|
|
3390
|
+
var version$1 = "2.8.31";
|
|
3391
|
+
var date = "2025-11-05T04:02:33+0000";
|
|
3238
3392
|
var packageInfo = {
|
|
3239
3393
|
name: name,
|
|
3240
3394
|
version: version$1,
|