2017-09-10

Headless Chromeを試してみる(Node.jsとchrome-remote-interfaceでGoogleログイン)

Chrome 59からヘッドレスモードが標準搭載されたそうです。ヘッドレスモードは、 --headless オプション(現在は --disable-gpu も?)を付けてchromeを起動すると画面無しで起動するというものです。

画面が無いのでどうやって操作するんだという話になるのですが、開発用のリモートAPI(Chrome DevTools Protocol)があるので、それを通して操作することになります。

node.js用のモジュールとして chrome-remote-interface というものがあります。これを使用するとnode.js上のコードからこのリモートAPIを経由してChromeを操作できます。

chrome-remote-interface を使う前にヘッドレスモードでChromeを起動する必要があるのですが、これは "C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --headless --disable-gpu --remote-debugging-port=9222 https://www.google.com/ ようにコマンドラインから起動しても良いのですが、 chrome-launcher というnode.jsモジュールもあります。それを使うと次のコードでheadless chromeが起動できます。

const chromeLauncher = require('chrome-launcher');

chromeLauncher.launch({
  startingUrl: "https://google.com",
  chromeFlags: ["--headless", "--disable-gpu"]
}).then(chrome => {
  console.log(`Chrome debugging port running on ${chrome.port}`);
});

chrome-launcher はchromeへのパスを補ってくれるだけでは無く、クリーンなユーザープロファイルを用意するなどの処理も行ってくれます。これは自動テストの時などに便利な場合がありますが、普段使っているプロファイルを元に自動処理をしたい場合には不便かもしれません。一応userDataDirオプションでディレクトリを指定できるようになっています(chromeFlagsに–user-data-dirを直接指定するのは良くないと思われる)。

というわけで、試しにGmailの未読件数を求めるコードを書いてみました。 chrome-launcher でChromeを起動し、 chrome-remote-interface を使用してChromeへアクセスしてGoogleにログインしGmailの未読件数を求めます。GmailなんてAPIを通せば済むと思いますが、何かログインが絡む手頃な例が思いつかなかったので。

//
// chrome-remote-interfaceを使用してGmailの未読件数を求める。
// 2017-09-10
//

const chromeLauncher = require('chrome-launcher');
const CDP = require('chrome-remote-interface');

// 標準入力から文字列を受け付ける。
function prompt(question) {
    return new Promise((resolve, reject) => {
        process.stdout.write(question);
        process.stdin.resume();
        process.stdin.once("data", (data) => {
            resolve(data.toString().trim());
            process.stdin.unref(); //call Socket.unref()
        });
    });
}

// Chromeを起動してdevtoolsのドメインを有効にしてurlを開く。
function launchChrome(url){
    return new Promise((resolve, reject)=>{
        chromeLauncher.launch({
            //startingUrl: "https://google.com",
            //chromeFlags: ["--headless", "--disable-gpu"], // <= ヘッドレスモードにするなら指定する。なぜかuserDataDirが効かない? なぜかcloseChrome()が効かない?
            //userDataDir: "c:\\home\\k-aki\\tmp\\chromeuser" // <= 毎回ログインしたくないなら指定する。
        }).then(chrome => {
            console.log(`Chrome pid=${chrome.pid} port=${chrome.port}`);

            CDP({port: chrome.port}, (devtools) => {
                // ドメインを有効にする
                Promise.all([
                    devtools.Page.enable(),
                    devtools.Runtime.enable()
                ]).then(() => {
                    // ページを移動する。
                    devtools.Page.navigate({url: url});
                    devtools.Page.loadEventFired(() => {
                        resolve({chrome: chrome, devtools:devtools});
                    });
                })
            }).on('error', (err) => {
                console.error('Cannot connect to browser:', err);
                chrome.kill();
                reject();
            });
        });
    });
}

// 全てのタブを閉じます。
// chrome-launcherのkill()だとクッキー等が保存されない場合があるので。
function closeChrome(port){
    CDP.List({port:port}, (err, targets)=>{
        if(!err){
            console.log(targets);
            targets.forEach((target)=>{
                CDP.Close({port:port, id:target.id});
            });
        }
    });
}


