let selectedDeviceId; let isScanning = false; // Flag to prevent multiple scans at the same time let quaggaInitialized = false; // Flag to check if Quagga has been initialized // Get available video input devices (cameras) async function getCameras() { try { const cameraSelect = document.getElementById('camera-select'); const stream = await navigator.mediaDevices.getUserMedia({ video: true }); // Prompt for camera access const devices = await navigator.mediaDevices.enumerateDevices(); const videoDevices = devices.filter(device => device.kind === 'videoinput'); if (videoDevices.length > 0) { // Clear any existing options cameraSelect.innerHTML = ''; videoDevices.forEach((device, index) => { const option = document.createElement('option'); option.value = device.deviceId; option.text = device.label || `Camera ${index + 1}`; cameraSelect.appendChild(option); }); // Set selectedDeviceId to the first camera by default selectedDeviceId = cameraSelect.value; startScanner(); // Start the scanner with the default camera } else { console.log("No video devices found."); cameraSelect.innerHTML = ''; } // Update selectedDeviceId when user selects a camera cameraSelect.addEventListener('change', function() { selectedDeviceId = this.value; // Stop Quagga and re-initialize with the new deviceId if (quaggaInitialized) { Quagga.stop(); quaggaInitialized = false; } startScanner(); }); } catch (error) { console.error("Error accessing the camera or enumerating devices:", error); document.getElementById('camera-select').innerHTML = ''; } } function startScanner() { if (!selectedDeviceId) { alert('No camera selected or available.'); return; } if (quaggaInitialized) { Quagga.start(); } else { Quagga.init({ inputStream: { name: "Live", type: "LiveStream", target: document.querySelector('#interactive'), constraints: { deviceId: selectedDeviceId, facingMode: "environment", // Remove advanced constraints if not needed }, }, decoder: { readers: ["ean_reader", "ean_8_reader"] } }, function (err) { if (err) { console.log(err); return; } console.log("Initialization finished. Ready to start"); Quagga.start(); quaggaInitialized = true; // Set up the onDetected handler Quagga.onDetected(processBarcode); }); } } async function processBarcode(data) { if (isScanning) return; // Prevent further scans while a scan is being processed isScanning = true; // Set the scanning flag const isbn = data.codeResult.code; console.log("Detected ISBN:", isbn); Quagga.stop(); // Stop the scanner once an ISBN is detected if (document.getElementById('confirm-mode').checked) { // Confirm book in library await confirmBookInLibrary(isbn); } else { // Normal flow await fetchBookInfo(isbn); } isScanning = false; // Reset the scanning flag once processing is done } async function fetchBookInfo(isbn) { try { const response = await fetch(`/book/${isbn}`); const bookData = await response.json(); if (bookData.title) { bookData.isbn2 = isbn; // Add the ISBN to the book data promptUserWithBook(bookData); } else { console.log("No book data found. Restarting scanner..."); startScanner(); // Restart the scanner if no book information is found } } catch (error) { console.error('Error fetching book data:', error); startScanner(); // Restart the scanner on error as well } } function promptUserWithBook(bookData) { // Prepare the information to display in the confirm dialog const title = bookData.title; const authors = bookData.authors ? bookData.authors.join(', ') : 'Unknown Author'; const description = bookData.description || 'No description available'; // Combine the information into a single message const message = `Title: ${title}\nAuthor(s): ${authors}\nDescription: ${description}\n\nDo you want to add this book to the database?`; // Use the built-in confirm prompt to ask the user const userConfirmed = confirm(message); if (userConfirmed) { console.log('User confirmed to add the book to the database.'); storeBookInDatabase(bookData); } else { console.log('User declined to add the book to the database.'); startScanner(); // Restart the scanner if the user declines } } async function storeBookInDatabase(bookData) { try { const response = await fetch('/store-book', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ isbn: bookData.isbn || bookData.isbn2, title: bookData.title, authors: bookData.authors, publishedDate: bookData.publishedDate, description: bookData.description, url: bookData.url, number_of_pages: bookData.number_of_pages, identifiers: bookData.identifiers, publishers: bookData.publishers, subjects: bookData.subjects, notes: bookData.notes, cover_small: bookData.cover_small, cover_medium: bookData.cover_medium, cover_large: bookData.cover_large }) }); const result = await response.json(); if (result.success) { alert('Book added to the database successfully!'); } else { alert('Failed to add the book to the database.'); } } catch (error) { console.error('Error storing book in database:', error); alert('An error occurred while storing the book.'); } finally { startScanner(); // Restart the scanner after storing the book or handling errors } } async function confirmBookInLibrary(isbn) { try { const response = await fetch(`/book/confirm/${isbn}`); const bookData = await response.json(); if (bookData.title) { // Display the book information and a success message const title = bookData.title; const authors = bookData.authors ? bookData.authors.join(', ') : 'Unknown Author'; const description = bookData.description || 'No description available'; const message = `Title: ${title}\nAuthor(s): ${authors}\nDescription: ${description}\n\nBook found in the library!`; alert(message); } else { alert('Book not found in the library.'); } } catch (error) { console.error('Error confirming book in library:', error); alert('An error occurred while confirming the book in the library.'); } finally { startScanner(); // Restart the scanner after processing } } // Add an event listener for the search button document.getElementById('search-title').addEventListener('click', function() { const title = document.getElementById('title-input').value; searchByTitle(title); }); async function searchByTitle(title = '') { if (!title) { alert('Please enter a book title to search.'); return; } try { const response = await fetch(`/search-title?title=${encodeURIComponent(title)}`); const data = await response.json(); if (data.results && data.results.length > 0) { displaySearchResults(data.results); } else { alert('No books found with that title.'); } } catch (error) { console.error('Error searching for book by title:', error); alert('An error occurred while searching for the book.'); } } function displaySearchResults(results) { // Display the search results and allow the user to select one const bookInfoDiv = document.getElementById('book-info'); bookInfoDiv.innerHTML = ''; // Clear previous results results.forEach((book, index) => { const bookElement = document.createElement('div'); bookElement.innerHTML = `

Title: ${book.title}

Author(s): ${book.authors.join(', ')}

Published: ${book.publishDate || 'N/A'}

ISBN: ${book.isbn}

Publisher: ${book.publishers || 'N/A'}

`; bookInfoDiv.appendChild(bookElement); }); // Store the results so that we can reference them when the user makes a selection window.searchResults = results; } // Client-side function to request checkout function requestCheckout(isbn) { fetch(`/api/checkout/${isbn}`, { method: 'POST', headers: { 'Content-Type': 'application/json' } }) .then(response => response.json()) .then(data => { if (data.success) { alert('Checkout request sent successfully.'); } else { alert('Failed to send checkout request.'); } }) .catch(error => { console.error('Error:', error); alert('An error occurred while sending the checkout request.'); }); } function selectBook(index) { const selectedBook = window.searchResults[index]; console.log('Selected book:', selectedBook); // You can now fetch more details using the unique key if needed, or directly store this in your database const isbn = selectedBook.isbn; if (isbn) { fetchBookInfo(isbn); } else { promptUserWithBook(selectedBook); // You might have to adapt this if there's no ISBN } } // Get cameras on page load window.onload = function() { getCameras(); searchByTitle(''); };