305 lines
9.2 KiB
JavaScript
305 lines
9.2 KiB
JavaScript
document.body.style.border = "5px solid red";
|
|
|
|
function onError(error) {
|
|
console.error(`Error: ${error}`);
|
|
$('#error-message').text(`Error: ${error}`);
|
|
$('#error-message').show();
|
|
$('#loading').hide();
|
|
$('#book-info').hide();
|
|
}
|
|
|
|
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.';
|
|
}
|
|
}, onError);
|
|
}
|
|
|
|
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 MiniWebServer{
|
|
|
|
// constructor() {
|
|
// this.url = "http://127.0.0.1:8080/test.zip"
|
|
// }
|
|
|
|
// downloadTestZip(){
|
|
// console.info(`Downloading ${this.url}`)
|
|
// return fetch(this.url, {
|
|
// credentials: 'include'
|
|
// }).then((res) => {
|
|
// console.log(`Getting blob.`)
|
|
// return res.blob()
|
|
// }).then(JSZip.loadAsync)
|
|
// .then((zip) => {
|
|
// this.zip = zip
|
|
// })
|
|
|
|
// }
|
|
|
|
// fetchUrl(url){
|
|
// let name = "meta/"+url.replace(/[^a-z0-9]/gi, '_').toLowerCase()
|
|
// this.zip.file(name)
|
|
// .
|
|
// }
|
|
// }
|
|
|
|
class Book{
|
|
|
|
constructor(book_id, epub) {
|
|
this.book_id = book_id;
|
|
this.raw_book = {};
|
|
this.chapter_list = [];
|
|
this.book_files = {}
|
|
this.book_info = null
|
|
this.book_toc = null
|
|
this.book_flattoc = null
|
|
}
|
|
|
|
downloadResource(url){
|
|
console.info(`Downloading ${url}`)
|
|
return fetch(url, {
|
|
credentials: 'include'
|
|
}).then((res) => {
|
|
console.log(`Downloaded.`)
|
|
return res;
|
|
}, onError)
|
|
}
|
|
|
|
downloadJson(url){
|
|
return this.downloadResource(url)
|
|
.then((res) => {
|
|
return res.json();
|
|
}, onError);
|
|
}
|
|
|
|
downloadBookInfo(){
|
|
console.info(`Downloading book info for ${this.book_id}`);
|
|
let url = `https://www.safaribooksonline.com/api/v1/book/${this.book_id}/`;
|
|
return this.downloadJson(url)
|
|
.then((book_info)=>{
|
|
this.book_info = book_info;
|
|
}, onError);
|
|
}
|
|
|
|
downloadChapterList(){
|
|
function helper(book, url){
|
|
console.info(`Downloading chapter list ${url}`);
|
|
return book.downloadJson(url)
|
|
.then((chapter_list)=>{
|
|
book.chapter_list
|
|
= book.chapter_list.concat(chapter_list.results);
|
|
|
|
if (chapter_list.next != null){
|
|
return helper(book, chapter_list.next);
|
|
}
|
|
}, onError);
|
|
}
|
|
|
|
return helper(this, this.book_info.chapter_list)
|
|
.then(() => {
|
|
console.info(`Chapter List Downloaded.`);
|
|
}, onError);
|
|
}
|
|
|
|
downloadMetaContent(){
|
|
let downloads = []
|
|
|
|
this.insertBookFile({
|
|
url: this.book_info.cover,
|
|
file: "cover.img",
|
|
context: null,
|
|
mime: null
|
|
})
|
|
|
|
downloads.push(
|
|
this.downloadJson(this.book_info.toc)
|
|
.then((json)=>{
|
|
this.book_toc=json
|
|
}))
|
|
downloads.push(
|
|
this.downloadJson(this.book_info.flat_toc)
|
|
.then((json)=>{
|
|
this.book_flattoc=json
|
|
}))
|
|
downloads.push(
|
|
Promise.map(this.book_info.chapters, (chapter) => {
|
|
return this.downloadJson(chapter)
|
|
.then((json) => {
|
|
return this.extractChapterAssets(json)
|
|
})
|
|
},{concurrency: 1}))
|
|
|
|
return Promise.all(downloads)
|
|
}
|
|
|
|
downloadMetaContent(){
|
|
let downloads = []
|
|
// downloads.push(this.downloadResource(this.book_info.cover))
|
|
downloads.push(
|
|
this.downloadJson(this.book_info.toc)
|
|
.then((json)=>{
|
|
this.book_toc=json
|
|
}))
|
|
downloads.push(
|
|
this.downloadJson(this.book_info.flat_toc)
|
|
.then((json)=>{
|
|
this.book_flattoc=json
|
|
}))
|
|
downloads.push(
|
|
Promise.map(this.book_info.chapters, (chapter) => {
|
|
return this.downloadJson(chapter)
|
|
.then((json) => {
|
|
return this.extractChapterAssets(json)
|
|
})
|
|
},{concurrency: 1}))
|
|
|
|
return Promise.all(downloads)
|
|
}
|
|
|
|
|
|
extractChapterAssets(json){
|
|
if (!json.asset_base_url
|
|
|| !json.content){
|
|
throw "Missing data."
|
|
}
|
|
|
|
// Html
|
|
this.insertBookFile({
|
|
url: json.content,
|
|
file: json.full_path,
|
|
context: null,
|
|
mime: null
|
|
})
|
|
// List of images.
|
|
for (let idx in json.images){
|
|
this.insertBookFile({
|
|
url: json.asset_base_url + json.images[idx],
|
|
file: json.images[idx],
|
|
context: null,
|
|
mime: null
|
|
})
|
|
}
|
|
// List of stylesheets.
|
|
for (let idx in json.stylesheets){
|
|
this.insertBookFile({
|
|
url: json.stylesheets[idx].original_url,
|
|
file: json.stylesheets[idx].full_path,
|
|
context: null,
|
|
mime: null
|
|
})
|
|
}
|
|
}
|
|
|
|
insertBookFile(obj){
|
|
this.book_files[obj.url] = obj
|
|
}
|
|
}
|
|
|
|
function renderInfo(book){
|
|
$("#book-name").text(book.book_info.title);
|
|
$("#book-cover").attr("src", book.book_info.cover);
|
|
$('#book-info').show();
|
|
}
|
|
|
|
function renderChapterList(book){
|
|
// Add chapters to UI
|
|
for (let chapter_idx in book.chapter_list) {
|
|
console.log(book.chapter_list[chapter_idx])
|
|
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();
|
|
}
|
|
|
|
function createEpub(book, epub){
|
|
for (let url in book.raw_book){
|
|
let name = "meta/"+book.raw_book[url].url.replace(/[^a-z0-9]/gi, '_').toLowerCase()
|
|
epub.addFile(name, book.raw_book[url].clone().blob())
|
|
}
|
|
// epub.addFile("book_info.json", JSON.stringify(book.book_info))
|
|
// epub.addFile("book_files.json", JSON.stringify(book.book_files))
|
|
// epub.addFile("book_toc.json", JSON.stringify(book.book_toc))
|
|
// epub.addFile("book_flattoc.json", JSON.stringify(book.book_flattoc))
|
|
epub.addFile("book.json", JSON.stringify(book, null, '\t'))
|
|
}
|
|
|
|
function onDownloadBookClicked(){
|
|
console.info("Begin book download.");
|
|
$('#loading').show();
|
|
$('#error-message').hide();
|
|
$('#book-info').hide();
|
|
|
|
getCurrentTab()
|
|
.then(extractBookId, onError)
|
|
.then((book_id) => {
|
|
epub = new EpubWriter();
|
|
book = new Book(book_id);
|
|
book.downloadBookInfo()
|
|
.then(() => { renderInfo(book); })
|
|
.then(() => { return book.downloadChapterList(); })
|
|
.then(() => { return renderChapterList(book); })
|
|
.then(() => { return book.downloadContent(); })
|
|
.then(() => { return createEpub(book, epub); })
|
|
.then(() => { return epub.generateAsync(); })
|
|
.then((file) => {
|
|
let filename = "books/" + "test" + ".zip"
|
|
// book.filename.replace(/[^a-z0-9]/gi, '_').toLowerCase()
|
|
let url = window.URL.createObjectURL(file)
|
|
browser.downloads.download({ "filename" : filename, url : url})
|
|
})
|
|
|
|
}, onError);
|
|
}
|
|
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
console.log("Start safari book hunter.");
|
|
$('#loading').hide();
|
|
$('#error-message').hide();
|
|
$('#book-info').hide();
|
|
|
|
$('#download-book-button').click(() => {
|
|
onDownloadBookClicked();
|
|
});
|
|
|
|
|
|
|
|
$('#deselect-all-button').show()
|
|
$('#download-button').show()
|
|
$('#download-section').hide();
|
|
// let bookInfo = new BookInfo();
|
|
})
|