// Gmailの未読件数を取得する。
launchChrome("https://mail.google.com/").then((env)=>{
    const {chrome, devtools} = env;

    // devtools
    function eval(expr){
        return devtools.Runtime.evaluate({expression: expr});
    }
    function check(cond){
        return eval("!!(" + cond + ")");
    }
    function dqs(selector){
        return ("document.querySelector(\"" + selector + "\")");
    }
    function waitFor(cond){
        return new Promise((resolve, reject)=>{
            function checkCond(){
                check(cond).then((result)=>{
                    if(result.result.value) {
                        resolve();
                    }
                    else {
                        setTimeout(checkCond, 500);
                    }
                });
            }
            checkCond();
        });
    }

    // Googleログイン(2017-09-10)
    //   ID
    const LOGIN_ID_INPUT = "input#identifierId";
    const LOGIN_ID_NEXT = "#identifierNext";
    //   Password
    const LOGIN_PASS_INPUT = "input[name=password]";
    const LOGIN_PASS_NEXT = "#passwordNext";
    //   Time-based One-time Password
    const LOGIN_TOTP_INPUT = "input#totpPin";
    const LOGIN_TOTP_NEXT = "#totpNext";

    function sendLoginEntry(inputSelector, nextButtonSelector, value){
        const js =
            '(()=>{'+
            '    const input = ' + dqs(inputSelector) + ';'+
            '    if(input){'+
            '        input.value = "' + value + '";'+
            '        ' + dqs(nextButtonSelector) + '.click();'+
            '    }'+
            '})()';
        return devtools.Runtime.evaluate({expression: js});
    }
    function sendUserId(id){
        return sendLoginEntry(LOGIN_ID_INPUT, LOGIN_ID_NEXT, id);
    }
    function sendPassword(password){
        return sendLoginEntry(LOGIN_PASS_INPUT, LOGIN_PASS_NEXT, password);
    }
    function sendTOTP(totp){
        return sendLoginEntry(LOGIN_TOTP_INPUT, LOGIN_TOTP_NEXT, totp);
    }
    function loginGoogle(){
        return new Promise((resolve, reject)=>{
            check(dqs(LOGIN_ID_INPUT)).then((result)=>{
                if(result.result.value){
                    prompt("User ID:")
                        .then((id)=>sendUserId(id))
                        .then(()=>waitFor(dqs(LOGIN_PASS_INPUT)))
                        .then(()=>prompt("Password:"))
                        .then((password)=>sendPassword(password))
                        .then(()=>prompt("One-time Password:")) ///@todo TOTPを要求しない場合でもTOTPを受け付けて送信している。
                        .then((totp)=>sendTOTP(totp))
                        .then(()=>{ resolve();});
                }
                else{
                    //already logged in.
                    resolve();
                }
            });
        });
    }

    // for Gmail
    function waitForGmailOpen(){
        return waitFor('document.title != "Gmail"');
    }
    function getUnreadCount(){
        return new Promise((resolve, reject)=>{
            eval("document.title").then((result)=>{
                const title = result.result.value;
                resolve(/\(([0-9]+)\)/.exec(title)[1]);
            });
        });
    }

    loginGoogle()
        .then(()=>waitForGmailOpen())
        .then(()=>getUnreadCount())
        .then((result)=>{
            // 結果を出力する。
            console.log("unread=" + result);
            // 閉じる。
            devtools.close();
            //chrome.kill();
            closeChrome(chrome.port);
        });

});

ページを開いたときにGoogleのログインフォームが検出されたらログイン処理をします。ユーザー名、パスワード、二段階認証コードをコンソールから受け付けてフォームへ書き込み「次へ」ボタンをクリックしてログインを完了させます。

ログインが済んでいる(済んだ)ならGmailが開くのを待ちます。title要素に未読件数などが表示されるので、これが出るまで一定時間間隔でチェックするようにしました。出たら未読件数の数字部分を取り出して出力します。

