風柳メモ

ソフトウェア・プログラミング関連の覚書が中心

Twitter 原寸びゅー:原寸画像を開く拡張機能&ユーザースクリプト(PC用Google Chrome・Firefox・Opera等対応)



Web 版公式 Twitter上で、ツイートの画像を原寸*1で表示したり保存することが出来るようになるスクリプトです。
バージョン 0.1.5 以降、TweetDeck にも対応。
Chrome 拡張機能版(Google Chrome/Opera/Vivaldi対応)、Firefox Quantum アドオン版と、ユーザースクリプト版(Firefox/Google Chrome/Microsoft Edge/Opera対応)とがあります。
Windows 7 Professional SP1 Windows 10 Pro 上にて動作確認2016/01/16→2016/05/22→2017/01/02時点ソースコードはGitHub上にて公開しています。
また、ブックマークレット版も用意しました。こちらは保存関係の機能は使えませんが、一応 Internet Explorer 11や Microsoft Edge でも動作します。


動作イメージ

www.youtube.com
デモは Chrome 拡張機能版 0.1.4.500 のものです。
オプションの表示と変更・コンテキストメニュー(右クリック)からの保存は、拡張機能版でのみ動作します。

インストール

Chrome 拡張機能版(Google Chrome/Opera対応)

chrome.google.com
より追加。
「Twitter 原寸びゅー」のページに表示されている [+ CHROME に追加] ボタンを押してインストールして下さい。
Operaの場合、あらかじめ
https://addons.opera.com/ja/extensions/details/download-chrome-extension-9/?display=enを導入しておけば、同様にインストール可能。

Firefox Quantum アドオン版(WebExtensions)

addons.mozilla.org

より追加。
ページ上に表示されている [+ Firefox へ追加] ボタンを押してインストールして下さい。

ユーザースクリプト(Greasemonkey / Tampermonkey)版(Firefox/Google Chrome/Edge/Opera対応)

Firefox+Gresemonkey、Google Chrome+Tampermonkey、Microsoft Edge+Tampermonkey、あるいは Opera+Tampermonkeyの環境で、

Twitter 原寸びゅー・ユーザースクリプト版(twOpenOriginalImage.user.js)

GitHub - furyutei/twOpenOriginalImage: Twitter 原寸びゅー

をクリックし、指示に従ってインストール。

必要であれば、

Twitter 画像ダウンロードヘルパー(twImageDownloadHelper.user)

GitHub - furyutei/twOpenOriginalImage: Twitter 原寸びゅー

も同様にインストール。
バージョン 0.1.3.0 以降、画像ダウンロードヘルパーの機能は原寸びゅーに吸収したため、画像ダウンロードヘルパーのインストールは必要なくなりました。

ブックマークレット版(IE11/Edge/Google Chrome/Firefox/Opera対応)

Twitter 原寸びゅー

Twitter 原寸びゅー - Hatena::Let

2016年6月現在、Microsoft Edgeだと、ブックマークレットを登録するのがかなり面倒みたいですね……参考:[Windows 10]Microsoft Edgeでブックマークレットを使う。 | 初心者備忘録

使い方

基本的な使用方法

インストール後には、Web 版公式 Twitter上で、画像を含むツイートに対し、[原寸画像]ボタンが追加されるようになります。
ブックマークレット版の場合は、Twitterのタイムラインが表示された状態で、実行(ブックマークレットをクリック)してください。

このボタン(もしくはサムネイル)をクリックすることで、原寸の画像(URLの後ろに「:orig」がついたもの)を開くことができます。
タイムライン上であれば、[j][k]キーで移動後、[v]キーを押すことで、当該のツイートの画像を開くこともできます*2
デフォルトでは、

  • クリック → ひとつのページにまとめて開く
  • [Alt]+クリック → 複数画像がある場合、各画像毎にページが開く(twitter原寸ボタン互換動作)

となります。
動作を逆にしたい場合、拡張機能版ではオプション設定で変更可能です。
ユーザースクリプト版の場合は、インストール後にソースコードの一部変更が必要になります(「■ パラメータ」のところにある、「DISPLAY_ALL_IN_ONE_PAGE : true」の、true を false に書き換えます)。

