let selectedDeviceId; let isScanning = false; // Flag to prevent multiple scans at the same time async function testCameraAccess() { try { const stream = await navigator.mediaDevices.getUserMedia({ video: true }); console.log('Camera access granted', stream); } catch (error) { console.error('Error accessing camera:', error); } } // 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'); // Update selectedDeviceId when user selects a camera cameraSelect.addEventListener('change', function() { selectedDeviceId = this.value; startScanner(); }); if (videoDevices.length > 0) { videoDevices.forEach((device, index) => { const option = document.createElement('option'); option.value = device.deviceId; option.text = device.label || `Camera ${index + 1}`; cameraSelect.appendChild(option); }); } else { console.log("No video devices found."); cameraSelect.innerHTML = ''; } } 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; } // TODO: Limit the field of view to a smaller area for faster detection Quagga.init({ inputStream: { name: "Live", type: "LiveStream", target: document.querySelector('#interactive'), constraints: { deviceId: selectedDeviceId, facingMode: "environment", // Default to rear camera, advanced: [{torch: true}] }, }, decoder: { readers: ["ean_reader", "ean_8_reader"] } }, function (err) { if (err) { console.log(err); return; } console.log("Initialization finished. Ready to start"); Quagga.start(); // TODO: Add a button to enable/disable torch Quagga.CameraAccess.enableTorch(); // TODO: Add a button to enable/disable the "locate" functionality }); Quagga.onDetected(async function (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 // TODO: Validate the ISBN before fetching book details // Use a library like barcode-validator to validate the ISBN // Fetch book details 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(); // TODO: If bookData value of "source" is "local", then the book data is already in the database // TODO: Display the book info and ask if they would like to checkout the book // TODO: Store scanned ISBN in a different field to check against the one we get from the API 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 } } // TODO: Function to prompt user for physical location of the book // Should pull from the database and allow the user to select or add a location // Add an event listener for the search button document.getElementById('search-title').addEventListener('click', searchByTitle); async function searchByTitle() { const title = document.getElementById('title-input').value; 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.publish_date || 'N/A'}

ISBN: ${book.isbn}

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

`; bookInfoDiv.appendChild(bookElement); }); // Store the results so that we can reference them when the user makes a selection window.searchResults = results; } 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 } } 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 } } // Start the scanner when the start button is clicked //document.getElementById('start-scanner').addEventListener('click', startScanner); // Get cameras on page load window.onload = getCameras;