Chromeを閉じる処理は最初 chrome-launcher のkill()を使用してみたのですが、2回目以降の起動時にChromeが前回正常に終了しなかったと言われてしまう(クッキーも保存されていない)ので chrome-remote-interface を通して閉じる方法も試しました。

--headless を指定していないときはこれでうまく動いたのですが、指定したら二つ問題がありました。一つは毎回ログインを要求されること。どうもユーザープロファイルをちゃんと読み込んでくれていないようです。ヘッドレスモードはデフォルトでシークレットモードになるらしいことが関係しているのかもしれません。二つ目はChromeが終了しないこと。 chrome-remote-interface を通して全てのタブを閉じるコード(closeChrome)ではなぜか終了しないみたいです。

参考URL:

2017-09-04

この一年で行ったところ

9月になって涼しい日が続いていますね。今年の夏は去年行けなかった分あちこち行こうと思ったのですが、天気の悪い日が多くてそれほど行けませんでした。このまま秋になってしまうのかな。

この1年、去年の秋くらいから行ったところ:

2017-09-04-IMG_20161020_104522.jpg 2017-09-04-IMG_20161031_112229.jpg 2017-09-04-IMG_20170425_110937_0.jpg 2017-09-04-IMG_20170508_114708.jpg 2017-09-04-P_20170605_145354.jpg 2017-09-04-P_20170721_090344.jpg 2017-09-04-P_20170803_120408_HDR.jpg 2017-09-04-P_20170826_122422_HDR.jpg

まだまだ行きたいところがいっぱいあるなぁ。

2017-08-22

高速バスの乗り方

自宅から中央道日野バス停への行き方が本当に悩ましい。歩けないこともないけど疲れる距離。自転車ならすぐだけど当然駐輪場は無い。公共交通機関を使うとなると、バスと電車とモノレールを乗り継いで倍以上の距離になるから徒歩とそれほど時間が変わらない上にお金もかかるし遅延や乗り逃しのリスクがある(ので早めに出るとなると結局徒歩の方が早い)。タクシー、うーん、せっかく高速バスの安さで良い気分になっているところに冷や水が。なので結局「歩くか……」ということに。いっその事バスタ新宿まで行ってしまおうかと思ったりもするが、バス停までの時間はそれほど変わらなくても新宿から中央道日野までの時間と運賃の増加はなんだか馬鹿らしい。

2017-07-31

2017年夏の新番組

既に3~4話くらいまで進んでいますが、今のところ一番楽しめているのは賭グルイでしょうか。 頑張って1話はできるだけ見ましたが、最近は少しでも気に入らないところがあるとすぐに切ってしまうので今見ているのはそれほど多くは無いはず……。 といってもそれなりに見ているみたいですが。あ、前期からの続きですが、サクラダリセットは私は好きですよ。

