Downbooru

danbooruに画像をダウンロードするボタンを追加する。画像にはタグ(XP Keywords)が付与される。Picasa3と相性抜群。

Устаревшая версия за 18.05.2017. Перейдите к последней версии.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey, Greasemonkey или Violentmonkey.

Для установки этого скрипта вам необходимо установить расширение, такое как Tampermonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Violentmonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Userscripts.

Чтобы установить этот скрипт, сначала вы должны установить расширение браузера, например Tampermonkey.

Чтобы установить этот скрипт, вы должны установить расширение — менеджер скриптов.

(у меня уже есть менеджер скриптов, дайте мне установить скрипт!)

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

(у меня уже есть менеджер стилей, дайте мне установить скрипт!)

// ==UserScript==
// @name         Downbooru
// @name:en      Downbooru
// @namespace    https://greasyfork.org/ja/scripts/29802-downbooru
// @version      2.0
// @description  danbooruに画像をダウンロードするボタンを追加する。画像にはタグ(XP Keywords)が付与される。Picasa3と相性抜群。
// @description  PNGはExifが無いためJPGに変換。サイズの大きい画像は縮小される。
// @description:en donwloads an image added tags from danbooru
// @author       You
// @match        *://danbooru.donmai.us$
// @match        *://danbooru.donmai.us/posts*
// @grant        none
// @require      http://ajax.googleapis.com/ajax/libs/jquery/1.5.2/jquery.min.js
// ==/UserScript==

