OverDrive Transcriber

Transcribes books you read on OverDrive for offline reading

目前為 2018-04-24 提交的版本,檢視 最新版本

// ==UserScript==
// @name OverDrive Transcriber
// @description Transcribes books you read on OverDrive for offline reading
// @namespace Violentmonkey Scripts
// @match *://*.read.overdrive.com/*
// @match *://*.greasyfork.org/*
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_listValues
// @grant GM_deleteValue
// @run-at document-start
// @version 0.1.1
// @author qsniyg
// ==/UserScript==

(function() {
    var content_html_regex = /\.x?html?\?cmpt=/;
    var content_html_match = /^(?:.*\/)?([^/.]*)\.x?html?/;
    
    function onload(f) {
        if (document.readyState === "interactive" || document.readyState === "complete") {
            f();
        } else {
            document.addEventListener("DOMContentLoaded", f, false);
        }
    }
    
    function makebutton(el, bg) {
        el.style.padding = ".5em 1em";
        el.style.background = bg;
        el.style.color = "white";
        el.style.textDecoration = "none";
        el.style.display = "inline-block";
        el.style.margin = ".3em .5em";
    }
    
    // OverDrive section
    function overdrive() {    
        function parse(url, content) {
            var html = document.createElement("html");
            html.innerHTML = content;

            var url_match = url.match(content_html_match);
            if (!url_match)
                // shouldn't happen
                return;
            //var book_id = url_match[2];
            var book_id = bData["-odread-buid"];
            var var_id = url_match[1];

            var titleel = html.getElementsByTagName("title")[0];
            if (!titleel)
                return;

            var title = titleel.innerHTML;
            /*console.log(book_id);
            console.log(var_id);
            console.log(title);*/

            GM_setValue("TITLE:" + book_id, title.toString());
            GM_setValue("INFO:" + book_id, JSON.stringify(unsafeWindow.bData));

            var setcontents = function(contents) {
                GM_setValue("CONTENTS:" + book_id + ":" + var_id, contents);
            }

            var scripts = html.getElementsByTagName("script");
            var regex = /^ *parent\.[^;(]*\(.*?['"](.*?)['"]/;
            var set = false;
            for (var i = 0; i < scripts.length; i++) {
                var matchobj = scripts[i].innerHTML.match(regex);
                if (matchobj) {
                    var text = unsafeWindow.atob(matchobj[1]);
                    setcontents(text);
                    set = true;
                }
            }

            if (!set) {
                if (html.querySelectorAll("body > p").length >= 0) {
                    var body = html.getElementsByTagName("body")[0].cloneNode(true);
                    body.removeAttribute("xmlns");
                    body.removeAttribute("onload");
                    setcontents(body.outerHTML);
                }
            }
        }

        var original_open = window.XMLHttpRequest.prototype.open;
        window.XMLHttpRequest.prototype.open = function(method, url) {
            if (!url)
                return;

            if (url.match(content_html_regex)) {
                this.addEventListener("readystatechange", function() {
                    if (this.readyState === 4) {
                        parse(url, this.responseText);
                    }
                });
            }
            original_open.apply(this, arguments);
        };

        function run_iframe(iframe) {
            var ifdocument = iframe.contentDocument || iframe.contentWindow.document;
            parse(iframe.src, ifdocument.documentElement.innerHTML)
        }

        function find_iframes() {
            return;
            var iframes = document.getElementsByTagName("iframe");
            for (var i = 0; i < iframes.length; i++) {
                if (iframes[i].src.match(content_html_regex)) {
                    (function(iframes, i) {
                        iframes[i].onload = function() {
                            run_iframe(iframes[i]);
                        }

                        run_iframe(iframes[i]);
                    })(iframes, i);
                }
            }
        }
        
        function transcribe(el) {
            el.innerHTML = "Transcribing...";
            
            var info = unsafeWindow.bData;
            
            var i = 0;
            function do_request() {
                if (i >= info.spine.length) {
                    console.log("Done");
                    el.innerHTML = "Done";
                    return;
                }
                
                var path = info.spine[i].path;
                i++;
                console.log(path);
                if (!path.match(content_html_regex)) {
                    console.log("Skipping: " + path);
                    do_request();
                    return;
                }

                var oReq = new XMLHttpRequest();
                oReq.addEventListener("load", do_request);
                oReq.open("GET", path);
                oReq.send();
            }
            
            do_request();
        }

        function start() {
            new MutationObserver(find_iframes).observe(document.documentElement, {
                attributes: true,
                childList: true
            });
            find_iframes();
            
            if (unsafeWindow.bData) {
                var outer_div = document.createElement("div");
                outer_div.style.width = "100%";

                var transcribe_btn = document.createElement("a");
                transcribe_btn.innerHTML = "Transcribe";
                transcribe_btn.onclick = function() {
                    transcribe(transcribe_btn);
                };
                transcribe_btn.href = "javascript:void(0)";
                transcribe_btn.style.zIndex = 999999999;
                transcribe_btn.style.position = "absolute";
                makebutton(transcribe_btn, "#1070a0");

                outer_div.appendChild(transcribe_btn);
                document.body.appendChild(outer_div);
            }
        }

        onload(start);
    }
    
    // GreasyFork section
    function greasyfork() {
        function start() {
            var insert = document.getElementById("overdrive-insert");
            
            if (!insert) {
                var addto = document.querySelector(".script-author-description");
                addto.innerHTML = "<div id='overdrive-insert'></div>" + addto.innerHTML;
            
                insert = document.getElementById("overdrive-insert");
            }
            
            insert.innerHTML = "";
            
            var table = document.createElement("table");
            table.style.border = "1px solid black";
            table.style.background = "white";
            table.style.width = "100%";
            
            var preview_iframe = document.createElement("iframe");
            preview_iframe.style.width = "100%";
            preview_iframe.style.display = "block";
            preview_iframe.style.border = "0";
            var preview_tr = document.createElement("tr");
            preview_tr.style.padding = 0;
            preview_tr.style.margin = 0;
            var preview_td = document.createElement("td");
            preview_td.style.padding = 0;
            preview_td.style.margin = 0;
            preview_td.style.borderBottom = "1px solid black";
            preview_td.style.display = "none";
            preview_td.setAttribute("colspan", 10);
            preview_td.appendChild(preview_iframe);
            preview_tr.appendChild(preview_td);
            table.appendChild(preview_tr);
            
            var keys = GM_listValues();
            if (keys.length === 0) {
                var tr = document.createElement("tr");
                var td = document.createElement("td");
                td.innerHTML = "<i>No books yet</>";
                tr.appendChild(td);
                table.appendChild(tr);
            }
            
            for (var i = 0; i < keys.length; i++) {
                if (!keys[i].match(/^TITLE:/)) {
                    continue;
                }
                
                (function(key) {
                    var title = GM_getValue(key);
                    var id = key.replace(/.*:/, "");
                    var info = JSON.parse(GM_getValue("INFO:" + id));

                    var tr = document.createElement("tr");
                    tr.style.border = "1px solid black";

                    var name_td = document.createElement("td");
                    name_td.innerHTML = "<b>" + title + "</b>";
                    
                    var html = '<html><head><title>' + title + '</title><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /></head><body>';
                    var size = 0;
                    var items = {};
                    for (var i = 0; i < keys.length; i++) {
                        if (keys[i].indexOf("CONTENTS:" + id + ":") !== 0) {
                            continue;
                        }

                        var contents = GM_getValue(keys[i]);
                        size += contents.length;
                        
                        var element = document.createElement("html");
                        element.innerHTML = '<head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /></head>';
                        element.innerHTML += contents;
                        
                        var text = element.getElementsByTagName("body")[0].innerHTML;

                        //html += text;
                        items[keys[i].replace(/.*:/, "")] = text;
                    }
                    
                    var have = 0;
                    var total = 0;
                    for (var i = 0; i < info.spine.length; i++) {
                        if (!info.spine[i].path.match(content_html_regex))
                            continue;
                        total++;
                        var spinematch = info.spine[i].path.match(content_html_match);
                        if (!spinematch)
                            continue;
                        var spineid = spinematch[1];
                        if (spineid in items) {
                            html += items[spineid];
                            have++;
                        } else {
                            console.log("Missing " + spineid);
                        }
                    }
                    html += "</body></html>";
                    
                    var link = "data:text/html," + encodeURIComponent(html);
                    name_td.innerHTML += "  (" + (size / 1024).toFixed(1) + "KB, " + have + "/" + total + ")";
                    

                    var preview = document.createElement("a");
                    preview.onclick = function() {
                        preview_iframe.src = link;
                        preview_td.style.display = "table-cell";
                    };
                    preview.href = "javascript:void(0)";
                    preview.innerHTML = "Preview";
                    makebutton(preview, "#505210");
                    
                    var download = document.createElement("a");
                    download.href = link;
                    download.setAttribute("download", title + ".html");
                    download.innerHTML = "Download";
                    makebutton(download, "#1070a0");
                    
                    var del = document.createElement("a");
                    del.href = "javascript:void(0)";
                    del.onclick = function() {
                        if (!confirm("Delete '" + title + "'?"))
                            return;
                        
                        for (var i = 0; i < keys.length; i++) {
                            if (keys[i] === ("TITLE:" + id) ||
                                keys[i] === ("INFO:" + id) ||
                                keys[i].indexOf("CONTENTS:" + id + ":") >= 0) {
                                console.log(keys[i]);
                                GM_deleteValue(keys[i]);
                            }
                        }
                        
                        start();
                    }
                    del.innerHTML = "Delete";
                    del.style.float = "right";
                    makebutton(del, "#a02010");
                    
                    var actions_td = document.createElement("td");
                    actions_td.appendChild(preview);
                    actions_td.appendChild(download);
                    actions_td.appendChild(del);
                    
                    tr.appendChild(name_td);
                    tr.appendChild(actions_td);
                    
                    table.appendChild(tr);
                })(keys[i]);
            }
            
            insert.appendChild(table);
        }
        
        onload(start);
    }
    
    if (document.location.href.match(/\.read\.overdrive\.com\//))
        overdrive();
    else
        greasyfork();
})();