1話評 開始日時 曜日 時間 チャネル タイトル
07/01(土) –:–~ –:-- Netflix ソニックトゥーン
× 07/01(土) 21:00~ 21:00 TOKYO MX 縁結びの妖狐ちゃん
07/01(土) 22:00~ 22:00 TOKYO MX 賭ケグルイ
07/01(土) 22:30~ 22:30 TOKYO MX ひなろじ ~from Luck & Logic~
07/01(土) 24:00~ 24:00 TOKYO MX Fate/Apocrypha
× 07/01(土) 24:30~ 24:30 TOKYO MX 活撃 刀剣乱舞
07/01(土) 25:00~ 25:00 TOKYO MX 戦姫絶唱シンフォギアAXZ (第4期)
07/02(日) 22:30~ 22:30 TOKYO MX ナイツ&マジック
07/02(日) 24:00~ 24:00 TOKYO MX 潔癖男子!青山くん
× 07/02(日) 24:30~ 24:30 TOKYO MX バトルガール ハイスクール
07/02(日) 25:00~ 25:00 TOKYO MX スカートの中はケダモノでした
07/02(日) 27:30~ 27:30 テレビ東京 闇芝居 第5期
07/03(月) 23:00~ 23:00 TOKYO MX 妖怪アパートの幽雅な日常
× 07/03(月) 24:00~ 24:00 TOKYO MX 恋と嘘
07/03(月) 25:35~ 25:35 テレビ東京 異世界食堂
07/04(火) 23:00~ 23:00 TOKYO MX アホガール
07/04(火) 23:15~ 23:15 TOKYO MX 徒然チルドレン
07/04(火) 25:59~ 25:59 日本テレビ ナナマル サンバツ
07/05(水) 27:40~ 27:40 TOKYO MX 捏造トラップ -NTR-
07/06(木) 24:00~ 24:00 TOKYO MX 最遊記RELOAD BLAST
07/06(木) 24:55~ 24:55 フジテレビ DIVE!!
07/06(木) 25:58~ 25:58 TBS コンビニカレシ
× 07/06(木) 26:28~ 26:28 TBS アクションヒロイン チアフルーツ
07/07(金) –:–~ –:-- Netflix キャッスルヴァニア(悪魔城ドラキュラ) 第1シーズン
07/07(金) 21:55~ 21:55 TOKYO MX キャッチーくんのナイスキャッチ!
07/07(金) 22:00~ 22:00 TOKYO MX 18if
07/07(金) 24:30~ 24:30 TOKYO MX 時間の支配者
07/07(金) 25:05~ 25:05 TOKYO MX RWBY Volume 1-3: The Beginning
07/07(金) 25:40~ 25:40 TOKYO MX メイドインアビス
07/07(金) 26:25~ 26:25 TBS 将国のアルタイル
07/08(土) 25:30~ 25:30 TOKYO MX ひとりじめマイヒーロー
07/09(日) 22:00~ 22:00 TOKYO MX セントールの悩み
07/09(日) 23:00~ 23:00 TOKYO MX プリンセス・プリンシパル
07/09(日) 23:30~ 23:30 TOKYO MX ボールルームへようこそ
× 07/10(月) 24:30~ 24:30 TOKYO MX 天使の3P!(スリーピース)
07/11(火) 12:00~ 12:00 ニコニコ動画 無責任ギャラクシー☆タイラー
07/11(火) 24:30~ 24:30 TOKYO MX NEW GAME!! (第2期)
07/11(火) 25:35~ 25:35 テレビ東京 魔法陣グルグル -MAGICAL CIRCLE-(新作)
07/12(水) 22:00~ 22:00 TOKYO MX カイトアンサ
× 07/12(水) 22:10~ 22:10 TOKYO MX イケメン戦国 時をかけるが恋は始まらない
07/12(水) 22:30~ 22:30 TOKYO MX クリオネの灯り
07/12(水) 22:40~ 22:40 TOKYO MX てーきゅう 第9期
07/12(水) 22:45~ 22:45 TOKYO MX ノラと皇女と野良猫ハート
07/12(水) 22:50~ 22:50 TOKYO MX SHADOW OF LAFFANDOR ラファンドール国物語
07/12(水) 25:05~ 25:05 TOKYO MX ようこそ実力至上主義の教室へ
07/12(水) 25:35~ 25:35 TOKYO MX はじめてのギャル
07/13(木) 22:00~ 22:00 TOKYO MX 異世界はスマートフォンとともに。
× 07/13(木) 23:30~ 23:30 TOKYO MX ゲーマーズ!
07/14(金) 22:30~ 22:30 TOKYO MX バチカン奇跡調査官
07/14(金) 24:00~ 24:00 TOKYO MX 地獄少女 宵伽 (第4期)
07/22(土) 23:00~ 23:00 NHK総合 THE REFLECTION WAVE ONE
2017-05-06

2017年春の新番組

今期はどうでしょうね。頑張って一通り見ましたが、今期はパッとしない印象。

  開始日時 チャネル タイトル
