Created
June 26, 2010 18:38
-
-
Save shimarin/454252 to your computer and use it in GitHub Desktop.
共有フォルダに動画ファイルを放り込んでおくと勝手にiPhone/iPad用にトランスコードしといてくれるシステムを作った。動画ファイルのハッシュ値を記録しておいて、過去に処理済みの動画は後回しにして暇なときだけ処理する機能つき。きわめて自分専用。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/ruby | |
# -*- coding: utf-8 -*- | |
require "fileutils" | |
require "digest/md5" | |
require "dbi" | |
# HandBrakeを使って infileを outfileにトランスコードする。 | |
# optionsはコマンドラインオプション | |
def encode(infile, outfile, options) | |
cmdline = "HandBrakeCLI -i \"#{infile}\" -o \"#{outfile}\" #{options}" | |
system(cmdline) | |
$? | |
end | |
# ファイルのmd5sumを得る | |
def get_md5(filename) | |
md5 = Digest::MD5.new | |
File.open(filename) {|f| | |
while s = f.read(1024) | |
md5.update(s) | |
end | |
} | |
md5 | |
end | |
# データベースアクセス | |
def with_dbi | |
# DBサーバlocalhost(デフォルト), DB名 handbrake、ユーザ名 handbrake、パスワードなし | |
DBI.connect("DBI:Mysql:handbrake", "handbrake", "") {|dbh| | |
dbh['AutoCommit'] = false | |
dbh.transaction {|dbh| | |
yield dbh | |
} | |
} | |
end | |
# 過去に同じmd5sumを持つファイルを処理したことがあるか | |
def md5sum_exists?(md5, device) | |
with_dbi {|dbh| | |
num = dbh.select_one("select count(*) from completed_processes where md5sum=? and device_name=?", md5.hexdigest, device)[0] | |
return true if num > 0 | |
} | |
return false | |
end | |
# ソースディレクトリ(の中にある対応するターゲットデバイス名のディレクトリ)を走査し、 | |
# それぞれトランスコードにかける | |
def process_dir(source_dir, target_dir, device, config, skip_cached = true, max_files = nil) | |
# ディレクトリがない場合はただ戻る | |
return 0 if !FileTest.exist?("#{source_dir}/#{device}") | |
count = 0 | |
Dir.open("#{source_dir}/#{device}") {|dir| | |
dir.each {|file| | |
source = dir.path + "/" + file | |
next if File.ftype(source) != "file" | |
next if file[0,1] == "." | |
stat = File.stat(source) | |
# ファイルサイズゼロはスキップ | |
next if stat.size == 0 | |
# 最終更新チェック。最後の書き込みから60秒が経過していない場合はまだ使用中とみなして | |
# 処理をスキップする | |
next if Time.new - 60 < stat.mtime | |
# ハッシュ値を計算し、過去に同じ物を同じデバイス向けに処理したことがあるかチェック | |
# skip_cachedフラグがtrueでかつ過去に同じ物を処理したことがあるならスキップする | |
md5 = get_md5(source) | |
next if md5sum_exists?(md5,device) && skip_cached | |
# ターゲットディレクトリに対象デバイス名のサブディレクトリが無ければ作る | |
if !FileTest.exist?("#{target_dir}/#{device}") then | |
FileUtils.mkdir_p("#{target_dir}/#{device}") | |
end | |
# ターゲットファイル名の決定。処理中のファイルはサフィックスを ".bin" にすることで | |
# 処理中のファイルをプレイヤーで開いてしまうことを防ぐ | |
destination_base = target_dir + "/" + device + "/" + File.basename(file, ".*") | |
temp_destination = destination_base + ".bin" | |
destination = destination_base + "." + config[:suffix] | |
# トランスコード処理にかける | |
if encode(source, temp_destination, config[:options]) == 0 && FileTest.exist?(temp_destination) then | |
# トランスコードが成功したようであれば、ファイル名(サフィックス)をあるべき | |
# ものに変更する | |
FileUtils.mv(temp_destination, destination) | |
# トランスコード元ファイルのmd5sumをデータベースに記録する。新規ならINSERT | |
# 既存なら UPDATEで最終更新日時の更新を行う | |
with_dbi {|dbh| | |
row = dbh.select_one("select * from completed_processes where md5sum=? and device_name=? for update", md5.hexdigest, device) | |
if row != nil then | |
dbh.do("update completed_processes set updated_at=now() where md5sum=? and device_name=?", md5.hexdigest, device) | |
else | |
dbh.do("insert into completed_processes(md5sum,device_name,created_at) values(?,?,now())", md5.hexdigest, device) | |
end | |
} | |
# 処理済みのトランスコード元ファイルを done サブディレクトリに移動する | |
FileUtils.mkdir_p("#{source_dir}/#{device}/done") | |
FileUtils.mv(source, "#{source_dir}/#{device}/done") | |
count += 1 | |
return count if max_files != nil && count >= max_files | |
else | |
# トランスコードが失敗した場合は、元ファイルを failed サブディレクトリに移動する | |
FileUtils.mkdir_p("#{source_dir}/#{device}/failed") | |
FileUtils.mv(source, "#{source_dir}/#{device}/failed") | |
end | |
} | |
} | |
count | |
end | |
def main() | |
# ユーザーがトランスコード元の動画ファイルを設置するディレクトリ | |
# sambaでここを共有する | |
source_dir = "/var/handbrake" | |
# トランスコード済みの動画ファイルを出力するディレクトリ | |
# ApacheのDocumentRootをここにする | |
target_dir = "/var/www/localhost/htdocs" | |
# 多重起動防止のためのロックファイルを開く | |
File.open(File.expand_path("~") + "/var/run/lock", "w") {|lock| | |
# ロックが既に他のプロセスに獲得されている場合はおとなしく処理を中断する | |
return if lock.flock(File::LOCK_EX | File::LOCK_NB) == false | |
# 対象デバイスのリストをデータベースから取得する | |
devices = {} | |
with_dbi {|dbh| | |
dbh.select_all("select device_name,program,switches,suffix from devices").each {|row| | |
device_name = row["device_name"] | |
devices[device_name] = {:program=>row["program"],:options=>row["switches"],:suffix=>row["suffix"]} | |
} | |
} | |
# 各デバイス用のサブディレクトリについて一括トランスコード処理を呼び出す | |
devices.each {|device,config| | |
if process_dir(source_dir, target_dir, device, config, true) == 0 then | |
# 処理するファイルが無くて暇なときだけ、過去にトランスコード済みのファイルも1個だけ処理する | |
process_dir(source_dir, target_dir, device, config, false, 1) | |
end | |
} | |
} | |
end | |
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
create table sources ( | |
md5sum varchar(32) primary key, | |
filesize int not null | |
); | |
create table source_filenames ( | |
md5sum varchar(32) not null, | |
filename varchar not null, | |
primary key(md5sum, filename) | |
); | |
create table completed_processes ( | |
md5sum varchar(32) not null, | |
device_name varchar(32) not null, | |
created_at datetime not null, | |
updated_at datetime null, | |
primary key(md5sum,device_name) | |
); | |
create table devices ( | |
device_name varchar(32) primary key, | |
program varchar(32) null, | |
switches varchar(255) not null, | |
suffix varchar(16) not null | |
); | |
insert into devices(device_name,switches,suffix) values('iPad','-e x264 -q 0.6 -a 1,1 -E faac,ac3 -B 160,auto -R 48,Auto -6 dpl2,auto -f mp4 -4 -X 960 -m --loose-anamorphic -x cabac=0:ref=2:me=umh:b-adapt=2:weightb=0:trellis=0:weightp=0','m4v'); | |
insert into devices(device_name,switches,suffix) values('iPhone','-e x264 -q 0.6 -a 1 -E faac -B 128 -6 dpl2 -R 48 -D 0.0 -f mp4 -X 480 -m -x cabac=0:ref=2:me=umh:bframes=0:subme=6:8x8dct=0:trellis=0','m4v'); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment