Adding queue for download.
This commit is contained in:
@@ -22,30 +22,19 @@
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<button id="download-book-button" type="button" class="btn btn-outline-primary">Download Book</button>
|
||||
|
||||
<div class="status">
|
||||
<p id="error-message" class="bg-warning"></p>
|
||||
<div class="loader" id="loading" style="display:none;">
|
||||
<p class="bg-success">
|
||||
<strong>
|
||||
Loading book info
|
||||
<span class="loader__dot">.</span>
|
||||
<span class="loader__dot">.</span>
|
||||
<span class="loader__dot">.</span>
|
||||
</strong>
|
||||
</p>
|
||||
</div>
|
||||
<!-- <button id="add-book-button" type="button" class="btn btn-primary">Add Book</button> -->
|
||||
<button id="download-book-button" type="button" class="btn btn-primary">Download</button>
|
||||
<div id="book-list" class="container">
|
||||
<ul id="book-queue" class="list-group">
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div id="book-info" class="container">
|
||||
<h4 id="book-info-notice"></h4>
|
||||
|
||||
<img id="book-cover" src="" class="img-fluid">
|
||||
<p class="bg-success">
|
||||
<span><strong>Book Name:</strong></span>
|
||||
<span id="book-name"></span>
|
||||
</p>
|
||||
<img id="book-cover" src="" class="img-fluid">
|
||||
<p class="bg-success">
|
||||
<span ><strong>Progress: </strong></span>
|
||||
</p>
|
||||
@@ -54,26 +43,16 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<div id="download-section">
|
||||
<p id="download-warning" style="display: none;" class="bg-warning"></p>
|
||||
<button id="download-button" type="button" style="display: none;" class="btn btn-primary">Download above chapters as zip package</button>
|
||||
<p id="book-name"><strong></strong></p>
|
||||
<div id="status"></div>
|
||||
<img id="image-result" hidden>
|
||||
</div>
|
||||
|
||||
|
||||
<link rel="stylesheet" href="3pty/bootstrap.min.css">
|
||||
|
||||
<script src="3pty/bluebird.js"></script>
|
||||
<script src="3pty/jszip.js"></script>
|
||||
<script src="epub.js"></script>
|
||||
<script src="sidebar.js"></script>
|
||||
|
||||
<script src="3pty/jquery-3.1.1.slim.min.js"></script>
|
||||
<script src="3pty/tether.min.js"></script>
|
||||
<script src="3pty/bootstrap.min.js"></script>
|
||||
|
||||
<script src="epub.js"></script>
|
||||
<script src="sidebar.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
327
src/sidebar.js
327
src/sidebar.js
@@ -1,48 +1,12 @@
|
||||
var concurrency = 5
|
||||
var retry_delay = 15/* seconds */
|
||||
var book_delay = 300/* seconds */
|
||||
var download_list = []
|
||||
|
||||
function sleep(delay) {
|
||||
return new Promise((resolve) => setTimeout(resolve, delay*1000));
|
||||
}
|
||||
|
||||
function getCurrentTab() {
|
||||
console.debug("Querying active tab.");
|
||||
var queryInfo = {
|
||||
active: true,
|
||||
currentWindow: true
|
||||
};
|
||||
|
||||
return browser.tabs.query(queryInfo)
|
||||
.then(function(tabs) {
|
||||
if (tabs.length == 1){
|
||||
let url = tabs[0].url;
|
||||
console.info(`Active URL: ${url}`);
|
||||
return url;
|
||||
} else {
|
||||
console.error(`Expected 1 active tab, received: ${tabs}`);
|
||||
throw 'Failed to get active tab.';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function extractBookId(url){
|
||||
console.debug(`Extracting book id from ${url}`);
|
||||
// match a url like:
|
||||
// https://www.safaribooksonline.com/library/view/startup-opportunities-2nd/9781119378181/
|
||||
// https://www.safaribooksonline.com/library/view/startup-growth-engines/77961SEM00001/
|
||||
let match = url.match(/\/library\/view\/[^\/]+\/(\w+)\//);
|
||||
let bookId = match && match[1];
|
||||
|
||||
if (bookId) {
|
||||
console.debug(`Extracted book id: ${bookId}`);
|
||||
return bookId;
|
||||
}else{
|
||||
console.error('Could not extract book id from url, only '
|
||||
+'domain "www.safaribooksonline.com“ is supported.');
|
||||
throw 'Failed to extract book id.';
|
||||
}
|
||||
}
|
||||
|
||||
class Book{
|
||||
|
||||
constructor(book_id, page) {
|
||||
@@ -57,6 +21,25 @@ class Book{
|
||||
this.page = page
|
||||
}
|
||||
|
||||
addToLocalPlaylist(){
|
||||
let method = "https://learning.oreilly.com/api/v2/collections/92f0712e-eb58-4c6e-939e-d5c57a7e6d90/add-content/"
|
||||
let request = {
|
||||
content: [`/api/v1/book/${this.book_id}/`]
|
||||
}
|
||||
return fetch(method, {
|
||||
method: "post",
|
||||
body: JSON.stringify(request),
|
||||
headers: new Headers({
|
||||
"Accept" : "application/json; version=v2",
|
||||
"Content-Type": "application/json"
|
||||
}),
|
||||
credentials: 'include'
|
||||
})
|
||||
.then((res)=>{
|
||||
console.warn(`Post ${method} - ${res.status} - ${res.statusText}`)
|
||||
})
|
||||
}
|
||||
|
||||
downloadResource(url, retry=false){
|
||||
if (retry){
|
||||
console.warn(`Retry ${url}`)
|
||||
@@ -212,50 +195,120 @@ class Book{
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
class SidebarPage{
|
||||
|
||||
constructor() {
|
||||
$('#loading').show();
|
||||
$('#error-message').hide();
|
||||
$('#book-info').hide();
|
||||
$("#book-file-list").empty();
|
||||
$('#book-info').hide()
|
||||
this.id = 1
|
||||
}
|
||||
|
||||
renderInfo(book){
|
||||
$("#book-name").text(book.book_info.title);
|
||||
getUniqueueId(){
|
||||
let id = `item_${this.id}`
|
||||
this.id++
|
||||
return id
|
||||
}
|
||||
|
||||
loading_animatedDot(){
|
||||
return $('<snap/>')
|
||||
.addClass('loader__dot')
|
||||
.text('.')
|
||||
}
|
||||
|
||||
loading_animation(){
|
||||
return $('<snap/>')
|
||||
.attr('id', 'dot_animation')
|
||||
.append(this.loading_animatedDot())
|
||||
.append(this.loading_animatedDot())
|
||||
.append(this.loading_animatedDot())
|
||||
}
|
||||
|
||||
remove_loading_animation(){
|
||||
$('#dot_animation').remove()
|
||||
}
|
||||
|
||||
/****************************************************/
|
||||
|
||||
renderBookItemId(book_id){
|
||||
let id = this.getUniqueueId()
|
||||
|
||||
let link = $('<a/>')
|
||||
.attr('href', `https://learning.oreilly.com/search/?query=${book_id}`)
|
||||
.attr('id', `a_${id}`)
|
||||
.text(`${book_id}`)
|
||||
|
||||
var book_item =
|
||||
$("<li></li>")
|
||||
.addClass("list-group-item")
|
||||
.attr('id', id)
|
||||
.append(link)
|
||||
|
||||
$("#book-queue").prepend(book_item);
|
||||
return id
|
||||
}
|
||||
|
||||
renderBookItemNameAndURL(id, name, url){
|
||||
$('#a_'+id)
|
||||
.attr('href', url)
|
||||
.text(name)
|
||||
}
|
||||
|
||||
renderBookItemAnimatedLoading(id){
|
||||
$('#'+id)
|
||||
.append(this.loading_animation())
|
||||
}
|
||||
|
||||
renderBookItemSuccess(id){
|
||||
this.remove_loading_animation()
|
||||
$('#'+id)
|
||||
.css( "color", "green" )
|
||||
.append(' - OK');
|
||||
}
|
||||
|
||||
renderBookItemFail(id, err){
|
||||
this.remove_loading_animation()
|
||||
$('#'+id)
|
||||
.css( "color", "red" )
|
||||
.append(' - Fail: ')
|
||||
.append(err)
|
||||
}
|
||||
|
||||
/****************************************************/
|
||||
|
||||
renderBookBeginLoading(id){
|
||||
$('#book-info').hide();
|
||||
$("#book-file-list").empty();
|
||||
this.renderBookItemAnimatedLoading(id)
|
||||
}
|
||||
|
||||
renderBookInfo(book, id){
|
||||
$("#book-cover").attr("src", book.book_info.cover);
|
||||
let link = $('<a/>')
|
||||
.attr('href', book.book_info.web_url)
|
||||
.text(book.book_info.title)
|
||||
$("#book-name").empty()
|
||||
.append(link);
|
||||
this.renderBookItemNameAndURL(id,
|
||||
book.book_info.title,
|
||||
book.book_info.web_url)
|
||||
|
||||
$('#book-info').show();
|
||||
}
|
||||
|
||||
renderDoneWithBook(){
|
||||
$('#loading').hide();
|
||||
renderBookSuccess(id){
|
||||
this.renderBookItemSuccess(id)
|
||||
}
|
||||
|
||||
renderFailedTheBook(error) {
|
||||
console.error(`Error: ${error}`);
|
||||
$('#loading').hide();
|
||||
$('#error-message').text(`Error: ${error}`);
|
||||
$('#error-message').show();
|
||||
// $('#book-info').hide();
|
||||
renderBookFail(id, err){
|
||||
this.renderBookItemFail(id, err)
|
||||
}
|
||||
|
||||
renderChapterList(book){
|
||||
// Add chapters to UI
|
||||
for (let chapter_idx in book.chapter_list) {
|
||||
let chapter = book.chapter_list[chapter_idx];
|
||||
var chapter_dom = $("<li></li>")
|
||||
.addClass("list-group-item")
|
||||
.html(chapter.title)
|
||||
.attr("chapterIndex", chapter_idx);
|
||||
$("#book-chapter-list").append(chapter_dom);
|
||||
}
|
||||
$('#loading').hide();
|
||||
}
|
||||
/****************************************************/
|
||||
|
||||
renderProgress(txt){
|
||||
let id = `item_${this.id}`
|
||||
this.id++
|
||||
let id = this.getUniqueueId()
|
||||
|
||||
// Add chapters to UI
|
||||
var progress_dom = $("<li></li>")
|
||||
.addClass("list-group-item")
|
||||
@@ -278,7 +331,9 @@ class SidebarPage{
|
||||
.attr('title',reason.message);
|
||||
}
|
||||
}
|
||||
var page = new SidebarPage()
|
||||
|
||||
/*********************************************************/
|
||||
|
||||
function fillMetadata(epub, book)
|
||||
{
|
||||
@@ -390,20 +445,13 @@ function createEpub(book, epub){
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************************************/
|
||||
|
||||
function onDownloadBookClicked(){
|
||||
console.info("Begin book download.");
|
||||
page = new SidebarPage()
|
||||
|
||||
getCurrentTab()
|
||||
.then(extractBookId)
|
||||
.then((book_id) => {
|
||||
epub = new EpubWriter();
|
||||
book = new Book(book_id, page);
|
||||
function downloadBook(book_id, item_id){
|
||||
let epub = new EpubWriter();
|
||||
let book = new Book(book_id, page);
|
||||
return book.downloadBookInfo()
|
||||
.then(() => { page.renderInfo(book); })
|
||||
// .then(() => { return book.downloadChapterList(); })
|
||||
// .then(() => { return page.renderChapterList(book); })
|
||||
.then(() => { page.renderBookInfo(book, item_id); })
|
||||
.then(() => { return book.downloadMetaContent(); })
|
||||
.then(() => { return book.downloadContent(); })
|
||||
.then(() => { return createEpubStructure(book, epub); })
|
||||
@@ -419,15 +467,132 @@ function onDownloadBookClicked(){
|
||||
let url = window.URL.createObjectURL(file)
|
||||
return browser.downloads.download({ "filename" : filename, url : url})
|
||||
})
|
||||
.then(()=>{
|
||||
// Add to playlist "local" to indicate that this book
|
||||
// is already downloaded.
|
||||
return book.addToLocalPlaylist()
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
/*********************************************************************/
|
||||
|
||||
var progress = false
|
||||
|
||||
function downloadNextBook(){
|
||||
progress = true
|
||||
|
||||
let download = download_list.pop()
|
||||
item_id = download.page_id
|
||||
book_id = download.book_id
|
||||
|
||||
page.renderBookBeginLoading(item_id)
|
||||
|
||||
downloadBook(book_id, item_id)
|
||||
.then(() =>{
|
||||
page.renderDoneWithBook();
|
||||
page.renderBookSuccess(item_id);
|
||||
})
|
||||
.catch((error)=>{
|
||||
page.renderFailedTheBook(error);
|
||||
page.renderBookFail(item_id, error);
|
||||
})
|
||||
.then(()=>{
|
||||
if (download_list.length){
|
||||
return sleep(book_delay)
|
||||
.then(()=>{
|
||||
return downloadNextBook()
|
||||
})
|
||||
}
|
||||
else{
|
||||
progress = false
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/*********************************************************************/
|
||||
|
||||
function getCurrentTabUrl() {
|
||||
console.debug("Querying active tab.");
|
||||
var queryInfo = {
|
||||
active: true,
|
||||
currentWindow: true
|
||||
};
|
||||
|
||||
return browser.tabs.query(queryInfo)
|
||||
.then(function(tabs) {
|
||||
if (tabs.length == 1){
|
||||
let url = tabs[0].url;
|
||||
console.info(`Active URL: ${url}`);
|
||||
return url;
|
||||
} else {
|
||||
console.error(`Expected 1 active tab, received: ${tabs}`);
|
||||
throw 'Failed to get active tab.';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function closeCurrentTab() {
|
||||
console.debug("Querying active tab.");
|
||||
var queryInfo = {
|
||||
active: true,
|
||||
currentWindow: true
|
||||
};
|
||||
|
||||
return browser.tabs.query(queryInfo)
|
||||
.then(function(tabs) {
|
||||
if (tabs.length == 1){
|
||||
let tabId = tabs[0].id;
|
||||
console.info(`Closing tab: ${tabId}`);
|
||||
return browser.tabs.remove(tabId);
|
||||
} else {
|
||||
console.error(`Expected 1 active tab, received: ${tabs}`);
|
||||
throw 'Failed to get active tab.';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function extractBookId(url){
|
||||
console.debug(`Extracting book id from ${url}`);
|
||||
// match a url like:
|
||||
// https://www.safaribooksonline.com/library/view/startup-opportunities-2nd/9781119378181/
|
||||
// https://www.safaribooksonline.com/library/view/startup-growth-engines/77961SEM00001/
|
||||
let match = url.match(/\/library\/view\/[^\/]+\/(\w+)\//);
|
||||
let bookId = match && match[1];
|
||||
|
||||
if (bookId) {
|
||||
console.debug(`Extracted book id: ${bookId}`);
|
||||
return bookId;
|
||||
}else{
|
||||
console.error('Could not extract book id from url, only '
|
||||
+'domain "www.safaribooksonline.com“ is supported.');
|
||||
throw 'Failed to extract book id.';
|
||||
}
|
||||
}
|
||||
|
||||
function onDownloadBookClicked(){
|
||||
console.info("Begin book download.");
|
||||
|
||||
getCurrentTabUrl()
|
||||
.then((url)=>{
|
||||
return extractBookId(url)
|
||||
})
|
||||
.then((book_id)=>{
|
||||
closeCurrentTab()
|
||||
return book_id
|
||||
})
|
||||
.then((book_id)=>{
|
||||
page_id = page.renderBookItemId(book_id)
|
||||
download_list.push({
|
||||
book_id : book_id,
|
||||
page_id : page_id
|
||||
})
|
||||
})
|
||||
.then(()=>{
|
||||
if (download_list.length && !progress){
|
||||
downloadNextBook()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
console.log("Start safari book hunter.");
|
||||
$('#loading').hide();
|
||||
@@ -438,8 +603,6 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
onDownloadBookClicked();
|
||||
});
|
||||
|
||||
|
||||
|
||||
$('#deselect-all-button').show()
|
||||
$('#download-button').show()
|
||||
$('#download-section').hide();
|
||||
|
||||
Reference in New Issue
Block a user