04/01(土) 08:00~ テレビ東京系 フューチャーカード バディファイト バッツ(第4期)
04/01(土) 19:00~ YouTube モンスターストライク 2ndシーズン
× 04/01(土) 21:00~ TOKYO MX 銀の墓守り(ガーディアン)
04/01(土) 21:15~ TOKYO MX TO BE HERO
04/01(土) 22:00~ TOKYO MX 進撃の巨人 Season2
× 04/01(土) 24:00~ TOKYO MX GRANBLUE FANTASY(グランブルーファンタジー) The Animation
04/02(日) 07:30~ テレビ東京系 アバローのプリンセス エレナ
04/02(日) 08:30~ テレビ東京系 デュエル・マスターズ
04/02(日) 09:00~ テレビ東京系 ふるさとめぐり 日本の昔ばなし
04/02(日) 18:45~ 群馬テレビ ワールドフールニュース シーズン2
04/02(日) 22:30~ TOKYO MX アリスと蔵六
04/02(日) 24:30~ TOKYO MX つぐもも
04/02(日) 25:00~ TOKYO MX 僧侶と交わる色欲の夜に…
04/02(日) 27:35~ テレビ東京 世界の闇図鑑
04/03(月) 17:55~ テレビ東京系 ベイブレードバースト 神(ゴッド)
04/03(月) 18:00~ NHK Eテレ わしも(WASIMO) 第5シリーズ
04/03(月) 18:10~ NHK Eテレ 忍たま乱太郎 第25シリーズ
04/03(月) 21:55~ TOKYO MX アキンド星のリトル・ペソ
04/03(月) 23:00~ TOKYO MX 笑ゥせぇるすまんNEW
04/03(月) 24:00~ TOKYO MX スタミュ-高校星歌劇- 第2期
× 04/03(月) 25:05~ TOKYO MX フレームアームズ・ガール
04/04(火) 17:55~ テレビ東京系 アイドルタイムプリパラ
04/04(火) 18:25~ テレビ東京系 レゴ ニンジャゴー 時空の支配者編
04/04(火) 18:45~ NHK Eテレ 少年アシベ GO!GO!ゴマちゃん 第2シリーズ
04/04(火) 21:55~ TOKYO MX アイドルマスター シンデレラガールズ劇場
04/04(火) 24:30~ TOKYO MX ロクでなし魔術講師と禁忌教典
04/04(火) 26:05~ テレビ東京 王室教師ハイネ
04/05(水) 17:55~ テレビ東京系 BORUTO-ボルト- NARUTO NEXT GENERATIONS
04/05(水) 18:00~ NHK Eテレ おじゃる丸 第20シリーズ
04/05(水) 18:45~ NHK Eテレ ねこねこ日本史 第2シリーズ
04/05(水) 22:45~ TOKYO MX まけるな!! あくのぐんだん!
× 04/05(水) 22:45~ TOKYO MX ラブ米-WE LOVE RICE-
04/05(水) 23:30~ TOKYO MX サクラダリセット
04/05(水) 24:00~ TOKYO MX サクラクエスト
04/05(水) 25:35~ TOKYO MX 武装少女マキャヴェリズム
04/06(木) 24:00~ TOKYO MX 月がきれい
04/06(木) 25:41~ SBS パンパカパンツ おNEWさん(第3期)
04/06(木) 25:58~ TBS クロックワーク・プラネット
04/06(木) 26:29~ TBS カブキブ!
× 04/06(木) 26:35~ テレビ東京 恋愛暴君
04/07(金) 17:55~ テレビ東京系 リルリルフェアリル~魔法の鏡~
04/07(金) 21:55~ TOKYO MX 兄に付ける薬はない!
04/07(金) 22:00~ TOKYO MX ツインエンジェルBREAK
○+ 04/07(金) 22:30~ TOKYO MX 正解するカド KADO:The Right Answer
04/07(金) 25:35~ TOKYO MX 信長の忍び 第2期
04/07(金) 25:40~ TOKYO MX ひなこのーと
04/07(金) 25:55~ TBS 神撃のバハムート VIRGIN SOUL (第2期)
04/07(金) 26:25~ TBS ベルセルク
04/08(土) 17:35~ NHK Eテレ 境界のRINNE 第3期
04/08(土) 23:30~ TOKYO MX Re:CREATORS(レクリエイターズ)
04/08(土) 24:30~ TOKYO MX エロマンガ先生
04/09(日) 22:00~ TOKYO MX 有頂天家族2
04/09(日) 23:00~ TOKYO MX ID-0
04/10(月) 24:30~ TOKYO MX ゼロから始める魔法の書
× 04/11(火) 23:00~ TOKYO MX 覆面系ノイズ
04/11(火) 25:35~ テレビ東京 夏目友人帳 陸 (第6期)
04/12(水) 22:30~ TOKYO MX 喧嘩番長 乙女-Girl Beats Boys-
04/12(水) 22:40~ TOKYO MX Room Mate ~One Room side M~
04/12(水) 25:05~ TOKYO MX 終末なにしてますか? 忙しいですか? 救ってもらっていいですか?
× 04/13(木) 19:25~ テレビ東京系 スナックワールド
04/13(木) 24:55~ フジテレビ 冴えない彼女の育てかた♭ (第2期)
× 04/14(金) 24:30~ TOKYO MX ソード・オラトリア ダンジョンに出会いを求めるのは 間違っているだろうか 外伝
× 04/14(金) 25:05~ TOKYO MX sin 七つの大罪
04/15(土) 06:30~ TBS系 100%パスカル先生 & プリプリちぃちゃん!!
04/15(土) 07:00~ TBS系 トミカハイパーレスキュー ドライブヘッド 機動救急警察
04/15(土) 23:00~ NHK総合 アトム ザ・ビギニング

