wgetでYouTube等から動画を落とす

Rubyに戻ってみました。selfを書かなくて済むのはいいんですが、Pythonに慣れてきたせいか、endを書くのが少し面倒になってます。後、インスタンスごとにメソッドの振る舞いを変えられるのはいいです。新しくクラスを作らなくて済みますし。

#!ruby

require 'net/http'
require 'cgi'
require 'kconv'

save_dir = 'c:/My Documents'
interval = 3

module Video
  class Error < StandardError; end
  class InvalidUrlError < Error; end

  VideoDetail = Struct.new(:id, :dl_url, :title, :ext, :encoding)

  def get_detail(url)
    @@videos.each do |video|
      begin
        return video.get_detail(url)
      rescue InvalidUrlError
        raise if video === @@videos.last
      end
    end
  end
  module_function :get_detail

  class Video

    def initialize(url_re, dl_url_re, title_re=nil, api_url=nil,
                   ext=".flv", encoding=Kconv::UTF8)
      @url_re = url_re
      @dl_url_re = dl_url_re
      @title_re = title_re
      @api_url = api_url
      @ext = ext
      @encoding = encoding
    end

    def get_detail(url)
      id = extract_video_id(url)
      if @api_url
        video_url = @api_url % id
      else
        video_url = url
      end
      content = get_content(video_url)
      params = extract_dl_url_params(content)
      dl_url = build_dl_url(params)
      title = extract_title(content)
      VideoDetail.new(id, dl_url, title, @ext, @encoding)
    end

    private

    def extract_video_id(url)
      @url_re.match(url).to_a[1] or
        raise InvalidUrlError.new('invalid video url')
    end

    def get_content(url)
      begin
        res = Net::HTTP.get_response(URI.parse(url))
        raise unless Net::HTTPSuccess === res      
        res.body
      rescue
        raise Error.new('unable to download video page')
      end
    end

    def extract_dl_url_params(content)
      @dl_url_re.match(content).to_a[1..-1] or
        raise Error.new('unable to extract download url params')
    end

    def build_dl_url(params)
      params.first
    end

    def extract_title(content)
      @title_re.match(content).to_a[1] if @title_re
    end

  end

  @@videos = []

  youtube = Video.new(
    %r!\Ahttp://(?:\w+\.)youtube\.com/watch\?v=([\w-]+)!,
    %r!watch_fullscreen\?.*?video_id=([\w-]+).*?&t=([\w-]+)!,
    %r!<title>YouTube - ([^<>]*)</title>!
  )
  def youtube.build_dl_url(params)
    "http://www.youtube.com/get_video?video_id=%s&t=%s" % params
  end
  @@videos << youtube

  veoh = Video.new(
    %r!\Ahttp://www\.veoh\.com/videos/(\w+)!,
    %r!fullPreviewHashPath="([^"]+)"!,
    %r!title="([^"]*)"\s+dateAdded=!,
    'http://www.veoh.com/rest/video/%s/details'
  )
  @@videos << veoh

  dailymotion = Video.new(
    %r!http://www.dailymotion\.com/.*?/video/([\w/-]+)!,
    %r!(http%3A%2F%2Fwww\.dailymotion\.com%2Fget%2F\d{2}%2F320x240%2Fflv%2F\d+\.flv%3Fkey%3D\w+)!,
    %r!<h1 class="nav with_uptitle">([^<>]*)</h1>!
  )
  def dailymotion.build_dl_url(params)
    CGI.unescape(params.first)
  end
  @@videos << dailymotion

  amebavision = Video.new(
    %r!http://vision\.ameba\.jp/watch\.do.*?\?movie=(\d+)!,
    %r!<imageUrlLarge>([^<>]+)</imageUrlLarge>!,
    %r!<item>\s*<title>([^<>]*)</title>!,
    "http://vision.ameba.jp/api/get/detailMovie.do?movie=%s"
  )
  def amebavision.build_dl_url(params)
    flv_url = params.first
    flv_url['//vi'] = '//vm'
    flv_url['/jpg/'] = '/flv/'
    flv_url['_4.jpg'] = '.flv'
    flv_url
  end
  @@videos << amebavision

  yourfilehost = Video.new(
    %r!http://(?:www\.)?yourfilehost\.com/media\.php\?cat=video&file=([\w.-]+)\.!,
    %r!videoembed_id=([\w%.-]+)&!
  )
  def yourfilehost.build_dl_url(params)
    CGI.unescape(params.first)
  end
  @@videos << yourfilehost
end

ARGV.each do |url|
  begin
    video = Video.get_detail(url)
  rescue  => e
    puts "Error: #{e} (#{url})"
    next
  end
  if video.title
    filename = video.title.kconv(Kconv::SJIS, video.encoding)
  else
    filename = video.id
  end
  filepath = File.join(save_dir, filename << video.ext)
  system('wget', '-O', filepath, "--referer=#{url}", video.dl_url)
  sleep interval
end