事象の水平線

個人的ブックマーク代わりなメモ書きブログ。 地球は丸いよ。↓このへん。

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

PageTop
ソースをちゃんと追っかけたわけでもなく、単にコードの途中でログを吐かせて実験しただけなのでかなり不確かですが、連続録画のバグを見つけた気がするのでメモをしておきます。
EPGrec公式の『過去のバージョンの設定』の中段あたりの箇所で連続録画は(Experimental)であると書いてあるので、仕方ないとは思いますが、これはおそらく単純な見落としではないかと・・・(まぁ、だからExperimentalなんですけどね・・・)

で、『kn_ishi』さんが『EpgRecの録画予約アルゴリズム"Reservation.class.php"を改善する。』というページで、改良パッチを公開してますが、こちらも同様の問題がある気がします。

で、どういうバグかというと、連続録画で直前の番組を見つけるところでREC_SWITCH_TIME(Reservation.class.php上では$settings->rec_switch_timeとして呼び出されてる)を考慮することを忘れたのではないかと思います。

おそらく、環境設定をしたユーザー目線だと、
連続した番組の予約 を 行う
録画コマンドの切り替え時間 に 適当な秒数たとえば5秒
にすれば、つながった連続の録画が可能になると思う と思います。

具体的には、(分かりやすく1chで考えると)
1:00~2:00 ・・・番組A
2:00~3:00 ・・・番組B
の番組を連続してとろうとした場合、ユーザー(おそらく開発者も)は番組Aのお尻を5秒(先の例)分削って連続録画してくれるんだろうと期待するわけですが、そうならない場合があります。

EPGrecは、連続録画をする設定だと、前番組(A)のケツを削る際の判定に、
前番組(A)が終わる時間+録画時間を長めにする の時間(実際のAの録画終了時)

後番組(B)が始まる時間+録画開始の余裕時間(秒) の時間(実際のBの録画開始時)
の間に隙間がない(重なっている)場合に前番組(A)を削るようにプログラムしてあると思います。

で、仮に、
録画開始の余裕時間(秒) と 録画時間を長めにする の双方を 0秒に設定していると、先の例の番組A,Bの場合は重ならないので、ケツを削る処理をせずに連続録画が失敗します。
もし、開始の余裕時間を60秒にした場合も、1分間あいだの開いた番組を録画する場合に同様の失敗をすると思われます。
秒単位のラテ欄は見たことがないので、双方もしくはどちらかを秒単位で設定している場合このバグに気づかない(問題は起きない)でしょう。