なんとなく気になるのは:

  • ID-0
  • 月がきれい
  • サクラダリセット
  • 正解するカド
  • 兄に付ける薬はない!
  • TO BE HERO

「兄に付ける薬はない!」は中国のWeb漫画が原作、「TO BE HERO(再放送)」はハオライナーズという中国のアニメブランド作品で日中合作とか。近年中国由来アニメの放送が増えてきていますね。私はアニメは完全に趣味であり、作り手のことは極力考えず、与えられたものをただ純粋に鑑賞するよう心がけております。時秒もミンちゃんも可愛らしいですね。

最初から見ないものが増えてきました。アニメ力が衰えてきましたかね。いや、やることなすこと全てそんな感じですが。

2017-04-11

Android StudioでAndroidアプリを作る

特定の形式のメモをサッと取るための簡単なアプリが欲しかったので、久しぶりにAndroid Studioを入れて作っていました。

なんだかんだ言ってIDEは便利ですよね。

便利なテンプレート、入力補完、検証、警告、アドバイス、使っているだけで色々勉強になります。

いつのまにかEmacsキー割り当ても標準で入ってます。

2017-04-11

迷惑メールフィルタ

何年か前の某社のアカウント情報流出以降、迷惑メールが多くて困っていたのですが、使っているレンタルサーバの迷惑メールフィルタ機能(Spam Assassinベース)を使ってみたら大分マシになりました。

設定や学習なんかが面倒くさそうだと思っていたのですが、ウェブメールサービスがあるのでそちらからメールを振り分けて学習させればOK。このウェブメールサービス自体も結構イケてますし。

そうして振り分けられたメールのタイトルを眺めているのですが、なんというか、同じような表現の多さに胸焼けがしてきますね。

2017-03-22

新しいマウス

マウスを新調した。

ここ数年使っていたのはソニーストアのポイントで購入した(もらった?) Bluetooth光学式無線マウス(ELECOM M-XG1BB)なのだけど、 今使っているテーブルの上ではものすごく反応が悪い。

カッターマットを敷いてマウスパッドの代わりにしていたのだけど、 鍋の熱でカッターマットが変形してしまい、そしてそれを二回もやってしまいマットを撤去。 結局反応の悪い状態でしばらく使っていた。

