2008/11/10


最近のインターネットは便利になったもので、なんと美輪明宏のチンコがあるのかないのかを返してくれるWebAPIまである。インターネットリソースの大きさは今後もきっと拡大して行き、いずれはきっとGoogle Alertや、はてなアンテナ、はたまたEngadget Japanese等でのリーク流出等で実際の「あり/なし」が判明、本当に正しい結果が返って来る事になると思われる。

だがしかし、上記JSONサーバにはRSS/Feedが存在せず、実は「美輪明宏にはチンコがない」とユーザが初めて気付く為には、結果をメール等で通知する様なデーモンプロセスを稼働させてこのサービスを監視(ポーリング)しJSONをパースした結果としてユーザに通知するか、ユーザにとって使用頻度の高い乗用アプリケーションへチェック機能を付けユーザの操作(トリガ)でチェックを行う方法が選択肢となってしまう。たしかにポーリングは簡単に、かつ昼夜を問わず最新の情報を人手無しに監視し続ける事が出来る。
しかしながら、このサーバに負荷を掛けることは今後のインターネット発達が望まれる中、希少な有志の力を自ら塞ぎ込む形と成り得ない。
ここはトリガ形式を考えるのが適切である。

では、トリガ形式としてどんな常用アプリケーションが適切か。私が考えた結果がテキストエディタである。テキストエディタはユーザであればほぼ万人が使っている。今回私は、このテキストエディタの中でもギーク性が高いと言われるvimを選んだ。

まずは、何をもって「美輪明宏のチンコの有無」を確認するか。
テキストエディタであれば、入力補完がユーザのニーズに答えられる物になるだろう。
たとえば
美輪明宏にチンコは
まで入力し、<tab>を押下して「ある」もしくは「ない」が補完されたとすれば、それはかなり使い勝手のある物になると思われる。

運が良いことに、vimでは入力補完にフックメソッドを組み込む事が出来るため、今回私は、以下の様なソースを書いた。
func! s:CheckTimpo()
  " ここにデータ取得処理を入れる
endfunc
iabbr <silent> 美輪明宏にチンコは 美輪明宏にチンコは<C-R>=<SID>CheckTimpo()<CR>
入力I/Fに対するメソッド呼出部が完成した。
あとはJSONをどう解釈するか。ここで私にあるひらめきが。
たしかvim7のDictionaryは、JSON展開形式に似てたはず
そして以下のようなコードを書いた。 func! s:CheckTimpo()
  let ret = system("curl -s http://dzfl.jp/mojo/")
  let true = 'ある'
  let false = 'ない'
  exec "let json = " . ret
  return json['miwa']
endfunc
iabbr <silent> 美輪明宏にチンコは 美輪明宏にチンコは<C-R>=<SID>CheckTimpo()<CR>
以下にこのソースの解説を行う。
JSONサーバからは以下の形式でデータが戻される。
{ "miwa": true }
vim7ではDictionaryが導入されており、そのシンタックスは { key: value }
となる。つまりtrue/falseといった予約語を定義してやればJSON形式が扱える事になるのだ。
上記ソースではtrue/falseの定義を行った上でDictionaryへ値展開し、代入している。これによりvim7ではjsonという変数に対して json["miwa"]
とアクセス出来るのである。true/falseはそれぞれ「ある」/「ない」と定義している為、もし美輪明宏にチンコがあれば
json["miwa"] == "ある"
また、美輪明宏にチンコがなければ
json["miwa"] == "ない"
となるのだ。
この値をスクリプト内メソッドCheckTimpoの戻り値として返し、入力補完を行えるようになる。
以上の処理手順により、vim7を使っているならば何時でも
美輪明宏にチンコは
とまで入力し、補完トリガと成り得るようなキー(例えばスペース)を入力すれば
美輪明宏にチンコはある
と入力補完されるのである。

今回は、vimとJSONでマッシュアップを行ったが、これはあくまで一例に過ぎない。これを応用すれば「某国会議員がカツラなのか」、「モー娘のあの娘はタバコを吸っているかどうか」が、テキストエディタから補完出来るのである。

皆さんも、色んなマッシュアップを試して見てほしいと切に感じている。

以下、今回作成したソース「miwa.vim」を公開しておく。