で、解決策
オリジナルのReservation.class.phpの場合
(2013/1/4現在のgit://git.sourceforge.jp/gitroot/epgrec/epgrec.git)
90行目からを
$battings = DBRecord::countRecords( RESERVE_TBL, "WHERE complete = '0' ".
"AND ".$type_str.
"AND starttime < '".toDatetime($end_time + $settings->rec_switch_time) ."' ".
"AND endtime > '".toDatetime($rec_start - $settings->rec_switch_time)."'"
);
にすれば多分大丈夫だと思います。
ただ、オリジナルの場合、上記の変更と関係なく(録画チャンネル2チャンネルで)、
1:00~2:00 ・・・番組A,C,D
2:00~3:00 ・・・番組B
がある場合、録画開始の余裕時間(秒) と 録画時間を長めにする が 0秒の状況下で、
A→B→Cと録画予約した後に、何故かDが録画予約できてしまうというバグが発生する(原因不明かつ調査してない)ようです。

で、
前述の『EpgRecの録画予約アルゴリズム"Reservation.class.php"を改善する。』の場合、
(2013/1/4現在のgit://git.sourceforge.jp/gitroot/epgrec/epgrec.git) 
87行目からを
			// 既存予約数 = TUNER番号
$tuners = ($crec->type == "GR") ? (int)($settings->gr_tuners) : (int)($settings->bs_tuners);
$type_str = ($crec->type == "GR") ? "type = 'GR' " : "(type = 'BS' OR type = 'CS') ";

// 影響する予約情報を集める
//$rec_start を $rec_start - $settings->rec_switch_time に した
//$end_time を $end_time + $settings->rec_switch_time に した

$trecs = DBRecord::createRecords(RESERVE_TBL, "WHERE complete = '0' ".
"AND ".$type_str.
"AND starttime < '".toDatetime($end_time + $settings->rec_switch_time)."' ".
"AND endtime > '".toDatetime($rec_start - $settings->rec_switch_time)."'" );
// 情報を配列に入れる
for( $i = 0; $i < count($trecs) ; $i++ ) {
$dim_start_time[$i] = toTimestamp($trecs[$i]->starttime);
$dim_end_time[$i] = toTimestamp($trecs[$i]->endtime);
}
// 新規予約の値も配列に追加
$dim_start_time[count($trecs)] = $rec_start;
$dim_end_time[count($trecs)] = $end_time;

// 配列を使って重複を調べ、重複解消を検証する
$battings = 0;
$mi = 0;
for( $i = 0; $i <= count($trecs) ; $i++ ) {
$mem_battings = 0;
for( $j = 0; $j <= count($trecs) ; $j++ ) {
//$dim_start_time[$j] を $dim_start_time[$j] - $settings->rec_switch_time にした
if( ( $i <> $j ) && ( $dim_start_time[$j] - $settings->rec_switch_time < $dim_end_time[$i] ) && ( $dim_end_time[$j] >= $dim_end_time[$i] ) ) {
$mem_battings++; // 重複をカウント
}
}
if( $mem_battings > $tuners ) { // 重複が多すぎるので予約不可
throw new Exception( " 重複予約があります" );
}
// チューナー数が足りないとき、連続予約="する"なら重複解消を試みる
if( ( $mem_battings >= $tuners ) && ( $settings->force_cont_rec == 1 ) ) {
for( $j = 0; $j <= count($trecs) ; $j++ ) {
// 連続予約があるか?
if( ( $i <> $j ) && ( $dim_end_time[$i] > $dim_start_time[$j] - $settings->rec_switch_time )
&& ( $dim_end_time[$i] <= $dim_start_time[$j] + $settings->extra_time + $settings->former_time ) ) {
// 録画が始まっていないか?
if( $dim_start_time[$i] > ( time() + PADDING_TIME + $settings->former_time + $settings->rec_switch_time ) + 1 ) {
$mem[$mi] = $i; // 変更すべき予約IDをメモ
$dim_end_time[$i] = $dim_start_time[$j] - $settings->rec_switch_time; // 先行予約の終了時刻を早める
}
else {
$mem[$mi] = $j; // 変更すべき予約IDをメモ
$dim_start_time[$j] = $dim_end_time[$i] + $settings->rec_switch_time; // 後続予約の開始時刻を遅くする
}
$mi++;
$mem_battings--;
break;
}
}
}
if( $mem_battings >= $tuners ) { // 重複解消できない
for( $j = 0; $j < count($trecs) ; $j++ ) {
if( ( $dim_start_time[$j] < $dim_end_time[$i] ) && ( $dim_end_time[$j] >= $dim_end_time[$i] ) ) {
$msg = $msg."\n 「".$trecs[$j]->title."」";
}
}
throw new Exception( " 予約が重複しています".$msg );
}
if( $battings < $mem_battings ) {
$battings = $mem_battings;
}
}

// ここまでくれば予約可能
for( $i = 0; $i < $mi ; $i++ ) { // 重複解消が必要なら実行する
if( $mem[$i] == count($trecs) ) { // 変更すべきは新規予約
$rec_start = $dim_start_time[$mem[$i]];
$end_time = $dim_end_time[$mem[$i]];
$duration = $end_time - $rec_start; // durationを計算しなおす
}
else { // 変更すべきは既存予約
// 予約修正に必要な情報を取り出す
$prev_id = $trecs[$mem[$i]]->id;
$prev_program_id = $trecs[$mem[$i]]->program_id;
$prev_channel_id = $trecs[$mem[$i]]->channel_id;
$prev_title = $trecs[$mem[$i]]->title;
$prev_description = $trecs[$mem[$i]]->description;
$prev_category_id = $trecs[$mem[$i]]->category_id;
$prev_starttime = $trecs[$mem[$i]]->starttime;
$prev_endtime = $trecs[$mem[$i]]->endtime;
$prev_autorec = $trecs[$mem[$i]]->autorec;
$prev_mode = $trecs[$mem[$i]]->mode;
$prev_dirty = $trecs[$mem[$i]]->dirty;
$prev_start_time = toTimestamp($prev_starttime);
// 開始時刻を再設定
$prev_starttime = toDatetime( $dim_start_time[$mem[$i]] + $settings->former_time );
// 終了時刻を再設定
$prev_endtime = toDatetime( $dim_end_time[$mem[$i]] );
// tryのネスト
try {
self::cancel( $prev_id ); // いったん予約取り消し
self::custom( // 再予約
$prev_starttime, // 開始時間Datetime型
$prev_endtime, // 終了時間Datetime型
$prev_channel_id, // チャンネルID
$prev_title, // タイトル
$prev_description, // 概要
$prev_category_id, // カテゴリID
$prev_program_id, // 番組ID
$prev_autorec, // 自動録画
$prev_mode,
$prev_dirty );
}
catch( Exception $e ) {
throw new Exception( " 予約時刻変更(再予約)に失敗しました\n 「".$prev_title."」" );
}
}
}

// チューナー番号
$tuner = $battings;
にすれば多分大丈夫だと思います。
変更したのは赤い部分のみ。
勝手にコード改変して乗っけちゃってますが、問題あれば連絡ください。

ちなみに、前述したとおり全然ちゃんとソースを読んでないので試す際は自己責任でお願いします。

また双方とも、前後の予約を取り消したときには、それによって削られていた他の番組のお尻部分は復活しないようです。(やるならcancelReservation.phpかな。ファイルを開いてすらないけど・・・・)

ちなみにうちの環境で、
録画コマンドの切り替え時間
2秒だとたまに失敗。
5秒だと今のところ失敗なし。です。
スポンサーサイト

PageTop

コメント

 ※
 ※
管理者にだけ表示を許可する
  ※ 必須項目です

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。