それとマウスの形状にも少し不満があった。親指をかけるところが深く凹んでいて、それが私の親指の太さに合わないのだ。 さらにその凹んでいるところに上に前後ボタンが配置されていて、まともに指をかけると誤ってそのボタンを押してしまう。 なんでこんな所にボタンをつけたんだろう。

新しいマウスはロジクールのMX1500 MX Anywhere2にした。 少々値は張るが追跡性能は良いらしいとのこと。 実際今のテーブルの上でも全く問題なく使うことが出来た。

形状は親指のところが少し凹んでいるので心配したが、ELECOMのそれよりはずいぶんマシだ。 前後ボタンも多少指にかかるが押し込みに力がいるので誤って押すことは無い。 強いて言えばもう少し高さがある方が持ちやすいだろうか。 使う人の手の大きさにも寄るし、モバイル用マウスという点を考えると仕方ないのかもしれない。

一つ不満を挙げると重量だろうか。マウスは軽いほど良いと思うのだけど、このマウスは小ぶりな割にはかなり重い(100g超)。 カーソル移動で持ち上げるときに少し力がいる。 慣れれば気にならなくなるだろうか。

そうそう、ホイールが少し面白い。マウスのホイールと言えば回すときのクリッ ク感の有無(カリカリ引っかかりながら回るか、スムーズにサーッと回るか)が マウスによって異なるが、これはホイールを押し込むたびにその二つが切り 替わる(機械的なメカニズムによって)。作業の内容に応じて切り替えが可能だ。 そのため中ボタンはホイールの下に別に付いており、これも慣れが必要だ。 私は中ボタンをよく使うのでしばらく戸惑うと思う。

2017-01-22

2017冬の新番組

今期も一通り一話は見終わりました。

01/02(月) 21:55 TOKYO MX × 超・少年探偵団NEO
01/03(火) 12:00 ニコニコCh あいまいみー 第3期
01/04(水) 23:30 TOKYO MX AKIBA’S TRIP -THE ANIMATION-
01/05(木) 22:30 TOKYO MX ○+ 政宗くんのリベンジ
01/05(木) 25:58 TBS うらら迷路帖
01/05(木) 26:28 TBS セイレン
01/06(金) 24:30 TOKYO MX △+ スクールガールストライカーズ -Animation Channel-
01/06(金) 25:05 TOKYO MX △+ 幼女戦記
01/06(金) 25:55 TBS 青の祓魔師 京都不浄王編
01/06(金) 26:40 TBS 昭和元禄落語心中~助六再び篇~(第2期)
01/07(土) ニコニコCh ナンバカ 第2期
01/07(土) 21:00 TOKYO MX スピリットパクト
01/07(土) 22:00 TOKYO MX △- 風夏
01/07(土) 23:30 TOKYO MX △+ 亜人(デミ)ちゃんは語りたい
01/08(日) 22:00 TOKYO MX エルドライブ
01/08(日) 22:27 TOKYO MX にゃんこデイズ
01/08(日) 22:30 TOKYO MX △- チェインクロニクル ~ヘクセイタスの閃(ひかり)~
01/08(日) 23:00 TOKYO MX テイルズ オブ ゼスティリア ザ クロス 第2期
01/08(日) 23:30 TOKYO MX △- アイドル事変
01/08(日) 24:00 TOKYO MX リトルウィッチアカデミア TVシリーズ
01/08(日) 25:35 テレビ東京 銀魂 新シリーズ
01/09(月) 24:00 TOKYO MX ガヴリールドロップアウト
01/09(月) 24:30 TOKYO MX 霊剣山 叡智への資格 (第2期)
01/09(月) 25:00 TOKYO MX × ちるらん にぶんの壱
01/09(月) 25:00 TOKYO MX ワカコ酒
01/09(月) 25:35 テレビ東京 弱虫ペダル NEW GENERATION (第3期)
01/09(月) 26:05 テレビ東京 鬼平 -ONIHEI-
01/10(火) 23:00 TOKYO MX ACCA13区監察課
01/10(火) 24:30 TOKYO MX × ハンドシェイカー
01/10(火) 25:35 テレビ東京 ○+ けものフレンズ
01/11(水) 22:30 TOKYO MX ピアシェ~私のイタリアン~
01/11(水) 22:40 TOKYO MX One Room
01/11(水) 24:00 TOKYO MX 小林さんちのメイドラゴン
01/11(水) 25:05 TOKYO MX この素晴らしい世界に祝福を! 2
01/11(水) 25:35 TOKYO MX × CHAOS;CHILD
01/12(木) 23:30 TOKYO MX × MARGINAL#4 KISSから創造るBig Bang
01/12(木) 24:55 フジテレビ × クズの本懐
01/13(金) 25:40 TOKYO MX SUPER LOVERS 第2期
01/14(土) 23:30 TOKYO MX Rewrite 2ndシーズン
01/15(日) 26:05 テレビ東京 闇芝居 第4期
01/20(金) 12:00 ニコニコCh 南鎌倉高校女子自転車部
01/21(土) 22:30 TOKYO MX BanG Dream!

