Scanned basically everything with this
This commit is contained in:
47
index.js
47
index.js
@@ -61,14 +61,28 @@ app.get('/book/:isbn', async (req, res) => {
|
|||||||
// First try Open Library
|
// First try Open Library
|
||||||
const openLibraryResponse = await axios.get(`https://openlibrary.org/api/books?bibkeys=ISBN:${isbn}&format=json&jscmd=data`);
|
const openLibraryResponse = await axios.get(`https://openlibrary.org/api/books?bibkeys=ISBN:${isbn}&format=json&jscmd=data`);
|
||||||
const bookData = openLibraryResponse.data[`ISBN:${isbn}`];
|
const bookData = openLibraryResponse.data[`ISBN:${isbn}`];
|
||||||
|
//Check for timeout or rate limit:
|
||||||
|
if(openLibraryResponse.status === 429){
|
||||||
|
console.log('Rate limit exceeded');
|
||||||
|
res.status(429).json({ error: 'Rate limit exceeded' });
|
||||||
|
}
|
||||||
|
|
||||||
|
if(openLibraryResponse.status === 408){
|
||||||
|
console.log('Request Timeout');
|
||||||
|
res.status(408).json({ error: 'Request Timeout' });
|
||||||
|
}
|
||||||
|
|
||||||
if (bookData) {
|
if (bookData) {
|
||||||
console.log('Book data found in Open Library');
|
console.log('Book data found in Open Library');
|
||||||
res.json(formatOpenLibraryData(bookData));
|
res.json(formatOpenLibraryData(bookData));
|
||||||
} else {
|
} else {
|
||||||
|
console.log('Book not found in Open Library');
|
||||||
|
console.log('Trying Internet Archive...');
|
||||||
|
|
||||||
// Fallback to Internet Archive if no data from Open Library
|
// Fallback to Internet Archive if no data from Open Library
|
||||||
const archiveResponse = await axios.get(`https://archive.org/advancedsearch.php?q=isbn:${isbn}&output=json`);
|
const archiveResponse = await axios.get(`https://archive.org/advancedsearch.php?q=isbn:${isbn}&output=json`);
|
||||||
const archiveData = archiveResponse.data;
|
const archiveData = archiveResponse.data;
|
||||||
|
console.log(archiveData);
|
||||||
|
|
||||||
if (archiveData.response.numFound > 0) {
|
if (archiveData.response.numFound > 0) {
|
||||||
res.json(formatArchiveData(archiveData.response.docs[0]));
|
res.json(formatArchiveData(archiveData.response.docs[0]));
|
||||||
@@ -84,6 +98,39 @@ app.get('/book/:isbn', async (req, res) => {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Endpoint to search for book by title
|
||||||
|
app.get('/search-title', async (req, res) => {
|
||||||
|
const { title } = req.query;
|
||||||
|
console.log(`Searching for book by title: ${title}`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Search Open Library by title, limit to 5 results
|
||||||
|
const openLibraryResponse = await axios.get(`https://openlibrary.org/search.json?q=${encodeURIComponent(title)}&limit=7`);
|
||||||
|
const searchResults = openLibraryResponse.data.docs;
|
||||||
|
console.log(searchResults);
|
||||||
|
|
||||||
|
if (searchResults.length > 0) {
|
||||||
|
console.log('Books found by title');
|
||||||
|
const bookData = searchResults.map(result => ({
|
||||||
|
title: result.title,
|
||||||
|
authors: result.author_name || [],
|
||||||
|
publish_date: result.first_publish_year,
|
||||||
|
isbn: result.isbn ? result.isbn[0] : '',
|
||||||
|
publisher: result.publisher ? result.publisher[0] : '',
|
||||||
|
key: result.key // Unique key to fetch more detailed data later if needed
|
||||||
|
}));
|
||||||
|
console.log(bookData);
|
||||||
|
res.json({ results: bookData });
|
||||||
|
} else {
|
||||||
|
res.status(404).json({ error: 'No books found with that title.' });
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
res.status(500).json({ error: 'Failed to search for book by title' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
// Endpoint to store book in the database
|
// Endpoint to store book in the database
|
||||||
// Endpoint to store book in the database
|
// Endpoint to store book in the database
|
||||||
// Endpoint to store book in the database
|
// Endpoint to store book in the database
|
||||||
|
|||||||
@@ -3,20 +3,20 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>ISBN Scanner</title>
|
<title>ISBN Scanner and Title Lookup</title>
|
||||||
<link rel="stylesheet" href="styles.css">
|
<link rel="stylesheet" href="styles.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>Scan a Book ISBN</h1>
|
<h1>Scan a Book ISBN or Search by Title</h1>
|
||||||
<div>
|
<div>
|
||||||
<label for="camera-select">Select Camera:</label>
|
<label for="camera-select">Select Camera:</label>
|
||||||
<select id="camera-select"></select>
|
<select id="camera-select"></select>
|
||||||
<button id="start-scanner">Start Scanner</button>
|
|
||||||
</div>
|
</div>
|
||||||
<div id="interactive" class="viewport"></div>
|
<div id="interactive" class="viewport"></div>
|
||||||
<div>
|
<div>
|
||||||
<h2>Or upload a barcode image</h2>
|
<h2>Or search for a book by title</h2>
|
||||||
<input type="file" id="barcode-input" accept="image/*">
|
<input type="text" id="title-input" placeholder="Enter book title">
|
||||||
|
<button id="search-title">Search by Title</button>
|
||||||
</div>
|
</div>
|
||||||
<div id="book-info"></div>
|
<div id="book-info"></div>
|
||||||
<div id="prompt">
|
<div id="prompt">
|
||||||
@@ -27,11 +27,6 @@
|
|||||||
<button id="confirm">Add to Database</button>
|
<button id="confirm">Add to Database</button>
|
||||||
<button id="edit-title">No, I'll add a title</button>
|
<button id="edit-title">No, I'll add a title</button>
|
||||||
</div>
|
</div>
|
||||||
<div id="title-input">
|
|
||||||
<label for="manual-title">Title:</label>
|
|
||||||
<input type="text" id="manual-title">
|
|
||||||
<button id="save-title">Save</button>
|
|
||||||
</div>
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/@ericblade/quagga2/dist/quagga.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/@ericblade/quagga2/dist/quagga.js"></script>
|
||||||
<script src="script.js"></script>
|
<script src="script.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ async function getCameras() {
|
|||||||
// Update selectedDeviceId when user selects a camera
|
// Update selectedDeviceId when user selects a camera
|
||||||
cameraSelect.addEventListener('change', function() {
|
cameraSelect.addEventListener('change', function() {
|
||||||
selectedDeviceId = this.value;
|
selectedDeviceId = this.value;
|
||||||
|
startScanner();
|
||||||
});
|
});
|
||||||
|
|
||||||
if (videoDevices.length > 0) {
|
if (videoDevices.length > 0) {
|
||||||
@@ -53,11 +54,12 @@ function startScanner() {
|
|||||||
target: document.querySelector('#interactive'),
|
target: document.querySelector('#interactive'),
|
||||||
constraints: {
|
constraints: {
|
||||||
deviceId: selectedDeviceId,
|
deviceId: selectedDeviceId,
|
||||||
facingMode: "environment", // Default to rear camera
|
facingMode: "environment", // Default to rear camera,
|
||||||
|
advanced: [{torch: true}]
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
decoder: {
|
decoder: {
|
||||||
readers: ["ean_reader"] // EAN is the standard format for ISBN-13 barcodes
|
readers: ["ean_reader", "ean_8_reader",
|
||||||
}
|
}
|
||||||
}, function (err) {
|
}, function (err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
@@ -66,6 +68,7 @@ function startScanner() {
|
|||||||
}
|
}
|
||||||
console.log("Initialization finished. Ready to start");
|
console.log("Initialization finished. Ready to start");
|
||||||
Quagga.start();
|
Quagga.start();
|
||||||
|
Quagga.CameraAccess.enableTorch();
|
||||||
});
|
});
|
||||||
|
|
||||||
Quagga.onDetected(async function (data) {
|
Quagga.onDetected(async function (data) {
|
||||||
@@ -122,6 +125,68 @@ function promptUserWithBook(bookData) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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) {
|
async function storeBookInDatabase(bookData) {
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/store-book', {
|
const response = await fetch('/store-book', {
|
||||||
@@ -164,7 +229,7 @@ async function storeBookInDatabase(bookData) {
|
|||||||
|
|
||||||
|
|
||||||
// Start the scanner when the start button is clicked
|
// Start the scanner when the start button is clicked
|
||||||
document.getElementById('start-scanner').addEventListener('click', startScanner);
|
//document.getElementById('start-scanner').addEventListener('click', startScanner);
|
||||||
|
|
||||||
// Get cameras on page load
|
// Get cameras on page load
|
||||||
window.onload = getCameras;
|
window.onload = getCameras;
|
||||||
|
|||||||
@@ -20,10 +20,6 @@ canvas.drawing, canvas.drawingBuffer {
|
|||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#title-input {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#prompt-message {
|
#prompt-message {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user