0.1.4.1 以降、「ひとつのページにまとめて開く」操作をすると、別タブではなく、タイムラインと同じタブ上に、原寸画像をオーバーレイ(ポップアップ)表示するようになりました。
従来通りにしたい場合、拡張機能版ではオプション設定のオーバーレイを無効にしてください。
ユーザースクリプト版の場合、ソースコードの「■ パラメータ」のところにある、「DISPLAY_OVERLAY : true」の、true を false に書き換えます。

画像ダウンロードヘルパーが有効な場合*3、[ダウンロード]リンクが付きます。これを押すと、当該画像がダウンロードできます。
この際、ダウンロードするファイル名は、URLが例えば「…….jpg:orig」の場合には、「……-orig.jpg」に変換するようにしています……これまで、ダウンロード後に手動で拡張子を修正するのがわずらわしかったので…。

また、オーバーレイ(ポップアップ)表示時に[z]キーを押すことで、表示されている画像をまとめてZIP化したものをダウンロードできます*4

Chrome 拡張機能版のみの機能

拡張機能版の場合、オプションでデフォルト動作やボタンのテキスト等を変更できます。

また、画像を右クリックしてコンテキストメニューを開くと、直接「原寸画像を保存」もできます*5
この場合も、ダウンロードされるファイル名の拡張子は「…….jpg-orig」→「……-orig.jpg」のように書き換わります。

注意書き(不具合と対処方法等)

Chrome 拡張機能版で無効化される場合

バージョン:0.1.2以降、Twitter 画像ダウンロードヘルパーの機能を追加したため、対象となるドメインが増えています。
以前は twitter.com のみだったものに、pbs.twimg.com を追加。

バージョン:0.1.5以降では、TweetDeck対応のため、tweetdeck.twitter.com も追加されています。

バージョン:0.1.8.301以降では、「閲覧履歴の読み取り」権限を追加しています。

名称で警戒されるかもしれませんが、実際にはタブを操作するための権限であり、オプション変更時にTwitter/TweetDeckの全タブをリロードするためのものです。本拡張機能では、閲覧履歴は利用していません。

このため、それより前のバージョンで使用していて自動アップデートされた場合など、要求される権限の変更により、一時的に Google Chrome 側で無効化されてしまう(ツールバーからアイコンが消える)ことがあるようです。

この場合も、拡張機能の画面 chrome://extensions/ (「≡」(右上のハンバーガーメニュー)→「その他のツール(L)」→「拡張機能(E)」)を開き、「Twitter 原寸びゅー」を探して、「□ 有効にする」のチェックボックスにチェックを入れることで、再度有効にすることができます。

また、ダイアログが表示された場合には、[再度有効にする]([権限の許可])ボタンを押してください。


Chromeのバージョンによって、表示される画面や文言などが変更になっていることがあります。

[ダウンロード]ボタンが同じ画像に二つ表示されてしまう

Twitter 画像ダウンロードヘルパー(twImageDownloadHelper.user)をインストールしていませんか?
インストールしている場合には、これを削除しておいてください。
バージョン:0.1.3.0以降、拡張機能版・ユーザースクリプト版共に、Twitter 画像ダウンロードヘルパー(twImageDownloadHelper.user)の機能は Twitter 原寸びゅー側に吸収されたため、このスクリプトは不要となりました。

[ダウンロード]ボタンを押すと、タブが閉じられてしまう

リンクの target 属性を無効化する(target="_blank"等となっていても構わずに同一のタブに表示する)ような設定や拡張機能を入れている場合に、[ダウンロード]ボタンを押すと、画像がダウンロードされるとともに、そのタブが閉じてしまうといった現象が発生するようです。

当方の環境では、Firefox 43.0.4 にて、Tab Mix Plus 0.4.2.0 を入れている場合に発生しました。
この場合には、アドオンの設定画面 about:addons(「≡」(右上のハンバーガーメニュー)→「アドオン」)で、
「拡張機能」→(Tab Mix Plusの)[設定]→「リンク」タブ→「☑ ファイルをダウンロードするときに空白タブが開かないようにする」
のチェックを外して[適用]してやれば、タブが閉じられる現象は発生しなくなりました。
その代わりに他の動作で不都合があるかもしれませんが……。