一番気になるのが「けものフレンズ」というのがなんとも今期は不作なのか何なのか。

けものフレンズはほのめかしが凄い。 それが生きてくるかは最後まで見なければ分かりませんが、1話2話を見たところではなかなか興味をくすぐられますね。

  • 声が棒 → いや、あれは動物が元気で頭悪いということを表現する演技でしょう
  • 5分で十分 → 5分じゃ出会う場面だけで終わってしまうけど……
  • 絵が汚い → アニメは絵の綺麗さは必ずしも重要じゃ無くて、動く絵によって話が伝われば十分なのです
  • 子供向け → 明らかに可愛いものを愛でたい大きなお友達向けでしょう

動物の生態を再現しようと努力している点は好感が持てますし教育的でもあります。とはいえそんなに重視はしていないのでしょうね。教育を重視するなら動物園スタッフによる解説コーナーで実写を映しても良さそうなものです。

2016-10-27 ,

org2blogでpublish時に固まる(Emacs25.1, xml-rpc 20160430.1458)

Emacs25.1にしたせい(?)か org2blog でpublish時に "error in process sentinel: url-http-create-request: Multibyte text in HTTP request: POST /????/xmlrpc.php HTTP/1.1 …" とか言われて困ったのだけど xml-rpc-request の encode-coding-region の部分がダメっぽい。 xml-rpc-allow-unicode-string が t の時は encode-coding-string を使うようにしたらうまく行った。

xml-rpc.el:

                                         " encoding=\"UTF-8\"?>\n"
                                         (with-temp-buffer
                                           (xml-print xml)
-                                          (when xml-rpc-allow-unicode-string
-                                            (encode-coding-region
-                                             (point-min) (point-max) 'utf-8))
-                                          (buffer-string))
+                                          (if xml-rpc-allow-unicode-string
+                                              (encode-coding-string (buffer-string) 'utf-8)
+                                            (buffer-string)))
                                         "\n"))
               (url-mime-charset-string "utf-8;q=1, iso-8859-1;q=0.5")
               (url-request-coding-system xml-rpc-use-coding-system)

Emacs25からurl-http-系でリクエストを送るときはユニバイト文字列でないとエラーになるようになった(https://github.com/emacs-mirror/emacs/blob/master/etc/NEWS.25#L1306)。 RC1のときはユニバイト文字列を指定してもエラーになるバグがあったようなのだけど、今回のはそれではないようだ。

原因は以下の挙動の違いらしい。

(multibyte-string-p
 (with-temp-buffer
  (insert "こんにちは")
  (encode-coding-region (point-min) (point-max) 'utf-8)
  (buffer-string))) ;; => t
(multibyte-string-p (encode-coding-string "こんにちは" 'utf-8)) ;; => nil

encode-coding-regionの説明には「符号化結果は『生のバイト』であるが、 マルチバイトであったバッファはマルチバイトのままである。」とあるので、それが関係しているのかもしれない。