From 1870130b294bc701acae4bbe001c67f97de600a1 Mon Sep 17 00:00:00 2001 From: BlipRanger <1860540+BlipRanger@users.noreply.github.com> Date: Sun, 25 Aug 2024 00:36:41 -0400 Subject: [PATCH] Working ISBN scan --- books.db | Bin 12288 -> 24576 bytes index.js | 114 ++++++++++++++++++++++++++++++++++++++++------ public/index.html | 5 +- public/script.js | 64 ++++++++++++++++++++++++-- public/styles.css | 9 ++++ 5 files changed, 174 insertions(+), 18 deletions(-) diff --git a/books.db b/books.db index 872abe944d66437b65942a46f7ed7f9a78a2203f..fa364903d8d8ef54f2cf7434f1d9cb1904ef87ab 100644 GIT binary patch literal 24576 zcmeHPOKcoRdLELJY{@1Z??w>z5U4~$CI!z7`!U_qqkz~@BqdVfNHj^u793;H(_Nft zv1e-34>6Rm$Wqo~jl51dB*-Z_tq+S0_ORC+gd76okmKeS_?k;D$t9P3fAwRABo!GVGUzjYkg zn!a@HgAb-2M|Rui;Zq-GSS#dDW8Qa8ew|s_UR&N>W4p_DHrLpRvI~8?$IGaB>y6^v;DIMl9cFi*Nb~!+lX%morIIeX7JWUth?!V{+m|crFZ@(`Qx{4DHa1hn*)D- zFnzT&yLjX3U;OZBX8PqDM>DTJd-3$?(T~777D$A7)?)sO$CN7da!>cMs#K8VyWF@xwZJ7qOlQY@bEk zc0fy+wb8-`b44GuK~7Ss>xInmZ4YD%*5>S`=r3bGpKFc$KXsM1e!#0CB?(%b0B!{gq`? zJ0K4Yfwg5O((Z^yE|tcEQ1p1pUmOl-kvenmF6?5;x|o}IK)y;)PqI>`?vwWe4-=Ub zp$_*wY?)ME3W3Xe)Nzm(-=iH>77y7T=OmIY+DYqdiolgmB%LHIK8ir1XtEuUm@pP% z%m#K8h`tw+y!J&uc0?YX^34SQSBMQJ(6Hk2W=}VKi#0+FYa++8SM{6M4`y`?JjR57iZrRjA`p%=pO9eS$c znW|~Boe`qw)d)3npZ#dZqgi;L?KoYm99oBW0`UDd+ZD0X4I^4ycSvU`pL()95WeVe z2svJ^3MUG@J{I(Dm(%mZGTRb^7)KlgH=z2XqaOLjNVuqp<$lNaNUl;f1U=?WxmojN ztdQ}o=Iz+0+S!!nY;-u_OwLFdTNQ>DfW}=(?CYP~*m*j=*MEo6*k$$zQy6AFaiUF* z_4Sv@>tQ9ihh@!qs5yq@x#dyT5dqb?D_6Gpvnu0mmF;-}wya#af>bfJVQ9HbNo^vm zj?7{rT8o~$sOpWHp_y7`r}|{j$)s?i8qIHAW0PvUx;gXx()`-3n@dMOm_FjyfA8pl z5TXI`vvkF(nX0L*a~qP-p$!G>2t?dT_PfV;zKZ1x&VG!Yzz*CUK47XB2kMkR5E?q6BwT}{(9!!qW|b2@feMWLLDyTa&mAS6KOe&u_B%KmWuSUW8={FoVzxuFmyWO(89=1NcWi> zA=%#QdcFw70D97Vh_l9XVF7_*vdu~4SXB+xY#8R`f}GcwSd#M^$(ARkez!EgI1ee9 z!Irmlw10Fx+4IzzrhpW+Uf<-bNn*_0`Q;$$#=&k^^sv!2Z6C)B?Q@zYr~ceBuGMQw z%}|Zz;wEp&zFJvd)4v+s+;XCf;e<;~Vcd&M67qNTv67H?4oO(`I-XQks?fUFR^k-E zexHmRW(Nx}bn=(r*mUhd1T`2+SeZTOdpM$?ytAoXlegH0h2ZY=v??L)Ta9^mL7ds`7J`r^AZ%wjD64;(1s z|3&=2i2wg)$Nzt#Pk*m8d+YJlrNil$;myx)%pyXDkd8~S(*$92UsTo*DDAUVuje7W z9>Wvfw1a)NQpNA*&u!pYd>fH7>)ZjQnt`xgb206=mGnFHgAijAq3YMM%o zf&?ew30h6pWju8lXFU;sQ1rTCC~D1A<~}#4zh9bFuU}m{x;Xvv)5_7cr?a!OE26&_ zI~XQn1h!B3y!?r#DEfH^sn%7+teY5S-dro|U|fh0Kn(-}PX-7ZM$N2S;DJ`x)swLm z#X?k0F?I736v)09GRo*Xjtn}}2$WjAUYCKm45(qK7Gjr;#@Mh+PB_9Y>$bDcqlf}7 zRkk9k`A~qf$YN^1GdMtL@sO9*h6S0M5tz?@Axso?yT(r z+bKGMJpdL1@#cW<0jNSj9JVm1vQM>o{j-WzZ`>;4|BzmY#t0mti2pCP35-(2|BLuP zp<9ahe-ZyL;{OR?;M>IiCBi@=PAL9=appftSAKc<*KhvwrGLYp#i!sv!GZ4{2Y#}D z84$w<%h#8Vu1_Du@4Yf+Q*0Rkppy4genIfX?kiKvsWj2jK?U zhXQ$5GK=CK2kxA+&DgQSCo!@efUP~Z;iMTg0N3k)8>Te0j?vH}8d^j{=^sWBXhX9v zhnMApp?5=zNJaB{BQdrzi^$RprDh^qWxSf5kpz?I!`Oo@EYazb8^CqJTxm+gEgJw; z2AJ3_m`677Umn5H$coO;XgU7?z)taJ$IxmLOFDq@%`wPB_Dmd7QW}8n0qol{+vwRH zz}@ji_DYzxwjBZ9iy}FoEp$Axp@uzlcprusQY7*s010;E0V=J>z>=5coiD(+=Op(J zquX8J)g-jHl9IYalUh;g6-_O(hx`B;8DPY&*CR`ghfxT(01qZXQ`R+8F>3ncz_zTy z#0OheK^|j%(aeOp=`P;qq9$~ysgR(4YQMFyPB#CAy4sMsosQfpYL8JOl<0GWxEs+?gO zX8l5Du|D_q0)EKuNKsk0g8>hA?EYP3UBEDR#2Hx<$mJsZC~&t9vbADLT{xN5Fv)y3 zRI6r~-(tSA3KP$FRzc49%eUWnw>1CW2RD`uN=L?f8}RYW0zR$QbmEt$>np+$?UVPCO>v5dP3ib#%$AkYICdY(8^@eB zIS{7FKD6QyvKh;HB{&>Ji9-cbm8P2oY} z2;@EV!fn9D*|tDVY!qsWrk$4$x@9Rk_EfoxyREGGj1!r#BbOwlV(ftPR>&76To-w6 z$Qp_$y8{s2KYef!80p`qw2*gzk)Hpv7cW=RypXyM?0HQ!3^ELX^j_&AL5EWkS-_fJ z-)(wM*G694mLOMW6L}A6?f-_7hl36-JVj4X@<7EWUACyp7IiuG@1w_+l2ND+r65VI z8uKFKR$fVZ0>^lRQWC(eE#B|M$m)>?)jASj;Nt8fajK8(KsSgxkd`LaG`wzIWL!pF znM4Fn9FO6vL(UnTd3RFZ97Jg=JVa(6@>)5 zrrjr}poC&WyNJUCp-qPkK;3P)5Z%fENjy;n2U~?#C@fh)D&5HWV^tKhqUc|9{+xP+ zOjC~^a6rD(@uF)uD?#P5k6peGqMjic0NX*-!=UnFs~++*A#f+Nk=2EvME1~)<0t>xXnJMupXvYDm58Lkee#!kh>hDBQ0NM z42SHakS)u+W4OYhX^A*YglUyBm?`%FV^0zbRyneqLlW{N^K}=8ZHRLe_Xs$7g4x$5 zPgx4GPOQn*By72|!o3Jw$;C^%4X;5VEDueRU*UTOZu z{MDs+`sG_ceEA@=evMj9*Wd`Twt49JQW01Sa7^C|aHRxv8yG|9PmqT11$l$8dWVfq zRWj{AQFOEZ$Jjw|52Un_c0A&`slli~L|7UNsTGXmR1H2O{fs2Sj1Xx-Bz6NVfW18* z$}tRK2kLn=q>(KoDx8g+Kine{An6p&oK0^J*LBcM5>T^txW8BFa({sP8=y0JP~7lz zq#BB>ZQiY{uWfFzMPz6~!IYXWbnW+%7#I#bBojg{AQfq}WYz>GKpep4US#(Y&&?z@ zcD+`snU*@avXE7nSXsy_NM+%tPv3%1#kdBa%4JBE{zcPBl9XX`nMOlvD78&nRHElG zyoS3rZZ;x}KnKsBd%z@|I~Q52DF(cBa1Q3pw}#D^kdr!cOGQr0NXC6;O6BhoSjiAB z(NHwSO7F19?zK=Yt8O;Vy4WHk2@tO?vr1HF=NuSg3G6I3BlHvCxd3ntfRWL3r#T(bjME%KTS zvXE6vL7q6YS_fuN8cAznTab=O8#mREw=wlCqGM8KeJREvT}dJsISv5sKobddSTtGI z?1<-aMf9Y>5=bM4M*E8pcb37e1i>gX*QN^@*b}Ksr_V+?Ktd#eCUQJTai=Ri;1R|8 z%lJf=((14owji6qBHo4~-ywy4B%e3sATo-x>SXM$Z)`r=c?5$gV=(=WD-o|mL>V=S zZ=rPa=Jh0h0F9zPzy&xwaAQ{+1m5x0hN4t`&1BqwH%+nH@H8|URbx@bg(+E*F|jwD ztqE`8z*Q?+<6GUrk=B@a$*X!=BU$r*bOmc(-$~$8*Oq?zC$s6r1SW*Xq8ki$VMW0; zpWJH1**3U!zq*b8a2biek0XZWi!O?ix%k;b@I*PUp9%g?-BjUbLe~@(pqt!a%eAQM zssB8IDAP@B9da``i6hfBG^3sJ#pDAUI%%>EL^<~ElrJWuYuvGeMKHh)kw1GEt(~&j zNzgpZK{xA=Eu=l8%Vax1!a7~SlU#{|*6(JN?Iom^Ssqi6HzdhRtZ;W1c#c>|KGn(8 zqkjJ6Ntp#z`dN4)x|FkzLl74_#N0YmiEj}D-xt0`n*|k&_$F8J25~iUGO~+{ zi!(MCPQJ$bMS@EK321_}F!Jwb;NK6_u$7 { if (err) { @@ -31,15 +32,26 @@ const db = new sqlite3.Database('./books.db', (err) => { title TEXT, authors TEXT, publishedDate TEXT, - description TEXT + description TEXT, + url TEXT, + number_of_pages INTEGER, + identifiers TEXT, + publishers TEXT, + subjects TEXT, + notes TEXT, + cover_small TEXT, + cover_medium TEXT, + cover_large TEXT ) `); } }); + // Serve static files from the 'public' directory app.use(express.static(path.join(__dirname, 'public'))); +// Endpoint to fetch book details by ISBN // Endpoint to fetch book details by ISBN app.get('/book/:isbn', async (req, res) => { const { isbn } = req.params; @@ -52,7 +64,6 @@ app.get('/book/:isbn', async (req, res) => { if (bookData) { console.log('Book data found in Open Library'); - console.log(bookData); res.json(formatOpenLibraryData(bookData)); } else { // Fallback to Internet Archive if no data from Open Library @@ -72,23 +83,78 @@ app.get('/book/:isbn', async (req, res) => { }); + +// Endpoint to store book in the database +// Endpoint to store book in the database // Endpoint to store book in the database app.post('/store-book', (req, res) => { - const { isbn, title, authors, publishedDate, description } = req.body; + const { + isbn, title, authors, publishedDate, description, url, + number_of_pages, identifiers, publishers, subjects, + notes, cover_small, cover_medium, cover_large + } = req.body; - const query = `INSERT INTO books (isbn, title, authors, publishedDate, description) VALUES (?, ?, ?, ?, ?)`; - const params = [isbn, title, authors ? authors.join(', ') : '', publishedDate, description || '']; - - db.run(query, params, function (err) { + // Check if a book with the same ISBN already exists + const checkQuery = `SELECT * FROM books WHERE isbn = ?`; + db.get(checkQuery, [isbn], (err, row) => { if (err) { - console.error('Error storing book in database:', err); - res.status(500).json({ error: 'Failed to store book in database' }); + console.error('Error checking for existing book:', err); + return res.status(500).json({ error: 'Failed to check for existing book' }); + } + + if (row) { + // Book already exists, update the existing record + const updateQuery = ` + UPDATE books + SET title = ?, authors = ?, publishedDate = ?, description = ?, url = ?, + number_of_pages = ?, identifiers = ?, publishers = ?, subjects = ?, + notes = ?, cover_small = ?, cover_medium = ?, cover_large = ? + WHERE isbn = ? + `; + + const updateParams = [ + title, authors ? authors.join(', ') : '', publishedDate, description || '', + url, number_of_pages, identifiers, publishers, subjects, + notes, cover_small, cover_medium, cover_large, isbn + ]; + + db.run(updateQuery, updateParams, function (err) { + if (err) { + console.error('Error updating book in database:', err); + return res.status(500).json({ error: 'Failed to update book in database' }); + } else { + res.json({ success: true, message: 'Book updated successfully' }); + } + }); } else { - res.json({ success: true, message: 'Book stored successfully', bookId: this.lastID }); + // Book does not exist, insert a new record + const insertQuery = ` + INSERT INTO books ( + isbn, title, authors, publishedDate, description, url, + number_of_pages, identifiers, publishers, subjects, + notes, cover_small, cover_medium, cover_large + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + `; + + const insertParams = [ + isbn, title, authors ? authors.join(', ') : '', publishedDate, description || '', + url, number_of_pages, identifiers, publishers, subjects, + notes, cover_small, cover_medium, cover_large + ]; + + db.run(insertQuery, insertParams, function (err) { + if (err) { + console.error('Error storing book in database:', err); + return res.status(500).json({ error: 'Failed to store book in database' }); + } else { + res.json({ success: true, message: 'Book stored successfully', bookId: this.lastID }); + } + }); } }); }); + function formatOpenLibraryData(data) { return { isbn: data.identifiers.isbn_13 ? data.identifiers.isbn_13[0] : '', @@ -96,16 +162,38 @@ function formatOpenLibraryData(data) { authors: data.authors ? data.authors.map(author => author.name) : [], publishedDate: data.publish_date, description: data.excerpts ? data.excerpts[0].text : 'No description available', + url: data.url, + number_of_pages: data.number_of_pages || null, + identifiers: JSON.stringify(data.identifiers), // Store as JSON string + publishers: data.publishers ? data.publishers.map(pub => pub.name).join(', ') : '', + subjects: data.subjects ? data.subjects.map(sub => sub.name).join(', ') : '', + notes: data.notes || '', + cover_small: data.cover ? data.cover.small : '', + cover_medium: data.cover ? data.cover.medium : '', + cover_large: data.cover ? data.cover.large : '' }; } + function formatArchiveData(data) { return { isbn: data.isbn ? data.isbn[0] : '', title: data.title, - authors: data.creator, - publishedDate: data.date, - description: data.description ? data.description[0] : 'No description available', + authors: data.contributor ? [data.contributor] : [], + publishedDate: data.date ? new Date(data.date).getFullYear() : '', + description: data.description ? data.description.join(' ') : 'No description available', + url: `https://archive.org/details/${data.identifier}`, + number_of_pages: data.imagecount || null, + identifiers: JSON.stringify({ + archive_identifier: data.identifier, + oclc: data['external-identifier'] ? data['external-identifier'].filter(id => id.includes('urn:oclc')).map(id => id.split(':')[2]) : [] + }), + publishers: data.publisher || '', + subjects: data.subject ? data.subject.join(', ') : '', + notes: '', // Archive data does not provide specific notes like Open Library + cover_small: '', // Placeholder, as cover image URLs need to be constructed manually + cover_medium: '', // Same as above + cover_large: '' // Same as above }; } diff --git a/public/index.html b/public/index.html index aaef4a7..22b5eaa 100644 --- a/public/index.html +++ b/public/index.html @@ -21,7 +21,10 @@

- +

+

+

+
diff --git a/public/script.js b/public/script.js index 21dba8a..29bc8f0 100644 --- a/public/script.js +++ b/public/script.js @@ -89,6 +89,7 @@ async function fetchBookInfo(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..."); @@ -101,12 +102,67 @@ async function fetchBookInfo(isbn) { } function promptUserWithBook(bookData) { - // Display book information or prompt the user for confirmation - document.getElementById('book-info').textContent = `Title: ${bookData.title}`; - document.getElementById('prompt').style.display = 'block'; - // Additional logic for user confirmation can be added here + // 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 + } +} + + // Start the scanner when the start button is clicked document.getElementById('start-scanner').addEventListener('click', startScanner); diff --git a/public/styles.css b/public/styles.css index 1a9e32f..8496d2f 100644 --- a/public/styles.css +++ b/public/styles.css @@ -23,3 +23,12 @@ canvas.drawing, canvas.drawingBuffer { #title-input { display: none; } + +#prompt-message { + font-weight: bold; +} + +#book-title, #book-author, #book-desc { + margin-top: 10px; + font-size: 16px; +}