字幕データの取り出しは、ぱぱネット(仮)さんのdumpeit最新版20110718を使わせてもらうことにしました。 TSの切り出しやEPGの取出しができたりと高機能なのでreq.plやepg.plを置き換えるのがスマートな気がしますが、既にts.plやepg.plを弄っちゃってるので、字幕データの取り出し機能だけ利用することにしました。
次のようなオプションでdumpeitを実行すると、拡張子が.subの字幕ファイルが生成されるので、その内容をDBに取り込むことにします。
dumpeit -f TSファイル字幕ファイルからDBへのデータ取り込みは、次のように処理しています。
my $sql = make_sql('/path/to/xxxx.sub'); if (defined $sql) { store_data($sql); } sub make_sql() { my ($file) = shift @_; if (!-e $file or -z $file) { return undef; } if ($file !~ /(\d{8})_Ch(\d+)\.sub$/) { return undef; } my $dt = $1; my $ch = $2; my $t = Time::Piece->strptime($dt, "%y%m%d%H"); my $date = $t->ymd; my @sql = (); open my $fh, "<", $file or do { return undef; }; my ($t1, $t2, $dv_id) = (undef, undef, undef); while (my $line = <$fh>) { chomp($line); my ($tmp, $msg) = split(/\s/, $line, 2); my ($tm, $pid_s) = split(/\./, $tmp); my $t = Time::Piece->strptime("$date $tm", "%F %T"); if (!defined $dv_id or !($t1 <= $t and $t <= $t2)) { my ($id, $stime, $etime) = find_event($ch, "$date $tm"); if (defined $id) { $t1 = Time::Piece->strptime($stime, "%F %T"); $t2 = Time::Piece->strptime($etime, "%F %T"); } $dv_id = $id; } push @sql, sprintf "($ch, '$date $tm', $pid_s, '$msg', %s)", $dv_id ? $dv_id : "NULL"; } close($fh); if ($#sql < 0) { return undef; } my $sql_header = 'INSERT INTO da_caption '; $sql_header .= '(dc_ch, dp_start_time, dp_pid, dp_text, dv_id) VALUES '; return $sql_header . join(',', @sql); }データをそのまま入れているだけですが、検索結果を表示するときに番組情報を調べやすくするために、次の処理で番組データとの関連を追加しています。
sub find_event { my ($ch, $date) = @_; my $sql = <<END; select e.dv_id, e.dv_start_time, addtime(e.dv_start_time, e.dv_duration) from da_event as e inner join da_channel as c on e.dv_service_id = c.dc_service_id where e.dv_start_time <= ? and ? < addtime(e.dv_start_time, e.dv_duration) and c.dc_ch = ? order by e.dv_start_time limit 1 END my $dbh = dbiConnect(); my ($sth, $rv) = (); eval { $sth = $dbh->prepare($sql); $rv = $sth->execute($date, $date, $ch); }; return undef if ($@); my @events = (); while (my $ref = $sth->fetch()) { my ($dv_id, $start_time, $end_time) = @$ref; push(@events, [$dv_id, $start_time, $end_time]); } dbiDisconnect($dbh); if ($#events < 0) { return undef; } return @{$events[0]}; }コミット処理は、次のようにしています。
字幕データ用のテーブルは、MyISAMなので意味が無い気がしますが、その他のテーブルはInnoDBで利用しているので処理を共通化しています。
sub store_data { my ($sql) = shift @_; my $dbh = dbiConnect(); my $retry=3; while ($retry--) { eval { my $ddh = $dbh->prepare($sql); my $rv = $ddh->execute(); $dbh->commit(); }; last if (!$@); my $rnd = int(rand(10)) +1; my $st = 100000 * $rnd; printf STDERR "sleep %.1fs, retry(%d)", $st/1000000, $retry; usleep($st); } if ($retry < 0) { print STDERR "store-caption was give up."; } dbiDisconnect($dbh); return !$@; }あまり考えないでコーディングを始めてしまうので、テーブル構成がイマイチとか後から思ったりしたのですが…遊びなので結局そのままです。
こんな感じでスクリプト一つ作り、一時間毎にTSデータから字幕データをDBに取り込んで、約24日分の番組情報のデータ量は以下のようになっています。
mysql> select count(*) from da_caption; +----------+ | count(*) | +----------+ | 1195794 | +----------+ 1 row in set (0.00 sec) mysql> select count(*) from da_event; +----------+ | count(*) | +----------+ | 8436 | +----------+ 1 row in set (0.00 sec) mysql>この状態で字幕検索を行っても、今のところ一秒以内に結果が返ってくるので、CGIからの読み出しでもストレス無く使えています。
0 件のコメント:
コメントを投稿