Adding queue for download.
This commit is contained in:
@@ -22,30 +22,19 @@
|
|||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<button id="download-book-button" type="button" class="btn btn-outline-primary">Download Book</button>
|
<!-- <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 class="status">
|
<div id="book-list" class="container">
|
||||||
<p id="error-message" class="bg-warning"></p>
|
<ul id="book-queue" class="list-group">
|
||||||
<div class="loader" id="loading" style="display:none;">
|
</ul>
|
||||||
<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>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="book-info" class="container">
|
<div id="book-info" class="container">
|
||||||
<h4 id="book-info-notice"></h4>
|
|
||||||
|
|
||||||
<img id="book-cover" src="" class="img-fluid">
|
|
||||||
<p class="bg-success">
|
<p class="bg-success">
|
||||||
<span><strong>Book Name:</strong></span>
|
<span><strong>Book Name:</strong></span>
|
||||||
<span id="book-name"></span>
|
<span id="book-name"></span>
|
||||||
</p>
|
</p>
|
||||||
|
<img id="book-cover" src="" class="img-fluid">
|
||||||
<p class="bg-success">
|
<p class="bg-success">
|
||||||
<span ><strong>Progress: </strong></span>
|
<span ><strong>Progress: </strong></span>
|
||||||
</p>
|
</p>
|
||||||
@@ -54,26 +43,16 @@
|
|||||||
</div>
|
</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">
|
<link rel="stylesheet" href="3pty/bootstrap.min.css">
|
||||||
|
|
||||||
<script src="3pty/bluebird.js"></script>
|
<script src="3pty/bluebird.js"></script>
|
||||||
<script src="3pty/jszip.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/jquery-3.1.1.slim.min.js"></script>
|
||||||
<script src="3pty/tether.min.js"></script>
|
<script src="3pty/tether.min.js"></script>
|
||||||
<script src="3pty/bootstrap.min.js"></script>
|
<script src="3pty/bootstrap.min.js"></script>
|
||||||
|
|
||||||
|
<script src="epub.js"></script>
|
||||||
|
<script src="sidebar.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
357
src/sidebar.js
357
src/sidebar.js
@@ -1,48 +1,12 @@
|
|||||||
var concurrency = 5
|
var concurrency = 5
|
||||||
var retry_delay = 15/* seconds */
|
var retry_delay = 15/* seconds */
|
||||||
|
var book_delay = 300/* seconds */
|
||||||
|
var download_list = []
|
||||||
|
|
||||||
function sleep(delay) {
|
function sleep(delay) {
|
||||||
return new Promise((resolve) => setTimeout(resolve, delay*1000));
|
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{
|
class Book{
|
||||||
|
|
||||||
constructor(book_id, page) {
|
constructor(book_id, page) {
|
||||||
@@ -57,6 +21,25 @@ class Book{
|
|||||||
this.page = page
|
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){
|
downloadResource(url, retry=false){
|
||||||
if (retry){
|
if (retry){
|
||||||
console.warn(`Retry ${url}`)
|
console.warn(`Retry ${url}`)
|
||||||
@@ -212,50 +195,120 @@ class Book{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*************************************************************************/
|
||||||
|
|
||||||
class SidebarPage{
|
class SidebarPage{
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
$('#loading').show();
|
$('#book-info').hide()
|
||||||
$('#error-message').hide();
|
|
||||||
$('#book-info').hide();
|
|
||||||
$("#book-file-list").empty();
|
|
||||||
this.id = 1
|
this.id = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
renderInfo(book){
|
getUniqueueId(){
|
||||||
$("#book-name").text(book.book_info.title);
|
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);
|
$("#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();
|
$('#book-info').show();
|
||||||
}
|
}
|
||||||
|
|
||||||
renderDoneWithBook(){
|
renderBookSuccess(id){
|
||||||
$('#loading').hide();
|
this.renderBookItemSuccess(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
renderFailedTheBook(error) {
|
renderBookFail(id, err){
|
||||||
console.error(`Error: ${error}`);
|
this.renderBookItemFail(id, err)
|
||||||
$('#loading').hide();
|
|
||||||
$('#error-message').text(`Error: ${error}`);
|
|
||||||
$('#error-message').show();
|
|
||||||
// $('#book-info').hide();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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){
|
renderProgress(txt){
|
||||||
let id = `item_${this.id}`
|
let id = this.getUniqueueId()
|
||||||
this.id++
|
|
||||||
// Add chapters to UI
|
// Add chapters to UI
|
||||||
var progress_dom = $("<li></li>")
|
var progress_dom = $("<li></li>")
|
||||||
.addClass("list-group-item")
|
.addClass("list-group-item")
|
||||||
@@ -278,7 +331,9 @@ class SidebarPage{
|
|||||||
.attr('title',reason.message);
|
.attr('title',reason.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
var page = new SidebarPage()
|
||||||
|
|
||||||
|
/*********************************************************/
|
||||||
|
|
||||||
function fillMetadata(epub, book)
|
function fillMetadata(epub, book)
|
||||||
{
|
{
|
||||||
@@ -390,42 +445,152 @@ function createEpub(book, epub){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*********************************************************************/
|
||||||
|
|
||||||
|
function downloadBook(book_id, item_id){
|
||||||
|
let epub = new EpubWriter();
|
||||||
|
let book = new Book(book_id, page);
|
||||||
|
return book.downloadBookInfo()
|
||||||
|
.then(() => { page.renderBookInfo(book, item_id); })
|
||||||
|
.then(() => { return book.downloadMetaContent(); })
|
||||||
|
.then(() => { return book.downloadContent(); })
|
||||||
|
.then(() => { return createEpubStructure(book, epub); })
|
||||||
|
.then(() => { return createEpub(book, epub); })
|
||||||
|
.then(() => { return epub.generateAsync(); })
|
||||||
|
.then((file) => {
|
||||||
|
let title = book.book_info.title
|
||||||
|
let filename = "books/"
|
||||||
|
+ title.replace(/[^a-z0-9]/gi, '_').toLowerCase()
|
||||||
|
+ ".epub"
|
||||||
|
console.log(`Zip file name ${filename}`)
|
||||||
|
page.renderProgress(`Saved to ${filename}`)
|
||||||
|
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.renderBookSuccess(item_id);
|
||||||
|
})
|
||||||
|
.catch((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(){
|
function onDownloadBookClicked(){
|
||||||
console.info("Begin book download.");
|
console.info("Begin book download.");
|
||||||
page = new SidebarPage()
|
|
||||||
|
|
||||||
getCurrentTab()
|
getCurrentTabUrl()
|
||||||
.then(extractBookId)
|
.then((url)=>{
|
||||||
.then((book_id) => {
|
return extractBookId(url)
|
||||||
epub = new EpubWriter();
|
})
|
||||||
book = new Book(book_id, page);
|
.then((book_id)=>{
|
||||||
return book.downloadBookInfo()
|
closeCurrentTab()
|
||||||
.then(() => { page.renderInfo(book); })
|
return book_id
|
||||||
// .then(() => { return book.downloadChapterList(); })
|
})
|
||||||
// .then(() => { return page.renderChapterList(book); })
|
.then((book_id)=>{
|
||||||
.then(() => { return book.downloadMetaContent(); })
|
page_id = page.renderBookItemId(book_id)
|
||||||
.then(() => { return book.downloadContent(); })
|
download_list.push({
|
||||||
.then(() => { return createEpubStructure(book, epub); })
|
book_id : book_id,
|
||||||
.then(() => { return createEpub(book, epub); })
|
page_id : page_id
|
||||||
.then(() => { return epub.generateAsync(); })
|
|
||||||
.then((file) => {
|
|
||||||
let title = book.book_info.title
|
|
||||||
let filename = "books/"
|
|
||||||
+ title.replace(/[^a-z0-9]/gi, '_').toLowerCase()
|
|
||||||
+ ".epub"
|
|
||||||
console.log(`Zip file name ${filename}`)
|
|
||||||
page.renderProgress(`Saved to ${filename}`)
|
|
||||||
let url = window.URL.createObjectURL(file)
|
|
||||||
return browser.downloads.download({ "filename" : filename, url : url})
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.then(() =>{
|
.then(()=>{
|
||||||
page.renderDoneWithBook();
|
if (download_list.length && !progress){
|
||||||
|
downloadNextBook()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.catch((error)=>{
|
|
||||||
page.renderFailedTheBook(error);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
@@ -438,8 +603,6 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
onDownloadBookClicked();
|
onDownloadBookClicked();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
$('#deselect-all-button').show()
|
$('#deselect-all-button').show()
|
||||||
$('#download-button').show()
|
$('#download-button').show()
|
||||||
$('#download-section').hide();
|
$('#download-section').hide();
|
||||||
|
|||||||
Reference in New Issue
Block a user