ダウンロード:
注意:vimの入力補完は、全構文を途切れなく入力する必要がある。
Posted at by



2008/11/06


UN*X版だとssh-agentの実行結果が SSH_AUTH_SOCK=/tmp/ssh-suhGif2116/agent.2116; export SSH_AUTH_SOCK;
SSH_AGENT_PID=5672; export SSH_AGENT_PID;
echo Agent pid 5672;
みたいになっててログイン時に eval `ssh-agent`
とするんですがWindowsだと、そう簡単には行かない。 @echo off
if not "%SSH_AGENT_PID%" == "" goto end
for /f "eol=; tokens=1,2 delims==;" %%1 in ('ssh-agent.exe') do (
 if "%%1" == "SSH_AUTH_SOCK" set SSH_AUTH_SOCK=%%2
 if "%%1" == "SSH_AGENT_PID" set SSH_AGENT_PID=%%2
)
ssh-add
:end
開発を始める前にコマンドプロンプトで1回これを動かすとパスフレーズを聞かれ、1回入力すれば以降は聞かれない様になる(sshはmsysGitに含まれる物を使ってる)。 さらにWindowsサポートツールに入ってる"setx.exe"を使えば、ユーザ環境変数への反映も出来る為、新しく起動したコマンドプロンプトでsshコマンドがSSH_AGENT_PID/SSH_AUTH_SOCKを見てくれるようになってもっとウマーとなるはず(↑のバッチでsetしている所でさらにsetxもする必要があるかな)。

とりあえずこれで楽になった。ウマー
Posted at by



2008/10/29


追記 最新版はgithubで作ってます。
mattn's nicodown at master — GitHub
http://github.com/mattn/nicodown/tree/master


適当だけど書いてみた。
タイトル取って来る所はlibxml使うの面倒臭かったのでXMLパーサ使わずベタで(実態参照文字あると変になるので気を付けて)。Windowsの場合だけWin32 APIでシフトJISにファイル名を変換しています。
荒いコードなので色々直し所がありますが、サンプルって事で。
//#define CURL_STATICLIB
#include <curl/curl.h>

#define HEX_DIGITS "0123456789ABCDEF"
#define IS_QUOTED(x) (*x == '%' && strchr(HEX_DIGITS, *(x+1)) && strchr(HEX_DIGITS, *(x+2)))

static char* response_data = NULL;  /* response data from server. */
static size_t response_size = 0;    /* response size of data */

static void
curl_handle_init() {
    response_data = NULL;
    response_size = 0;
}

static void
curl_handle_term() {
    if (response_data) free(response_data);
}

static size_t
curl_handle_returned_data(char* ptr, size_t size, size_t nmemb, void* stream) {
    if (!response_data)
        response_data = (char*)malloc(size*nmemb);
    else
        response_data = (char*)realloc(response_data, response_size+size*nmemb);
    if (response_data) {
        memcpy(response_data+response_size, ptr, size*nmemb);
        response_size += size*nmemb;
    }
    return size*nmemb;
}

