Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
地味に便利な秘蔵のブックマークレット集

秘蔵のBookmarklet集

これまでちまちま作ってきたブックマークレット(Bookmarklet)を公開します。地味に便利系が多いです。 これらのブックマークレットは全てCC0でライセンスで提供されます。

Kancollet

もはやブックマークレットじゃなくて、Webアプリみたいなものですが、艦これ専用のタイマーアプリです。

遷移を防止する

TweetDeckやカレンダー、ブラウザゲーム(例えば艦これ)など、ページの遷移が起こってほしくない常駐ページってありますよね。
そういうページから遷移するときに、確認画面を出すスクリプトです。強制的に防止することはできません、あくまでも緩和策です。

Pixiv 指定数以上ブクマされてる作品の強調表示

ES6以上必須。検索結果で探すときに使えます。「〇〇 000users入り」とかのがいいかも。 取りこぼしがないと言う点では強い。

Twitter画像保存

ES6以上必須。200行、5,000文字を超過する超大作。

画像や動画の貼ってあるツイートページでこのブックマークレットを使うと、
ユーザ名_ツイートID_画像ファイル名.拡張子の名前でメディアを保存します。 複数画像にも対応しています。

javascript:window.addEventListener('beforeunload', function(e){
e.returnValue='%E9%81%B7%E7%A7%BB%E9%98%B2%E6%AD%A2%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%88%E3%82%88%E3%82%8A%E3%80%81%E3%83%9A%E3%83%BC%E3%82%B8%E3%81%AE%E9%81%B7%E7%A7%BB%E3%82%92%E7%A2%BA%E8%AA%8D%E3%81%97%E3%81%BE%E3%81%99%E3%80%82';
});
javascript:(function(){
const ar = document.querySelectorAll('li.image-item');
const n = window.prompt('指定数', '100');
Array.prototype.forEach.call(ar, e => {
const a = e.querySelector('a.bookmark-count');
if(+a.textContent >= +n) e.style.backgroundColor = '#FF0';
}); })();
javascript: (function() {
'use strict';
function async(gene, previous_result) {
const result = gene.next(previous_result);
if (result.done) return;
const value = result.value;
if (value instanceof Promise) {
value.then((promise_result) => {
async(gene, promise_result);
}, (err) => {
alert(err);
}).catch((err) => {
alert(err);
});
} else {
async(gene, value);
}
}
function download(url, filename) {
const link = document.createElement('a');
const xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.responseType = 'blob';
xhr.overrideMimeType('octet/stream');
xhr.addEventListener('load', () => {
if (xhr.status === 200) {
link.href = URL.createObjectURL(xhr.response);
link.download = filename;
link.click();
} else {
alert(`Failed to Download "${filename}". Status code was not 200.`);
}
});
xhr.send();
}
function* download_playlist(playlist_url, filename) {
const origin = new URL(playlist_url).origin;
const highres_playlist_url = yield new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('GET', playlist_url);
xhr.responseType = 'text';
xhr.timeout = 5000;
xhr.addEventListener('load', () => {
if (xhr.status != 200) {
reject('Failed to Download playlist. Status code was not 200.');
return;
}
const text = xhr.response;
const lines = text.split(/\s/);
const path = lines.reverse().find((e) => /\.m3u8$/.test(e));
if (path) {
resolve(origin + path);
} else {
reject('Playlist URL was NOT FOUND');
}
});
xhr.addEventListener('timeout', () => { reject('Timeout when fetching playlist'); });
xhr.send();
});
const files = yield new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('GET', highres_playlist_url);
xhr.responseType = 'text';
xhr.timeout = 5000;
xhr.addEventListener('load', () => {
if (xhr.status != 200) {
reject('Failed to Download highres_playlist. Status code was not 200.');
return;
}
const lines = xhr.response.split(/\n/);
const files = lines.filter(e => /\.ts$/.test(e));
resolve(files);
});
xhr.addEventListener('timeout', () => { reject('Timeout when fetching highres_playlist'); });
xhr.send();
});
const fetchers = files.map((path) => {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('GET', origin + path);
xhr.responseType = 'arraybuffer';
xhr.overrideMimeType('video/mp2t');
xhr.timeout = 5000;
xhr.addEventListener('load', () => {
if (xhr.status == 200) {
resolve(xhr.response);
} else {
reject('Failed to Download highres_playlist. Status code was not 200.');
}
});
xhr.send();
});
});
Promise.all(fetchers).then((values) => {
const blob = new Blob(values, { type: 'video/mp2t' });
const blob_url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = blob_url;
link.download = filename;
link.click();
});
}
function highlight(img) {
let top, left, height, width;
({
top, left, height, width
} = img.parentNode.getClientRects()[0]);
const cover = document.createElement('div');
cover.classList.add('highlight-image');
cover.style.display = 'block';
cover.style.position = 'absolute';
cover.style.zIndex = '16777215';
cover.style.top = `${top}px`;
cover.style.left = `${left}px`;
cover.style.height = `${height}px`;
cover.style.width = `${width}px`;
cover.style.boxSizing = 'border-box';
cover.style.border = '10px solid #1DA1F2';
document.body.appendChild(cover);
}
function unhighlight() {
const covers = document.querySelectorAll('.highlight-image');
Array.prototype.forEach.call(covers, (cover) => cover.remove());
}
function* exec() {
const tweet_match = location.href.match(/\/([^/]+)\/status\/([0-9]+)/);
const image_nodes = document.querySelectorAll('.permalink[role=main] .permalink-tweet-container .AdaptiveMedia-photoContainer > img');
const video_node = document.querySelector('.permalink[role=main] .permalink-tweet-container .AdaptiveMedia-videoContainer iframe');
if (image_nodes.length > 0) {
const images = Array.from(image_nodes);
images.filter((img) => {
highlight(img);
const result = images.length === 1 || confirm('Download this image?');
unhighlight(img);
return result;
}).forEach((img, i) => {
const imgurl = img.src + (/:orig$/.test(location.href) ? '' : ':orig');
const imgurl_match = imgurl.match(/\/([\w-]+\.\w+):orig$/);
if (!(tweet_match && imgurl_match)) {
alert('Failed to download. URL does not match to a format.');
return false;
}
const index = images.length > 1 ? `_${i+1}` : '';
const filename = `${tweet_match[1]}_${tweet_match[2]}${index}_${imgurl_match[1]}`;
download(imgurl, filename);
});
} else if (video_node) {
const filename = `${tweet_match[1]}_${tweet_match[2]}`;
const video_url = yield new Promise((resolve, reject) => {
const url = video_node.src;
const xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.responseType = 'document';
xhr.timeout = 5000;
xhr.addEventListener('load', () => {
if (xhr.status == 200) {
const doc = xhr.response;
const container = doc.querySelector('#playerContainer');
const config = JSON.parse(container.getAttribute('data-config'));
const video_url = config['video_url'];
resolve(video_url);
} else {
reject('Failed to fetch playerContainer');
}
});
xhr.addEventListener('timeout', () => { reject('Timeout'); });
xhr.send();
});
if (/\.mp4$/i.test(video_url)) {
download(video_url, `${filename}.mp4`);
} else if (/\.m3u8$/i.test(video_url)) {
async(download_playlist(video_url, `${filename}.ts`));
} else {
alert('VideoURL has UNKNOWN file extension');
}
} else {
alert('This page has no media content!!');
}
}
async(exec());
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment