Doge log

Abby CTO 雑賀 力王のオフィシャルサイトです

Twistedで作るローカルYoutubeサーバ その1

Youtubeネタ発展版。
せっかくなのでサーバも書いてしまおう。
Twistedはマルチサーバーを簡単に書く事ができる。
今回は

  1. Webサーバ(Webアプリケーション)
  2. Youtubeダウンローダー(定期的実行サービス)
  3. sshサーバ

を書いてみる。
なんでsshかは秘密。
キモいサーバを書くからだよ。
DBを使わないだっさいサーバだけどね。
今回はtwisted.webではなくnevowを使用。
更にAxiomを使った発展系はあとでかくかも。

まずはvideoのダウンロード部分を作る。

downloader.py
from twisted.web import client
from twisted.web import error
from twisted.internet import reactor

import os.path
import re

import web

const_video_url_params_re = re.compile(r'player2\.swf\?([^"]+)"', re.M)
const_video_url_real_str = 'http://www.youtube.com/get_video?%s'
const_video_url_re = re.compile(r'^((?:http://)?(?:\w+\.)?youtube\.com/(?:v/|(?:watch(?:\.php)?)?\?(?:.+&)?v=))?([0-9A-Za-z_-]+)(?(1)[&/].*)?$')
const_video_title_re = re.compile(r'<title>YouTube - ([^<]*)</title>', re.M | re.I)

def _finish(data, id, title, file):
    print 'finish %s %s %s' % (title, id, file)
    v = web.VideoItem()
    v.title = title
    v.id = id
    v.file = file
    web.items.append(v)

def _err(error):
    print error

def downloadFlv(url, file):
        
    def _parseFlvUrl(data):
        match = const_video_url_params_re.search(data)
        param = match.group(1)
        url =  const_video_url_real_str %  param
        match_title = const_video_title_re.search(data)
        title = match_title.group(1)
        return (url, title)
    
    def _download(file, id):
        if os.path.isdir(file):
            file = os.path.join(file, '%s.flv' % id)
        def _downloadPage(data):
            url, title = data
            client.downloadPage(url, file).addCallback(_finish, id, title, file).addErrback(_err)
        return _downloadPage

    match = const_video_url_re.search(url)
    id = match.group(2)

    return client.getPage(url).addCallback(_parseFlvUrl).addCallback(_download(file, id)).addErrback(_err)

def downloadAll():
    print 'start'
    import setting
    while setting.video:
        url = setting.video.pop()
        downloadFlv(url, setting.repo)

downloader部分は以前のままだけどtitleとidも取るようする。
finishで値を保持しているオブジェクトはwebで参照するためのオブジェクト。
今回はあまり深く触れない。
setitngのvideoという名前のリストにダウンロードするvideoのURLを入れておくと実行時にdownloadAllに読み込まれる。
あとはdownloadAllをTimerServiceから呼び出すし、定期的にキューを監視する。
またファイルの保存先もsetting.pyの'repo'を参照しているので設定する必要がある。

repo = '/tmp'
#video =['http://jp.youtube.com/watch?v=4-uxh1iQX0E']
video = []

TimerServiceへの登録は簡単こんな感じ。

from twisted.application.internet import TimerService
from twisted.application import service, strports
from nevow import static
from nevow import appserver

import setting
import downloader
import web
import shell

application = service.Application('youtube')

timer = TimerService(60, downloader.downloadAll)
timer.setServiceParent(application)

TimerServiceのコンストラクタに実行間隔(秒数)と関数を渡すだけ。
そしてサーバの親をsetParentでセットすると起動時に勝手にstartされる。
次はweb周りも絡めていく。
うくく。