画像の保存場所をその都度選択したい

画像のダウンロード時に保存場所を尋ねてくるかどうかは、ブラウザの設定に従います。
例えば Google Chrome であれば、
「≡」(右上のハンバーガーメニュー)→「設定(S)」→「詳細設定を表示...」→「ダウンロード」
にて、
「☑ ダウンロード前に各ファイルの保存場所を確認する」
にチェックがついていれば、ダウンロード時に保存場所が確認されます。

Firefox で ZIP ダウンロード時に警告が出る

Firefoxでは、ZIPファイルをダウンロードすると、「このファイルを開くのは危険です」という警告が表示される場合があります(危険なファイルなんて含んでいないんですが……)。
今のところ、対処方法が見つかっておりません……悪しからず。
一応、about:preferences#privacy(「≡」→「オプション」→「プライバシーとセキュリティ」)→「セキュリティ」→「フィッシング防護」→「☑不要な危険ソフトウェアを警告する」のチェックを外すと警告は出なくなりますが、その分他のサイトで危険が増すのでお勧めはできません。

開発の経緯

Twitterの画像は、縮小やトリミングがされていたりサムネイル状態だったりで、見難いことも多いです。
画像のURLの後ろに「:orig」と付けてやれば原寸画像を開くことはできますが、いちいちやるのは面倒で……と思っていたら、便利な拡張機能が公開されていることを知りました。

hogashi 氏(id:hogashi/[twitter:@hogextend])の、
hogashi.hatenablog.com
というものです。

大変便利でしたが、ひとつのツイートに複数の画像がある場合には複数のタブが開いてしまい、ちょっとわずらわしかったため、とりあえずブックマークレット
let.hatelabo.jp
などを作ったりしている内に、興が乗ってしまい、いちから実装してしまった次第です。
なお、ひととおり作って満足してしまったため、今後の更新を行うかどうか怪しいです(汗)。不安な方は
twitter原寸ボタンの方をインストールすることをお勧めします。

*1:画像のURLに『:orig』もしくは『name=orig』がついているものを便宜上『原寸』としています。実際には縮小されている場合もあります

*2:2016/02/01・0.1.3.1以降

*3:拡張機能版は0.1.2.0以降・ユーザースクリプト版は0.1.3.0以降。なお、ブックマークレット版では、画像ダウンロードヘルパー機能は未サポート。

*4:0.1.7.1 以降

*5:2016/02/02・0.1.3.101以降

ZIPファイルに日本語ファイル名が含まれる場合の文字化け予防方法(Windows7, CentOS 6)

他のシステムで作成されたZIPファイルを解凍した際に日本語ファイル名が文字化けする不具合への対処法をいくつか。



Windows7 の場合

標準の状態だと、Shift-JIS(CP932)以外でエンコードされていると、文字化けする。

これまでは

無料圧縮・解凍フリーソフト CubeICE - CubeSoft

などを使うことで対処していたが、日本語ファイル名が UTF-8 でエンコードされているものについては、Windows7・Windows Server 2008 R2 用 Hotfix を適用すれば解消される模様。

File names are corrupted after you decompress a .zip file in Windows 7 or in Windows Server 2008 R2

CentOS 6 の場合(unzip)

CentOS 6 系で yum を使ってインストールできる unzip (CentOS 6.6では現在 unzip-6.0-2.el6_6.x86_64) では、日本語ファイル名には未対応のため、エンコードに関わりなく文字化けしてしまう。

【UTF-8 エンコード時】

$ /usr/bin/unzip -l ./AmazonMusicDownload.zip
Archive:  ./AmazonMusicDownload.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
  8381815  10-11-2015 01:53   ?????????????(???????????????)/Cherish you/01 - Cherish you.mp3
  9099388  10-11-2015 01:53   ?????????????+?????????????????/?????????????+????????????????? ??????????????+??+????????+???????????????/01
- Blooming Lily.mp3
  8447415  10-11-2015 01:54   ?????????????+?????????????????/?????????????+????????????????? ??????????????+??+????????+???????????????/03
- ???????????????????????????.mp3
---------                     -------
 25928618                     3 files

【Shift-JIS エンコード時】

