ニコニコ動画ダウンローダ

以前、Irvine(via OLE) でダウンロードする Perl スクリプトを書いたけど、なんか FLV のダウンロードにも Cookie が必要になっているっぽいので、FirefoxCookie を使ったり、ファイル名をタイトルから取得したり Progress Bar を出したりとかするのをこの間 Python でリライトした。progressbar.py は http://cheeseshop.python.org/pypi/progressbar から。
一応、ファイル名はタイトルから取得するけど、「/」が含まれていていたり、CP932 に含まれない文字を使っていたりすると失敗する*1
で、Cookie は -c オプションで Firefox のプロファイルディレクトリ以下の cookie.txt を指定しませう。というか、ニコニコ動画はログインすると以前ログインした Cookie が無効になるので、こうしないとやってられない。

#!/usr/bin/env python

import sys, urllib2, cookielib, re, cgi

DEFAULT_COOKIE_FILE = 'cookies.txt'
FILESYSTEM_ENCODING = 'cp932'

def get_nicovideo_id(url):
    pat = re.compile('http://www.nicovideo.jp/watch/(.*)')
    m = pat.match(url)
    if m:
        return m.group(1)
    else:
        return None

def get_title(url, opener):
    content = get(url, opener).decode('utf-8')
    pat = re.compile(r'<h1><a [^>]*>(.*)</a></h1>', re.M)
    m = pat.search(content)
    if m:
        return m.group(1)
    else:
        raise KeyError('Not Found')

def get_flv_url(nid, opener):
    get_flv_url = 'http://www.nicovideo.jp/api/getflv?v=%s' % urllib2.quote(nid)
    return cgi.parse_qs(get(get_flv_url, opener))['url'][0]

def get(url, opener):
    fp = opener.open(url)
    try:
        content = fp.read()
        return content
    finally:
        fp.close()

def retrieve(url, filename, opener):
    from progressbar import ProgressBar, Percentage, Bar, ETA, FileTransferSpeed
    
    reader = opener.open(url)
    writer = file(filename, 'w')
    total = int(reader.headers.get('content-length'))

    print filename
    widgets = [
        Percentage(), ' ',
        Bar(marker = '#', left = '[', right = ']'), ' ',
        ETA(), ' ',
        FileTransferSpeed()]
    pbar = ProgressBar(widgets = widgets, maxval = total)

    l = 0
    while True:
    block = reader.read(512)
    if not block: break
    writer.write(block)
    l += len(block)
        pbar.update(l)
    pbar.finish()
    reader.close()
    writer.close()

def main(cookie_file, args):
    cj = cookielib.MozillaCookieJar()
    cj.load(cookie_file)
    opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))

    for url in args:
        nid = get_nicovideo_id(url)
        flv_url = get_flv_url(nid, opener)
        title = get_title(url, opener)
        filename = title.encode(FILESYSTEM_ENCODING) + '.flv'
        retrieve(flv_url, filename, opener)

if __name__ == '__main__':
    import optparse

    parser = optparse.OptionParser()
    parser.add_option('-c', '--cookie', dest = 'cookie')
    (opts, args) = parser.parse_args()

    if opts.cookie is not None:
        cookie_file = opts.cookie
    else:
        cookie_file = DEFAULT_COOKIE_FILE

    main(cookie_file, args)

*1:以前、どこかで議論していた気がするけど、UnicodeAPI を使ったファイルオープンてできるんだっけ?