2024-08-31 11:44:20 -04:00

250 lines
9.0 KiB
JavaScript

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 = '<option>No cameras found</option>';
}
} catch (error) {
console.error("Error accessing the camera or enumerating devices:", error);
document.getElementById('camera-select').innerHTML = '<option>' + error + '</option>';
}
}
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 = `
<p><strong>Title:</strong> ${book.title}</p>
<p><strong>Author(s):</strong> ${book.authors.join(', ')}</p>
<p><strong>Published:</strong> ${book.publish_date || 'N/A'}</p>
<p><strong>ISBN:</strong> ${book.isbn}</p>
<p><strong>Publisher:</strong> ${book.publisher || 'N/A'}</p>
<button onclick="selectBook(${index})">Select this book</button>
`;
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;