int
main(int argc, char* argv[]) {
    CURLcode res;
    CURL* curl;
    char error[256];
    char name[256];
    char data[1024];
    char* buf = NULL;
    char* ptr = NULL;
    char* tmp = NULL;
    FILE* fp = NULL;
    int status = 0;

    // usage
    if (argc != 4) {
        fputs("usage: nicodown [usermail] [password] [video_id]", stderr);
        goto leave;
    }

    // default filename
    sprintf(name, "%s.flv", argv[3]);

    curl = curl_easy_init();
    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
    curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, &error);
    curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_handle_returned_data);
    curl_easy_setopt(curl, CURLOPT_COOKIEJAR, "cookies.jar");
    curl_easy_setopt(curl, CURLOPT_COOKIEFILE, "cookies.txt");

    // login
    sprintf(data, "mail=%s&password=%s&next_url=/watch/%s", argv[1], argv[2], argv[3]);
    curl_handle_init();
    curl_easy_setopt(curl, CURLOPT_URL, "https://secure.nicovideo.jp/secure/login?site=niconico");
    curl_easy_setopt(curl, CURLOPT_POST, 1);
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data);
    res = curl_easy_perform(curl);
    if (res != CURLE_OK) {
        fprintf(stderr, error);
        goto leave;
    }
    buf = malloc(response_size + 1);
    strcpy(buf, response_data);
    if (strstr(buf, "id=\"login_bar\"")) {
        printf("%s\n", buf);
        free(buf);
        fprintf(stderr, "failed to login\n");
        goto leave;
    }
    free(buf);
    curl_handle_term();

    // get video url, and get filename
    sprintf(data, "http://www.nicovideo.jp/api/getthumbinfo?v=%s", argv[3]);
    curl_handle_init();
    curl_easy_setopt(curl, CURLOPT_URL, data);
    curl_easy_setopt(curl, CURLOPT_POST, 0);
    res = curl_easy_perform(curl);
    if (res != CURLE_OK) {
        fprintf(stderr, error);
        goto leave;
    }
    buf = malloc(response_size + 1);
    strcpy(buf, response_data);
    ptr = strstr(buf, "<title>");
    if (ptr) {
        ptr += 7;
        tmp = strstr(ptr, "</title>");
        if (*tmp) {
            *tmp = 0;
            strcpy(name, ptr);
        }
#ifdef _WIN32
        {
            UINT codePage;
            size_t wcssize;
            wchar_t* wcsstr;
            size_t mbssize;
            char* mbsstr;

            codePage = CP_UTF8;
            wcssize = MultiByteToWideChar(codePage, 0, name, -1,  NULL, 0);
            wcsstr = (wchar_t*)malloc(sizeof(wchar_t) * (wcssize + 1));
            wcssize = MultiByteToWideChar(codePage, 0, ptr, -1, wcsstr, wcssize + 1);
            wcsstr[wcssize] = 0;
            codePage = GetACP();
            mbssize = WideCharToMultiByte(codePage, 0, (LPCWSTR)wcsstr,-1,NULL,0,NULL,NULL);
            mbsstr = (char*)malloc(mbssize+1);
            mbssize = WideCharToMultiByte(codePage, 0, (LPCWSTR)wcsstr, -1, mbsstr, mbssize, NULL, NULL);
            mbsstr[mbssize] = 0;
            sprintf(name, "%s.flv", mbsstr);
            free(mbsstr);
            free(wcsstr);
        }
#endif
    }
    free(buf);
    printf("downloading %s\n", name);
    curl_handle_term();

    // get video url
    sprintf(data, "http://www.nicovideo.jp/api/getflv?v=%s", argv[3]);
    curl_handle_init();
    curl_easy_setopt(curl, CURLOPT_URL, data);
    curl_easy_setopt(curl, CURLOPT_POST, 0);
    res = curl_easy_perform(curl);
    if (res != CURLE_OK) {
        fprintf(stderr, error);
        goto leave;
    }
    buf = malloc(response_size + 1);
    strcpy(buf, response_data);
    ptr = strstr(response_data, "url=");
    if (!ptr) {
        free(buf);
        fprintf(stderr, "failed to get video info\n");
        goto leave;
    }
    tmp = strstr(ptr, "&");
    if (tmp) *tmp = 0;
    tmp = ptr;
    while(*tmp) {
        if (IS_QUOTED(tmp)) {
            char num = 0;
            sscanf(tmp+1, "%02x", &num);
            *tmp = num;
            strcpy(tmp + 1, tmp + 3);
        }
        tmp++;
    }
    strcpy(data, ptr + 4);
    printf("URL: %s\n", data);
    free(buf);
    curl_handle_term();

    // download video
    fp = fopen(name, "wb");
    if (!fp) {
        fprintf(stderr, "failed to open file\n");
        goto leave;
    }
    curl_handle_init();
    curl_easy_setopt(curl, CURLOPT_URL, data);
    curl_easy_setopt(curl, CURLOPT_POST, 0);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
    res = curl_easy_perform(curl);
    fclose(fp);
    if (res != CURLE_OK) {
        fprintf(stderr, error);
        goto leave;
    }

leave:
    curl_handle_term();
    curl_easy_cleanup(curl);

    return 0;
}
確認はWindowsでしかしてませんが、きっとUN*Xでも動くはず。perlやpythonやruby使わない派の方は雛形として持ってって下さい。

Posted at by