|
|
@ -36,7 +36,7 @@ export class WebInlineQRScanner implements QRScannerService { |
|
|
|
// Generate a short random ID for this scanner instance
|
|
|
|
this.id = Math.random().toString(36).substring(2, 8).toUpperCase(); |
|
|
|
this.options = options ?? {}; |
|
|
|
logger.error( |
|
|
|
logger.debug( |
|
|
|
`[WebInlineQRScanner:${this.id}] Initializing scanner with options:`, |
|
|
|
{ |
|
|
|
...this.options, |
|
|
@ -49,7 +49,7 @@ export class WebInlineQRScanner implements QRScannerService { |
|
|
|
this.context = this.canvas.getContext("2d", { willReadFrequently: true }); |
|
|
|
this.video = document.createElement("video"); |
|
|
|
this.video.setAttribute("playsinline", "true"); // Required for iOS
|
|
|
|
logger.error( |
|
|
|
logger.debug( |
|
|
|
`[WebInlineQRScanner:${this.id}] DOM elements created successfully`, |
|
|
|
); |
|
|
|
} |
|
|
@ -60,7 +60,7 @@ export class WebInlineQRScanner implements QRScannerService { |
|
|
|
this.cameraStateListeners.forEach((listener) => { |
|
|
|
try { |
|
|
|
listener.onStateChange(state, message); |
|
|
|
logger.info( |
|
|
|
logger.debug( |
|
|
|
`[WebInlineQRScanner:${this.id}] Camera state changed to: ${state}`, |
|
|
|
{ |
|
|
|
state, |
|
|
@ -89,7 +89,7 @@ export class WebInlineQRScanner implements QRScannerService { |
|
|
|
async checkPermissions(): Promise<boolean> { |
|
|
|
try { |
|
|
|
this.updateCameraState("initializing", "Checking camera permissions..."); |
|
|
|
logger.error( |
|
|
|
logger.debug( |
|
|
|
`[WebInlineQRScanner:${this.id}] Checking camera permissions...`, |
|
|
|
); |
|
|
|
|
|
|
@ -99,7 +99,7 @@ export class WebInlineQRScanner implements QRScannerService { |
|
|
|
const permissions = await navigator.permissions.query({ |
|
|
|
name: "camera" as PermissionName, |
|
|
|
}); |
|
|
|
logger.error( |
|
|
|
logger.debug( |
|
|
|
`[WebInlineQRScanner:${this.id}] Permission state from Permissions API:`, |
|
|
|
permissions.state, |
|
|
|
); |
|
|
@ -165,7 +165,7 @@ export class WebInlineQRScanner implements QRScannerService { |
|
|
|
"initializing", |
|
|
|
"Requesting camera permissions...", |
|
|
|
); |
|
|
|
logger.error( |
|
|
|
logger.debug( |
|
|
|
`[WebInlineQRScanner:${this.id}] Requesting camera permissions...`, |
|
|
|
); |
|
|
|
|
|
|
@ -175,7 +175,7 @@ export class WebInlineQRScanner implements QRScannerService { |
|
|
|
(device) => device.kind === "videoinput", |
|
|
|
); |
|
|
|
|
|
|
|
logger.error(`[WebInlineQRScanner:${this.id}] Found video devices:`, { |
|
|
|
logger.debug(`[WebInlineQRScanner:${this.id}] Found video devices:`, { |
|
|
|
count: videoDevices.length, |
|
|
|
devices: videoDevices.map((d) => ({ id: d.deviceId, label: d.label })), |
|
|
|
userAgent: navigator.userAgent, |
|
|
@ -188,7 +188,7 @@ export class WebInlineQRScanner implements QRScannerService { |
|
|
|
} |
|
|
|
|
|
|
|
// Try to get a stream with specific constraints
|
|
|
|
logger.error( |
|
|
|
logger.debug( |
|
|
|
`[WebInlineQRScanner:${this.id}] Requesting camera stream with constraints:`, |
|
|
|
{ |
|
|
|
facingMode: "environment", |
|
|
@ -210,7 +210,7 @@ export class WebInlineQRScanner implements QRScannerService { |
|
|
|
|
|
|
|
// Stop the test stream immediately
|
|
|
|
stream.getTracks().forEach((track) => { |
|
|
|
logger.error(`[WebInlineQRScanner:${this.id}] Stopping test track:`, { |
|
|
|
logger.debug(`[WebInlineQRScanner:${this.id}] Stopping test track:`, { |
|
|
|
kind: track.kind, |
|
|
|
label: track.label, |
|
|
|
readyState: track.readyState, |
|
|
@ -275,12 +275,12 @@ export class WebInlineQRScanner implements QRScannerService { |
|
|
|
|
|
|
|
async isSupported(): Promise<boolean> { |
|
|
|
try { |
|
|
|
logger.error( |
|
|
|
logger.debug( |
|
|
|
`[WebInlineQRScanner:${this.id}] Checking browser support...`, |
|
|
|
); |
|
|
|
// Check for secure context first
|
|
|
|
if (!window.isSecureContext) { |
|
|
|
logger.error( |
|
|
|
logger.debug( |
|
|
|
`[WebInlineQRScanner:${this.id}] Camera access requires HTTPS (secure context)`, |
|
|
|
); |
|
|
|
return false; |
|
|
@ -300,7 +300,7 @@ export class WebInlineQRScanner implements QRScannerService { |
|
|
|
(device) => device.kind === "videoinput", |
|
|
|
); |
|
|
|
|
|
|
|
logger.error(`[WebInlineQRScanner:${this.id}] Device support check:`, { |
|
|
|
logger.debug(`[WebInlineQRScanner:${this.id}] Device support check:`, { |
|
|
|
hasSecureContext: window.isSecureContext, |
|
|
|
hasMediaDevices: !!navigator.mediaDevices, |
|
|
|
hasGetUserMedia: !!navigator.mediaDevices?.getUserMedia, |
|
|
@ -379,7 +379,7 @@ export class WebInlineQRScanner implements QRScannerService { |
|
|
|
|
|
|
|
// Log scan attempt every 100 frames or 1 second
|
|
|
|
if (this.scanAttempts % 100 === 0 || timeSinceLastScan >= 1000) { |
|
|
|
logger.error(`[WebInlineQRScanner:${this.id}] Scanning frame:`, { |
|
|
|
logger.debug(`[WebInlineQRScanner:${this.id}] Scanning frame:`, { |
|
|
|
attempt: this.scanAttempts, |
|
|
|
dimensions: { |
|
|
|
width: this.canvas.width, |
|
|
@ -421,7 +421,7 @@ export class WebInlineQRScanner implements QRScannerService { |
|
|
|
!code.data || |
|
|
|
code.data.length === 0; |
|
|
|
|
|
|
|
logger.error(`[WebInlineQRScanner:${this.id}] QR Code detected:`, { |
|
|
|
logger.debug(`[WebInlineQRScanner:${this.id}] QR Code detected:`, { |
|
|
|
data: code.data, |
|
|
|
location: code.location, |
|
|
|
attempts: this.scanAttempts, |
|
|
@ -512,13 +512,13 @@ export class WebInlineQRScanner implements QRScannerService { |
|
|
|
this.scanAttempts = 0; |
|
|
|
this.lastScanTime = Date.now(); |
|
|
|
this.updateCameraState("initializing", "Starting camera..."); |
|
|
|
logger.error( |
|
|
|
logger.debug( |
|
|
|
`[WebInlineQRScanner:${this.id}] Starting scan with options:`, |
|
|
|
this.options, |
|
|
|
); |
|
|
|
|
|
|
|
// Get camera stream with options
|
|
|
|
logger.error( |
|
|
|
logger.debug( |
|
|
|
`[WebInlineQRScanner:${this.id}] Requesting camera stream...`, |
|
|
|
); |
|
|
|
this.stream = await navigator.mediaDevices.getUserMedia({ |
|
|
@ -531,7 +531,7 @@ export class WebInlineQRScanner implements QRScannerService { |
|
|
|
|
|
|
|
this.updateCameraState("active", "Camera is active"); |
|
|
|
|
|
|
|
logger.error(`[WebInlineQRScanner:${this.id}] Camera stream obtained:`, { |
|
|
|
logger.debug(`[WebInlineQRScanner:${this.id}] Camera stream obtained:`, { |
|
|
|
tracks: this.stream.getTracks().map((t) => ({ |
|
|
|
kind: t.kind, |
|
|
|
label: t.label, |
|
|
@ -550,14 +550,14 @@ export class WebInlineQRScanner implements QRScannerService { |
|
|
|
this.video.style.display = "none"; |
|
|
|
} |
|
|
|
await this.video.play(); |
|
|
|
logger.error( |
|
|
|
logger.debug( |
|
|
|
`[WebInlineQRScanner:${this.id}] Video element started playing`, |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
// Emit stream to component
|
|
|
|
this.events.emit("stream", this.stream); |
|
|
|
logger.error(`[WebInlineQRScanner:${this.id}] Stream event emitted`); |
|
|
|
logger.debug(`[WebInlineQRScanner:${this.id}] Stream event emitted`); |
|
|
|
|
|
|
|
// Start QR code scanning
|
|
|
|
this.scanQRCode(); |
|
|
@ -595,7 +595,7 @@ export class WebInlineQRScanner implements QRScannerService { |
|
|
|
} |
|
|
|
|
|
|
|
try { |
|
|
|
logger.error(`[WebInlineQRScanner:${this.id}] Stopping scan`, { |
|
|
|
logger.debug(`[WebInlineQRScanner:${this.id}] Stopping scan`, { |
|
|
|
scanAttempts: this.scanAttempts, |
|
|
|
duration: Date.now() - this.lastScanTime, |
|
|
|
}); |
|
|
@ -604,7 +604,7 @@ export class WebInlineQRScanner implements QRScannerService { |
|
|
|
if (this.animationFrameId !== null) { |
|
|
|
cancelAnimationFrame(this.animationFrameId); |
|
|
|
this.animationFrameId = null; |
|
|
|
logger.error( |
|
|
|
logger.debug( |
|
|
|
`[WebInlineQRScanner:${this.id}] Animation frame cancelled`, |
|
|
|
); |
|
|
|
} |
|
|
@ -613,13 +613,13 @@ export class WebInlineQRScanner implements QRScannerService { |
|
|
|
if (this.video) { |
|
|
|
this.video.pause(); |
|
|
|
this.video.srcObject = null; |
|
|
|
logger.error(`[WebInlineQRScanner:${this.id}] Video element stopped`); |
|
|
|
logger.debug(`[WebInlineQRScanner:${this.id}] Video element stopped`); |
|
|
|
} |
|
|
|
|
|
|
|
// Stop all tracks in the stream
|
|
|
|
if (this.stream) { |
|
|
|
this.stream.getTracks().forEach((track) => { |
|
|
|
logger.error(`[WebInlineQRScanner:${this.id}] Stopping track:`, { |
|
|
|
logger.debug(`[WebInlineQRScanner:${this.id}] Stopping track:`, { |
|
|
|
kind: track.kind, |
|
|
|
label: track.label, |
|
|
|
readyState: track.readyState, |
|
|
@ -631,7 +631,7 @@ export class WebInlineQRScanner implements QRScannerService { |
|
|
|
|
|
|
|
// Emit stream stopped event
|
|
|
|
this.events.emit("stream", null); |
|
|
|
logger.error( |
|
|
|
logger.debug( |
|
|
|
`[WebInlineQRScanner:${this.id}] Stream stopped event emitted`, |
|
|
|
); |
|
|
|
} catch (error) { |
|
|
@ -643,17 +643,17 @@ export class WebInlineQRScanner implements QRScannerService { |
|
|
|
throw error; |
|
|
|
} finally { |
|
|
|
this.isScanning = false; |
|
|
|
logger.error(`[WebInlineQRScanner:${this.id}] Scan stopped successfully`); |
|
|
|
logger.debug(`[WebInlineQRScanner:${this.id}] Scan stopped successfully`); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
addListener(listener: ScanListener): void { |
|
|
|
logger.error(`[WebInlineQRScanner:${this.id}] Adding scan listener`); |
|
|
|
logger.debug(`[WebInlineQRScanner:${this.id}] Adding scan listener`); |
|
|
|
this.scanListener = listener; |
|
|
|
} |
|
|
|
|
|
|
|
onStream(callback: (stream: MediaStream | null) => void): void { |
|
|
|
logger.error( |
|
|
|
logger.debug( |
|
|
|
`[WebInlineQRScanner:${this.id}] Adding stream event listener`, |
|
|
|
); |
|
|
|
this.events.on("stream", callback); |
|
|
@ -661,24 +661,24 @@ export class WebInlineQRScanner implements QRScannerService { |
|
|
|
|
|
|
|
async cleanup(): Promise<void> { |
|
|
|
try { |
|
|
|
logger.error(`[WebInlineQRScanner:${this.id}] Starting cleanup`); |
|
|
|
logger.debug(`[WebInlineQRScanner:${this.id}] Starting cleanup`); |
|
|
|
await this.stopScan(); |
|
|
|
this.events.removeAllListeners(); |
|
|
|
logger.error(`[WebInlineQRScanner:${this.id}] Event listeners removed`); |
|
|
|
logger.debug(`[WebInlineQRScanner:${this.id}] Event listeners removed`); |
|
|
|
|
|
|
|
// Clean up DOM elements
|
|
|
|
if (this.video) { |
|
|
|
this.video.remove(); |
|
|
|
this.video = null; |
|
|
|
logger.error(`[WebInlineQRScanner:${this.id}] Video element removed`); |
|
|
|
logger.debug(`[WebInlineQRScanner:${this.id}] Video element removed`); |
|
|
|
} |
|
|
|
if (this.canvas) { |
|
|
|
this.canvas.remove(); |
|
|
|
this.canvas = null; |
|
|
|
logger.error(`[WebInlineQRScanner:${this.id}] Canvas element removed`); |
|
|
|
logger.debug(`[WebInlineQRScanner:${this.id}] Canvas element removed`); |
|
|
|
} |
|
|
|
this.context = null; |
|
|
|
logger.error( |
|
|
|
logger.debug( |
|
|
|
`[WebInlineQRScanner:${this.id}] Cleanup completed successfully`, |
|
|
|
); |
|
|
|
} catch (error) { |
|
|
|