Downbooru

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

18.05.2017 itibariyledir. En son verisyonu görün.

Bu betiği kurabilmeniz için Tampermonkey, Greasemonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği yüklemek için Tampermonkey gibi bir uzantı yüklemeniz gerekir.

Bu betiği kurabilmeniz için Tampermonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Userscripts gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği indirebilmeniz için ayrıca Tampermonkey gibi bir eklenti kurmanız gerekmektedir.

Bu betiği yüklemek için bir betik yöneticisi eklentisi yüklemeniz gerekecektir.

(Zaten bir betik yöneticim var, hadi yükleyelim!)

Bu stili yüklemek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için Stylus gibi bir uzantı kurmanız gerekir.

Bu stili yükleyebilmek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı kurmanız gerekir.

Bu stili yükleyebilmek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

(Zateb bir user-style yöneticim var, yükleyeyim!)

// ==UserScript==
// @name         Downbooru
// @name:en      Downbooru
// @namespace    https://greasyfork.org/ja/scripts/29802-downbooru
// @version      2.0.1
// @description  danbooruに画像をダウンロードするボタンを追加する。画像にはタグ(XP Keywords)が付与される。Picasa3と相性抜群。
// @description  PNGはExifが無いためJPGに変換。サイズの大きい画像は縮小される。
// @description:en download image with tags from danbooru
// @author       You
// @match        http://danbooru.donmai.us/posts/*
// @match        https://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って何
(function() {
    
	getImgDataurl('large')
	.then(function(dataurl){
		//pngやあまりに大きい画像の場合dataurlの上限にひっかかるため、
		//取得できない場合jpg縮小された画像を取得
		if (dataurl.split(",")[1]) {
			setButton(dataurl);
		}
		else {
			getImgDataurl('small')
			.then(function(dataurl){
				setButton(dataurl);
			});
		}
	});

    function setButton(dataurl){
		//画像ページのID取得
        var num = /\d+/.exec($('#post-information').find('li')[0].innerText)[0];
        var file_name = "danbooru" + num + ".jpg";

        var tag_list = getTagList();
        var exif = createExif(tag_list);

        var blob = insertExif(exif, dataurl);
        var url = window.URL.createObjectURL(blob);
        var button = $('<a>', {
            href: url,
            download: file_name
        }).html('<button>Download</button>');
        var con = $('#image-container');
        con.append(button);
        return;
    }

    function getImgDataurl(size){
        var dfd = new $.Deferred();
        var img = new Image();
        switch (size) {
            case 'large':
                img.src = $('#post-options').find('a')[4].getAttribute('href');
                break;
            case 'small':
                img.src = $('#image').attr('src');
        }
        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');
            dfd.resolve(jpg_src);
        };
        return dfd.promise();
    }

    function getTagList() {
        var tag_list = [];
		$('.search-tag').each(function() {
			tag_list.push($(this).text().replace(/\s/g, "_"));
		});
        return tag_list;
    }

    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;
    }
}
)();