$ /usr/bin/unzip -l ./sjis-sample.zip
Archive:  ./sjis-sample.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
        0  10-11-2015 10:59   ?????+???????????-????/?????+???????????-???? ?L?????N?^?[?C???[?W?\???O?W/
  9099388  10-11-2015 01:53   ?????+???????????-????/?????+???????????-???? ?L?????N?^?[?C???[?W?\???O?W/01 - Blooming Lily.mp3
  8447415  10-11-2015 01:54   ?????+???????????-????/?????+???????????-???? ?L?????N?^?[?C???[?W?\???O?W/03 - ?`???X?L?????_???X.mp3
        0  10-11-2015 10:59   ?X?????q??(???????F??)/Cherish you/
  8381815  10-11-2015 01:53   ?X?????q??(???????F??)/Cherish you/01 - Cherish you.mp3
---------                     -------
 25928618                     5 files


そこで、unzip 6.0 Shift-JIS対応版 をインストールすることで対応してみることに。

unzip 6.0 Shift-JIS対応版のインストール方法
$ git clone https://github.com/ted-n/unzip.git
$ cd unzip/unzip60
$ make -f unix/Makefile LOCAL_UNZIP="-D_FILE_OFFSET_BITS=64 -DNO_LCHMOD -D_MBCS -DNO_WORKING_ISPRINT" generic_gcc
$ sudo make -f unix/Makefile install

これで、/usr/local/bin 下にパッチの当たった unzip がインストールされる。
一行目を修正("git clone git@github.com:ted-n/unzip.git"としていたが、これは予めSSH Keyの設定が必要なため)

【UTF-8 エンコード時】

$ /usr/local/bin/unzip -l ./AmazonMusicDownload.zip
Archive:  ./AmazonMusicDownload.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
  8381815  10-11-2015 01:53   氷堂美智留(矢作紗友里)/Cherish you/01 - Cherish you.mp3
  9099388  10-11-2015 01:53   冴えない彼女の育てかた/冴えない彼女の育てかた キャラクターイメージソング集/01 - Blooming Lily.mp3
  8447415  10-11-2015 01:54   冴えない彼女の育てかた/冴えない彼女の育てかた キャラクターイメージソング集/03 - 饒舌スキャンダラス.mp3
---------                     -------
 25928618                     3 files

【Shift-JIS エンコード時】

$ /usr/local/bin/unzip -l ./sjis-sample.zip
Archive:  ./sjis-sample.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
        0  10-11-2015 10:59   冴えない彼女の育てかた/冴えない彼女の育てかた キャラクターイメージソング集/
  9099388  10-11-2015 01:53   冴えない彼女の育てかた/冴えない彼女の育てかた キャラクターイメージソング集/01 - Blooming Lily.mp3
  8447415  10-11-2015 01:54   冴えない彼女の育てかた/冴えない彼女の育てかた キャラクターイメージソング集/03 - 饒舌スキャンダラス.mp3
        0  10-11-2015 10:59   氷堂美智留(矢作紗友里)/Cherish you/
  8381815  10-11-2015 01:53   氷堂美智留(矢作紗友里)/Cherish you/01 - Cherish you.mp3
---------                     -------
 25928618                     5 files
Python で Shift-JIS エンコードしたファイル名で圧縮する場合の注意

自分はこれまで、Linux(CentOS) 上の Python で日本語ファイル名を含んだ ZIP ファイルを作成する場合でも、Windows で扱いやすくするために Shift-JIS(CP932) でエンコードしたファイル名で圧縮していたのだが、こうして作成したファイルを上記のパッチ済みの unzip で表示すると、なぜか文字化けしてしまった。

【オリジナルの unzip 6.0 使用時】

$ /usr/bin/unzip -l ./test.zip
Archive:  ./test.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
      400  10-11-2015 15:57   ???{????f?B???N?g??/???{???t?@?C????.txt
---------                     -------
      400                     1 file

【パッチ済みの unzip 使用時】

$ /usr/local/bin/unzip -l ./test.zip
Archive:  ./test.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
      400  10-11-2015 15:57   傔
                                ・fB
                                    Ng
                                      /傔
                                         黎@C
                                             シ.txt
---------                     -------
      400                     1 file

調べてみると、どうも ZIP ファイルヘッダの version(ZipInfo.create_system) が 3 (Unix) になっているとこの現象が発生するようなので、これを考慮して Python スクリプトを作成する必要がある模様。
zipfile.py の class ZipInfo の定義を見てみると、sys.platform が 'win32' のときは self.create_system に 0 が、それ以外は 3 がデフォルトで設定されている。

【例】

import czipfile as zipfile
import datetime

zip = zipfile.ZipFile( 'test.zip', 'w' )
timestamp = datetime.datetime.now()
dir_filename = u'日本語のディレクトリ/日本語ファイル名.txt'
content = 'test' * 100

zipinfo = zipfile.ZipInfo(
  filename = dir_filename.encode( 'cp932', 'replace' ),
  date_time = ( timestamp.year, timestamp.month, timestamp.day, timestamp.hour, timestamp.minute, timestamp.second )
)
zipinfo.compress_type = zipfile.ZIP_DEFLATED
zipinfo.create_system = 0 # 0 - MS-DOS and OS/2 (FAT / VFAT / FAT32 file systems), 3 - UNIX (https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT)

zip.writestr( zipinfo, content )
zip.close()
$ /usr/local/bin/unzip -l ./test.zip
Archive:  ./test.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
      400  10-11-2015 15:59   日本語のディレクトリ/日本語ファイル名.txt
---------                     -------
      400                     1 file

追記(2015/10/26)

【覚書】はてなブログのレスポンシブWebデザイン対応

ほんとうに今更ながら、はてなブログはテーマ(テンプレート)によってはレスポンシブWebデザインに対応していることに気付く。
スマートフォンでアクセスする際に「PCのときとURLが変わってしまうのは不便だなー」と思っていたことだし、本ブログも対応してみることに。

手順1:レスポンシブ対応のテーマをインストール

テーマ ストアでレスポンシブ対応のテーマを探してインストール。
なお現状、レスポンシブ対応のテーマを探す機能などはないので、自力で探すしかない。

自分は、たまたま目についたシンプルな

Simple Gray - テーマ ストア - はてなブログ

をインストールしてベースにさせていただいた。


今気が付いたが、レスポンシブ対応テーマを探すには、

はてなブログのレスポンシブデザイン対応のテーマ集24個【随時更新】(スマホでもPCとほぼ同じ表示になるテンプレート) - 広汎性発達障害の女が毒を吐くブログ

の記事が便利そう。

手順2:CSS等の調整

デザイン設定で、CSSを適当に(好みに合わせて)調整。
自分の場合、はてなダイアリーのときに使用していたテーマの色合いが気に入っていたので、それに近い感じにしてみた。

また、ヘッダの下に検索フォームを設置し、画面の横幅が 1150px 以下のときには表示されるようにしておいた。
サイドバーが下に落ちて、検索フォームにアクセスしづらくなったときの代用。

手順3:レスポンシブ(スマートフォン)対応設定

デザインCSS先頭に、

/*
  Responsive: yes
 */

のようなコメントを追加したのち、スマートフォン→詳細設定にて、☑ レスポンシブデザインにチェックを入れる。

参考

レスポンシブデザインのテーマを作成する際の注意点

デザインCSSに Responsive: yes のコメントを入れるのは、忘れやすいので注意。

気が付いたら jQuery 用の highlight プラグインを書いていた

先日、自分のココログを全文検索するブログパーツ: 暴想の改修を行っていたのだが、furyu.tea-nifty.com
元のスクリプトのハイライト処理だと、TEXTAREA 内まで置換されてしまうのが難点だったため、せっかくなので jQuery を用いて実装してみた。github.com

$.setHighlightColor('yellow'); // .highlight要素に付けるハイライト(背景)色を指定(引数を指定しない場合、デフォルト(#ffcc33)になる)

$('p').highlight('キーワード'); // 全 p 要素内で、"キーワード" というテキストをハイライトする(引数として配列を指定することで、複数のキーワード指定も可能)

$('p').unhighlight(); // 全 p 要素内のハイライトを取り消す

style・script・textarea・iframe・frame 要素内は避けてハイライトするようになっている。
highlight()のオプションはjQuery Highlight Plugin | bartaz @ GitHub互換っぽくなっているはず。

裏話

最初はjq-cocolog_ajax_search.js内にベタ書きしていたのだが、せっかく jQuery 化したことだし、プラグインとして独立させることに。

例によって、ひととおり実装したところで、ふと検索してみると、
highlight: JavaScript text higlighting jQuery plugin
とか、
jQuery Highlight Plugin | bartaz @ GitHub
といったプラグインが見つかったので、オプションは後者のものと互換っぽくしておいた。
すなおに最初から調べて使っておけ、という話。

Google ChromeにてDOMノード追加に異様に時間がかかる現象が発生して悩む→拡張機能が原因だった

JavaScriptでページ遷移用のナビゲーションを作っていたのだが、リンクと半角スペース(テキストノード)とを交互に挿入するような実装にしていると、なぜか異様に時間がかかってしまう(数百個程度のノード挿入に、数秒要する)。
どうやら、ナビゲーションのノードがDOMツリー上にあるとこうなるようで、いったんDOMツリーから切り離した状態で作成し、最後にDOMツリーに戻すようにすると、ほとんど時間はかからない。でもなぜ……?


さんざん悩んだが、結局はとある拡張機能が原因だった、というオチ(今回の場合ははちまバスター)。
もっと早くに、シークレットウィンドウで調べておくべきだった……。
【追記】なお、はちまバスターは早々に改修された

テスト

function test_add_nodes( options ) {
    if ( ! options ) {
        options = {}
    }
    var parent_node = ( options.parent_node ) ? options.parent_node : document.body,
        before_processing = !! ( options.before_processing ),
        node_number = ( 0 < options.node_number ) ? options.node_number : 300,
        exclude_link = !! ( options.exclude_link ),
        exclude_text = !! ( options.exclude_text );
    
    var container = document.createElement('div'),
        link_node_template = document.createElement('a');
        text_node_template = document.createTextNode('');
        before_time = new Date().getTime();
    
    if ( parent_node && before_processing ) {
        parent_node.insertBefore( container, parent_node.firstChild );
    }
    for ( var ci=0; ci < node_number; ci++ ) {
        if ( ! exclude_link ) {
            var link_node = link_node_template.cloneNode( true );
            link_node.href = '#';
            link_node.innerHTML = ci;
            container.appendChild( link_node );
        }
        if ( ! exclude_text ) {
            var text_node = text_node_template.cloneNode( true );
            text_node.textContent = '*';
            container.appendChild( text_node );
        }
    }
    if ( parent_node && ( ! before_processing ) ) {
        parent_node.insertBefore( container, parent_node.firstChild );
    }
    var after_time = new Date().getTime(),
        elapsed_time = ( after_time - before_time );
    
    return elapsed_time;
}

function log_result( test_name, elapsed_time ) {
	console.log( test_name + ' : ' + ( elapsed_time / 1000.0) + ' sec' );
}

// DIV要素にリンクやテキストノードを一通り追加してから、DOMツリーに挿入
log_result( 'after , text only', test_add_nodes( { before_processing : false, exclude_link : true, exclude_text : false } ) );
log_result( 'after , link only', test_add_nodes( { before_processing : false, exclude_link : false, exclude_text : true } ) );
log_result( 'after , both     ', test_add_nodes( { before_processing : false, exclude_link : false, exclude_text : false } ) );

// DIV要素をDOMツリーに予め挿入してから、リンクやテキストノードを追加
log_result( 'before, text only', test_add_nodes( { before_processing : true, exclude_link : true, exclude_text : false } ) );
log_result( 'before, link only', test_add_nodes( { before_processing : true, exclude_link : false, exclude_text : true } ) );
log_result( 'before, both     ', test_add_nodes( { before_processing : true, exclude_link : false, exclude_text : false } ) );
拡張機能有効時
after , text only : 0.003 sec
after , link only : 0.057 sec
after , both      : 0.054 sec
before, text only : 0.04 sec
before, link only : 0.04 sec
before, both      : 6.856 sec // これは酷い
拡張機能無効時
after , text only : 0.002 sec
after , link only : 0.005 sec
after , both      : 0.01 sec
before, text only : 0.027 sec
before, link only : 0.045 sec
before, both      : 0.05 sec