Skip to content

Commit 580b5e5

Browse files
authored
Merge pull request #9 from isaacbock/dev
v1.0.2
2 parents e1b898f + 3f84263 commit 580b5e5

7 files changed

Lines changed: 273 additions & 69 deletions

File tree

README.md

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
# Library Scan
22
<p>
33
<a href="https://chrome.google.com/webstore/detail/mfckggnkebdpaocogfekaaicafooeiik"><img src="https://img.shields.io/chrome-web-store/v/mfckggnkebdpaocogfekaaicafooeiik" alt="Chrome Web Store"></a>
4+
<a href="https://chrome.google.com/webstore/detail/mfckggnkebdpaocogfekaaicafooeiik"><img src="https://img.shields.io/chrome-web-store/users/mfckggnkebdpaocogfekaaicafooeiik?color=blue" alt="Users"></a>
45
<a href="https://chrome.google.com/webstore/detail/mfckggnkebdpaocogfekaaicafooeiik"><img src="https://img.shields.io/badge/books%20scanned-33.4k%2B-blue" alt="Books Scanned"></a>
56
<a href="https://chrome.google.com/webstore/detail/mfckggnkebdpaocogfekaaicafooeiik"><img src="https://img.shields.io/badge/books%20found-29.0k%2B-blue" alt="Books Found"></a>
6-
<a href="https://chrome.google.com/webstore/detail/mfckggnkebdpaocogfekaaicafooeiik"><img src="https://img.shields.io/chrome-web-store/users/mfckggnkebdpaocogfekaaicafooeiik?color=blue" alt="Users"></a>
77
<a href="https://chrome.google.com/webstore/detail/mfckggnkebdpaocogfekaaicafooeiik"><img src="https://img.shields.io/chrome-web-store/stars/mfckggnkebdpaocogfekaaicafooeiik" alt="Rating"></a>
88
<a href="https://www.goodreads.com"><img src="https://img.shields.io/website?down_color=red&label=Goodreads&url=https%3A%2F%2Fwww.goodreads.com%2Fapi" alt="Goodreads Status"></a>
99
<a href="https://www.overdrive.com"><img src="https://img.shields.io/website?down_color=red&label=OverDrive&url=https%3A%2F%2Fwww.overdrive.com%2F" alt="OverDrive Status"></a>
@@ -16,25 +16,32 @@
1616

1717
Scan any OverDrive library for available eBooks & audiobooks from your Goodreads to-read shelf.
1818

19-
* Find to-read titles instantly available for checkout on OverDrive
20-
* Filter by type & availability
19+
* Find titles immediately available for checkout
2120
* Place holds on upcoming books
22-
* Automatic scans of Goodreads & OverDrive daily
21+
* Filter by media type & availability
22+
* Automatic daily scans
2323

2424
*Not affiliated with Goodreads or OverDrive, Inc. All data accessed and interpreted is available publicly online.*
2525

2626
*Google Analytics used to collect extension usage statistics & help improve user experience. To opt-out of Google Analytics tracking, please visit http://tools.google.com/dlpage/gaoptout or set up a filter within an ad blocker tool.*
2727

2828
## Installation
29-
<a href="https://chrome.google.com/webstore/detail/mfckggnkebdpaocogfekaaicafooeiik" target="_blank" rel="noopener"><img src="https://github.com/isaactbock/library-scan/blob/master/media/Chrome%20Web%20Store.png?raw=true" height=100 alt="Available in the Chrome Web Store"></a>
29+
<a href="https://chrome.google.com/webstore/detail/mfckggnkebdpaocogfekaaicafooeiik" target="_blank" rel="noopener"><img src="https://github.com/isaacbock/library-scan/blob/master/media/Chrome%20Web%20Store.png?raw=true" height=100 alt="Available in the Chrome Web Store"></a>
3030
## Permissions
31-
Library Scan only fetches data from public Goodreads profiles and OverDrive library pages.
31+
Library Scan only fetches data from public Goodreads profiles and OverDrive library pages. Tutorials and help available at isaacbock.com/library-scan.
3232
```
3333
https://www.goodreads.com/*
3434
https://*.overdrive.com/*
35+
https://isaacbock.com/library-scan
3536
```
3637

3738
## Changelog
39+
* Version 1.0.2 (05/22/21)
40+
* Improved book matching algorithm
41+
* Improved setup instructions & resources
42+
* Enhanced versioning analytics
43+
* Bug fix: accurate badge counts
44+
* Bug fix: limit auto refreshing after errors
3845
* Version 1.0.1 (07/26/20)
3946
* Improved analytics
4047
* Version 1.0.0 (07/25/20)

background/background.js

Lines changed: 116 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,16 @@
33
*/
44
let currently_scanning = false;
55

6+
/**
7+
* @type {number} Refresh wait duration in minutes -- defaults to refreshing every 24 hours
8+
*/
9+
let refreshWait = 24*60;
10+
11+
/**
12+
* @type {number} Initialize errorTimeout to prevent data from continually over-refreshing after any errors
13+
*/
14+
let errorTimeout = 0;
15+
616
// Initialize repeated time-since-refresh check
717
getElapsedTimeLoop();
818

@@ -24,15 +34,27 @@ _gaq.push(['_setAccount', _AnalyticsCode]);
2434
*/
2535
let carousel_message_timer;
2636

27-
// Recieve messages from popup.js (front-end) and respond with data
37+
// Install & uninstall pages, manifest version tracking
38+
chrome.runtime.onInstalled.addListener(function (details) {
39+
if (details.reason === "install") {
40+
chrome.tabs.create({
41+
url: "https://isaacbock.com/library-scan#start"
42+
});
43+
chrome.runtime.setUninstallURL('https://isaacbock.com/library-scan-uninstall');
44+
_gaq.push(['_trackEvent', 'Version', 'installed', chrome.app.getDetails().version]);
45+
} else if (details.reason === "update") {
46+
_gaq.push(['_trackEvent', 'Version', 'updated', chrome.app.getDetails().version]);
47+
chrome.runtime.setUninstallURL('https://isaacbock.com/library-scan-uninstall');
48+
}
49+
});
50+
51+
// Receive messages from popup.js (front-end) and respond with data
2852
chrome.runtime.onMessage.addListener(
2953
function(request, sender, sendResponse) {
3054
// Request to refresh book data
3155
if (request.msg === "goodreads" && !currently_scanning) {
3256
// Begin by refreshing all titles from Goodreads (which will then progress to identifying these title on OverDrive)
3357
queryGoodreads(request.goodreadsID, request.overdriveURL);
34-
// Reset available book badge count to zero
35-
updateBadgeCount(0);
3658
// Update Loading view carousel messages every 10 seconds
3759
let carousel_messages=["We'll scan the most recent 200 books on your Goodreads To-Read shelf to find titles already available at your local OverDrive library.","We'll automatically refresh your library every 24 hours so titles are always up-to-date.","Toggle between eBooks and Audiobooks to find exactly what you're looking for."];
3860
let carousel_position=1;
@@ -48,6 +70,7 @@ chrome.runtime.onMessage.addListener(
4870
// Get time since last refresh
4971
else if (request.msg === "elapsedTime") {
5072
getElapsedTime();
73+
5174
}
5275
// Update available book badge count as specified
5376
else if (request.msg === "badgeCount") {
@@ -75,18 +98,20 @@ function getElapsedTimeLoop() {
7598
*/
7699
function getElapsedTime() {
77100
// retrieve data from Chrome local storage
78-
chrome.storage.local.get(['LastRun', 'goodreadsID', 'overdriveURL'], async function(result) {
101+
chrome.storage.local.get(['LastRun', 'goodreadsID', 'overdriveURL', 'error', 'count'], async function(result) {
79102
let lastRunTime = await result.LastRun;
80103
let goodreadsID = await result.goodreadsID;
81104
let overdriveURL = await result.overdriveURL;
82-
if (lastRunTime!=undefined) {
105+
let error = await result.error;
106+
let count = await result.count;
107+
if (typeof lastRunTime!=='undefined' && typeof goodreadsID!=='undefined' && typeof overdriveURL!=='undefined') {
83108
// calculate time (in minutes) since last refresh
84109
let lastRun = new Date(lastRunTime);
85110
let currentTime = new Date();
86111
let elapsedTime = Math.floor((currentTime - lastRun) / 60000);
87112
console.log("Elapsed Time Since Last Refresh: " + elapsedTime + " min");
88-
// if more than 24 hrs have passed, data is outdated; trigger refresh
89-
if (elapsedTime > 60*24 && !currently_scanning) {
113+
// if more than refreshWait minutes have passed, data is outdated; trigger refresh
114+
if (elapsedTime > refreshWait && !currently_scanning) {
90115
_gaq.push(['_trackEvent', 'Data', 'refreshed', 'automatically']);
91116
queryGoodreads(goodreadsID, overdriveURL);
92117
elapsedTime = 0;
@@ -112,6 +137,15 @@ function getElapsedTime() {
112137
time: Math.floor(elapsedTime/60) + " hr ago"
113138
});
114139
}
140+
// auto add badge count (in case of extension update or reset, which eliminates badge)
141+
if (typeof error!=='undefined' && typeof count!=='undefined') {
142+
if (error=="None") {
143+
updateBadgeCount(count);
144+
}
145+
else {
146+
updateBadgeCount(0, true);
147+
}
148+
}
115149
}
116150
});
117151
}
@@ -124,6 +158,9 @@ function getElapsedTime() {
124158
*/
125159
function queryGoodreads(goodreadsID, overdriveURL) {
126160
currently_scanning = true;
161+
// set badge to searching icon
162+
chrome.browserAction.setBadgeBackgroundColor({ color: [128, 128, 128, 255] });
163+
chrome.browserAction.setBadgeText({text: '⟳'});
127164
// fetch data using Goodreads API
128165
fetch('https://www.goodreads.com/review/list?v=2&id='+goodreadsID+'&shelf=to-read&sort=position&order=d&per_page=200&key='+apiKeys.goodreads, {
129166
method: "GET",
@@ -172,6 +209,19 @@ function queryGoodreads(goodreadsID, overdriveURL) {
172209
currently_scanning = false;
173210
clearInterval(carousel_message_timer);
174211
_gaq.push(['_trackEvent', 'Goodreads', 'fetched', 'failed']);
212+
updateBadgeCount(0, true);
213+
214+
// save error state
215+
chrome.storage.local.set({'error': "Goodreads", 'count': 0});
216+
217+
// Double error timeout upon each repeated error to prevent over-refreshing
218+
errorTimeout = errorTimeout>0 ? errorTimeout*2 : 1;
219+
// Adjust last run time to incorporate error timeout & save to Chrome local storage
220+
let last_run_time = new Date();
221+
last_run_time.setMinutes( last_run_time.getMinutes() - refreshWait + errorTimeout );
222+
last_run_time = last_run_time.toJSON();
223+
chrome.storage.local.set({'LastRun': last_run_time});
224+
175225
chrome.runtime.sendMessage({
176226
msg: "GoodreadsError",
177227
});
@@ -190,6 +240,9 @@ async function queryOverdrive(ToRead, overdriveURL) {
190240
currently_scanning = true;
191241
let available_count = 0;
192242
let unavailable_count = 0;
243+
// set badge to searching icon
244+
chrome.browserAction.setBadgeBackgroundColor({ color: [128, 128, 128, 255] });
245+
chrome.browserAction.setBadgeText({text: '⟳'});
193246
console.log("Scanning OverDrive for titles:");
194247
// as books are identified on OverDrive, add to BookAvailability array
195248
let BookAvailability = [];
@@ -243,11 +296,12 @@ async function queryOverdrive(ToRead, overdriveURL) {
243296
// if current book was the final title to fetch (OverDrive fetch completed)
244297
if (i===ToRead.length-1) {
245298
// save BookAvailability data to Chrome local storage
246-
chrome.storage.local.set({'BookAvailability': BookAvailability});
299+
chrome.storage.local.set({'BookAvailability': BookAvailability, 'count': available_count});
247300
// save current time (used to calculate time elapsed since last refresh) & save to Chrome local storage
248301
let last_run_time = (new Date()).toJSON();
249302
chrome.storage.local.set({'LastRun': last_run_time});
250303
// notify popup.js (front-end) of completed data refresh
304+
chrome.storage.local.set({'error': "None"});
251305
chrome.runtime.sendMessage({
252306
msg: "Complete",
253307
BookAvailability: BookAvailability
@@ -263,10 +317,36 @@ async function queryOverdrive(ToRead, overdriveURL) {
263317
clearInterval(carousel_message_timer);
264318
// end data refesh
265319
currently_scanning = false;
320+
errorTimeout = 0;
266321
_gaq.push(['_trackEvent', 'OverDrive', 'fetched', 'success', BookAvailability.length]);
267322
_gaq.push(['_trackEvent', 'OverDrive', 'count', 'available', available_count]);
268323
_gaq.push(['_trackEvent', 'OverDrive', 'count', 'hold', unavailable_count]);
324+
325+
// filter eBook and audiobook preferences for accurate badge count
269326
updateBadgeCount(available_count);
327+
chrome.storage.local.get(['ebook_toggle', 'audiobook_toggle'], async function(result) {
328+
let ebookToggle = await result.ebook_toggle;
329+
let audiobookToggle = await result.audiobook_toggle;
330+
if (typeof ebookToggle!=='undefined' && typeof audiobookToggle!=='undefined') {
331+
let Available = [];
332+
for (let i=0; i<BookAvailability.length; i++) {
333+
if (BookAvailability[i].type==="eBook" && ebookToggle){
334+
if (BookAvailability[i].available===true){
335+
Available.push(BookAvailability[i]);
336+
}
337+
}
338+
else if (BookAvailability[i].type==="Audiobook" && audiobookToggle){
339+
if (BookAvailability[i].available===true){
340+
Available.push(BookAvailability[i]);
341+
}
342+
}
343+
}
344+
_gaq.push(['_trackEvent', 'Settings', 'media toggle', 'eBooks', ebookToggle?1:0]);
345+
_gaq.push(['_trackEvent', 'Settings', 'media toggle', 'audiobooks', audiobookToggle?1:0]);
346+
updateBadgeCount(Available.length);
347+
chrome.storage.local.set({'count': Available.length});
348+
}
349+
});
270350
console.log("OverDrive scan complete.");
271351
}
272352
})
@@ -275,6 +355,19 @@ async function queryOverdrive(ToRead, overdriveURL) {
275355
console.log('OverDrive fetch Error ', err);
276356
currently_scanning = false;
277357
clearInterval(carousel_message_timer);
358+
updateBadgeCount(0, true);
359+
360+
// save error state
361+
chrome.storage.local.set({'error': "OverDrive", 'count': 0});
362+
363+
// Double error timeout upon each repeated error to prevent over-refreshing
364+
errorTimeout = errorTimeout>0 ? errorTimeout*2 : 1;
365+
// Adjust last run time to incorporate error timeout & save to Chrome local storage
366+
let last_run_time = new Date();
367+
last_run_time.setMinutes( last_run_time.getMinutes() - refreshWait + errorTimeout );
368+
last_run_time = last_run_time.toJSON();
369+
chrome.storage.local.set({'LastRun': last_run_time});
370+
278371
chrome.runtime.sendMessage({
279372
msg: "OverdriveError",
280373
});
@@ -287,7 +380,7 @@ async function queryOverdrive(ToRead, overdriveURL) {
287380
*
288381
* @param {string} uri Fetch URL
289382
* @param {*} [options={}] Fetch options
290-
* @param {number} [time=10000] Fetch maximum allotted time
383+
* @param {number} [time=60000] Fetch maximum allotted time
291384
* @returns {*} Response from data fetch
292385
*/
293386
async function fetchWithTimeout(uri, options = {}, time=60000) {
@@ -319,16 +412,27 @@ async function fetchWithTimeout(uri, options = {}, time=60000) {
319412
* Update extension badge count to display number of books currently available
320413
*
321414
* @param {number} count Number of books currently available
415+
* @param {boolean} [error=false] Should notification color be set to red? Defaults to false.
322416
*/
323-
function updateBadgeCount(count) {
417+
function updateBadgeCount(count, error = false) {
324418
// default badge to blue background
325-
chrome.browserAction.setBadgeBackgroundColor({ color: [0, 123, 255, 255] });
419+
if (!error) {
420+
chrome.browserAction.setBadgeBackgroundColor({ color: [0, 123, 255, 255] });
421+
}
422+
// else upon error, badge to red background
423+
else {
424+
chrome.browserAction.setBadgeBackgroundColor({ color: [225, 0, 0, 255] });
425+
}
326426
// update badge to display count
327427
if (count!=0) {
328428
chrome.browserAction.setBadgeText({text: count.toString()});
329429
}
330-
// if count equals zero, do not dispay badge
430+
// if count equals zero, do not display badge
331431
else {
332432
chrome.browserAction.setBadgeText({text: ''});
333433
}
434+
// display "!" badge upon error
435+
if (error) {
436+
chrome.browserAction.setBadgeText({text: ' ! '});
437+
}
334438
}

background/install.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Replace download button after install
2+
let downloadButton = document.querySelector('#download');
3+
downloadButton.style.display = 'none';
4+
5+
// Replace download button after install
6+
let downloadedText = document.querySelector('#already-downloaded');
7+
downloadedText.style.display = 'flex';
8+
9+
// Show starting instructions after install
10+
let instructions = document.querySelector('#start');
11+
instructions.style.display = 'block';
12+
// Show starting instructions after install
13+
let instructions_nav_link = document.querySelector('#start_nav_link');
14+
instructions_nav_link.style.display = 'inline';

manifest.json

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"name": "Library Scan: OverDrive + Goodreads",
44
"short_name": "Library Scan",
55
"description": "Scan any OverDrive library for available eBooks & audiobooks from your Goodreads to-read shelf.",
6-
"version": "1.0.1",
6+
"version": "1.0.2",
77
"author": "Isaac B.",
88
"icons": {
99
"16": "icons/icon16.png",
@@ -14,9 +14,20 @@
1414
"default_popup": "popup/popup.html"
1515
},
1616
"background": {
17-
"scripts": ["bootstrap/jquery.min.js","background/api_keys.js","background/xmlToJson.min.js","background/background.js"],
17+
"scripts": [
18+
"bootstrap/jquery.min.js",
19+
"background/api_keys.js",
20+
"background/xmlToJson.min.js",
21+
"background/background.js"
22+
],
1823
"persistent": true
1924
},
25+
"content_scripts": [
26+
{
27+
"matches": ["https://isaacbock.com/library-scan"],
28+
"js": ["background/install.js"]
29+
}
30+
],
2031
"permissions": [
2132
"storage",
2233
"https://www.goodreads.com/*",

popup/popup.css

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,4 +85,44 @@ input:checked + .slider:before {
8585
}
8686
.slider.round:before {
8787
border-radius: 50%;
88+
}
89+
90+
/* Error messages & help */
91+
#goodreads_fail, #overdrive_fail {
92+
display: flex;
93+
flex-direction: column;
94+
}
95+
#goodreads_fail > hr, #overdrive_fail > hr {
96+
width: 100%;
97+
margin-top: 8px !important;
98+
}
99+
.help {
100+
background-color: darkgray;
101+
border-radius: 50%;
102+
color: white;
103+
transition: 500ms;
104+
width: 18px;
105+
height: 18px;
106+
display: inline-block;
107+
text-align: center;
108+
line-height: 18px;
109+
}
110+
.help:hover {
111+
background-color: rgb(92, 92, 92);
112+
color: white;
113+
text-decoration: none;
114+
}
115+
.helpLink {
116+
color: gray;
117+
font-weight: bold;
118+
text-decoration: underline;
119+
transition: 250ms;
120+
}
121+
.helpLink.red {
122+
color: #721c24;
123+
margin-left: auto;
124+
font-weight: normal;
125+
}
126+
.helpLink.red:hover {
127+
color: #411014;
88128
}

0 commit comments

Comments
 (0)