//参考:canvas https://blog.agektmr.com/2013/09/canvas-png-blob.html
//参考:deferred http://hamalog.tumblr.com/post/5159447047/jquerydeferredって何
//参考:http://qiita.com/geek_duck/items/2db28daa9e27df9b861d
(function () {
    var host = window.location.host;

    (function () {
        var btn_prt = [];
        switch (host) {
            case 'danbooru.donmai.us':
                $('[data-id]').each(function () {
                    btn_prt.push($(this));
                });
        }

        for (var i in btn_prt) {
            setButton(btn_prt[i]);
        }
        return;
    })();

    function setButton(btn_prt) {
        var tag_name = btn_prt.get(0).tagName;
        var button = $('<button>');
        switch (tag_name) {
            case 'SECTION':
                button.text('Download');
                break;
            case 'ARTICLE':
                button.text('DL').attr('style', 'position:absolute; right:0; bottom:0; opacity:0.5;');
                button.hover(function () {
                    button.attr('style', 'position:absolute; right:0; bottom:0; opacity:1;');
                }, function () {
                    button.attr('style', 'position:absolute; right:0; bottom:0; opacity:0.5;');
                });
                break;
        }
        button.click(function () {
            switch (host) {
                case 'danbooru.donmai.us':
                    var src = btn_prt.attr('data-file-url');
                    var tags = btn_prt.attr('data-tags').split(' ');
                    var file = "danbooru" + btn_prt.attr('data-id') + ".jpg";
            }
            switch (tag_name) {
                case 'SECTION':
                    button.text('Generating...').attr("disabled", true);
                    break;
                case 'ARTICLE':
                    button.text('...').attr("disabled", true);
                    break;
            }

            downloadImageWithTags(src, tags, file);
            switch (tag_name) {
                case 'SECTION':
                    button.html('<s>Download</s>');
                    break;
                case 'ARTICLE':
                    button.html('<s>DL</s>').attr('style', 'position:absolute; right:0; bottom:0; opacity:1;');
                    break;
            }
        });
        btn_prt.append(button);
        return;
    }

    function downloadImageWithTags(src, tags, name) {
        var exif = createExif(tags);
        getImgDataurl(src).then(function (dataurl) {
            var blob = insertExif(exif, dataurl);
            var a = document.createElement('a');
            a.href = window.URL.createObjectURL(blob);
            a.download = name;
            a.click();
            return;
        });
    }

    function getImgDataurl(url) {
        var dfd = new $.Deferred();
        var img = new Image();
        img.src = url;
        var canvas = document.createElement('canvas');
        canvas.setAttribute('width', img.width);
        canvas.setAttribute('height', img.height);
        var ctx = canvas.getContext('2d');
        img.onload = function () {
            ctx.drawImage(img, 0, 0);
            var jpg_src = canvas.toDataURL('image/jpeg');
            //画像が大きすぎる場合、width850pxで再todataURL
            if (jpg_src.split(',')[1]) {
                dfd.resolve(jpg_src);
            } else {
                var smallWidth = 850;
                var smallheight = 850 * img.height / img.width;
                canvas.setAttribute('width', smallWidth);
                canvas.setAttribute('height', smallheight);
                ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, smallWidth, smallheight);
                jpg_src = canvas.toDataURL('image/jpeg');
                dfd.resolve(jpg_src);
            }
        };
        return dfd.promise();
    }

    function createExif(list) {
        var bim = "50686f746f73686f7020332e30003842494d040400000000";
        var hex_list = [];
        for (var i = 0; i < list.length; i++) {
            hex_list[i] = strToHex(list[i]);
        }

        var tags = "1c021900" + tagLength(hex_list[0]) + hex_list[0] + "1c020000020004";
        for (var i = 1; i < hex_list.length; i++) {
            tags = tags + "1c021900" + tagLength(hex_list[i]) + hex_list[i];
        }
        tags = wholeTagLength(tags) + tags;
        //XX XX [BIM] [TAGS]
        var seg_len = (2 + bim.length / 2 + tags.length / 2).toString(16);
        if (seg_len.length > 4) {
            console.log("タグのデータ量が限界を超えました");
            return;
        }
        seg_len = "000" + seg_len;
        seg_len = seg_len.slice(-4);
        var exif = "ffed" + seg_len + bim + tags;
        return exif;
    }

    function insertExif(exif, dataurl) {
        var bin = atob(dataurl.split(",")[1]);
        var buffer = new Uint8Array(bin.length + exif.length / 2);
        for (var i = 0; i < 20; i++) {
            buffer[i] = bin.charCodeAt(i);
        }
        for (var bf_i = 20, ex_j = 0; ex_j < exif.length / 2; bf_i++,
                ex_j++) {
            buffer[bf_i] = parseInt(exif.slice(ex_j * 2, ex_j * 2 + 2), 16);
        }
        for (var bf_i = 20 + exif.length / 2, bin_j = 20; bin_j < bin.length; bf_i++,
                bin_j++) {
            buffer[bf_i] = bin.charCodeAt(bin_j);
        }
        var blob = new Blob([buffer.buffer], {
            type: 'image/jpeg'
        });
        return blob;
    }

//引数はhexで
    function tagLength(str) {
        var len = (str.length / 2).toString(16);
        len = ("0" + len).slice(-2);
        return len;
    }

//引数はhexで
    function wholeTagLength(str) {
        var len = (str.length / 2).toString(16);
        len = ("000" + len).slice(-4);
        return len;
    }

    function strToHex(str) {
        var hex = "";
        for (var i = 0; i < str.length; i++) {
            hex = hex + str.charCodeAt(i).toString(16);
        }
        return hex;
    }

    function strToU8(str) {
        var buffer = new Uint8Array(str.length);
        for (var i = 0; i < buffer.length; i++) {
            buffer[i] = str.charCodeAt(i);
        }
        return buffer;
    }

    function hexToU8(str) {
        var buffer = new Uint8Array(str.length / 2);
        for (var i = 0; i < buffer.length; i++) {
            buffer[i] = parseInt(str.slice(i * 2, i * 2 + 2), 16);
        }
        return buffer;
    }

//テスト用 16進数文字列をアルファベットに変換する
    function hexToChar(str) {
        var char = "";
        for (var i = 0; i < str.length / 2; i++) {
//4a→74→J
            char = char + String.fromCharCode(parseInt(str.slice(i * 2, i * 2 + 2), 16));
        }
        return char;
    }
}
)();