danbooruに画像をダウンロードするボタンを追加する。画像にはタグ(XP Keywords)が付与される。Picasa3と相性抜群。
Від
// ==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;
}
}
)();