changeset 155:190a3c46611d

merged with upstream
author Yoshiki Yazawa <yaz@honeyplanet.jp>
date Tue, 30 Mar 2010 00:37:47 +0900
parents 43f74519bd52 (current diff) 64b60603d7df (diff)
children be39a7efcc87
files Keyword.class.php config.php.sample do-record.sh.pt1 getepg.php
diffstat 3 files changed, 263 insertions(+), 3 deletions(-) [+]
line wrap: on
line diff
--- a/do-record.sh.pt1	Sun Mar 28 21:09:16 2010 +0900
+++ b/do-record.sh.pt1	Tue Mar 30 00:37:47 2010 +0900
@@ -26,7 +26,8 @@
 elif [ ${MODE} = 1 ]; then
    # 目的のSIDのみ残す
    $RECORDER --b25 --strip --sid $SID $CHANNEL $DURATION ${OUTPUT} >/dev/null
-elif [ ${MODE} = 2 ]; then
-   $RECORDER $CHANNEL $DURATION ${OUTPUT}.tmp.ts --b25 --strip
-   ffmpeg -i ${OUTPUT}.tmp.ts ... 適当なオプション ${OUTPUT}
+# mode 2 example is as follows
+#elif [ ${MODE} = 2 ]; then
+#   $RECORDER $CHANNEL $DURATION ${OUTPUT}.tmp.ts --b25 --strip
+#   ffmpeg -i ${OUTPUT}.tmp.ts ... 適当なオプション ${OUTPUT}
 fi
--- a/recLog.inc.php	Sun Mar 28 21:09:16 2010 +0900
+++ b/recLog.inc.php	Tue Mar 30 00:37:47 2010 +0900
@@ -4,6 +4,20 @@
 define( "EPGREC_WARN" , 1 );
 define( "EPGREC_ERROR", 2 );
 
+class RecException extends Exception {
+	
+	private $level = EPGREC_INFO;
+	
+	public function __construct( $mesg, $l = EPGREC_INFO ) {
+		parent::__construct( $mesg );
+		$this->level = $l;
+	}
+	
+	public function getLevel() {
+		return $this->level;
+	}
+}
+
 
 function reclog( $message , $level = EPGREC_INFO ) {
 	
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/recorder.php	Tue Mar 30 00:37:47 2010 +0900
@@ -0,0 +1,245 @@
+#!/usr/bin/php
+<?php
+$script_path = dirname( __FILE__ );
+chdir( $script_path );
+include_once( $script_path . '/config.php');
+include_once( INSTALL_PATH . "/DBRecord.class.php" );
+include_once( INSTALL_PATH . "/Settings.class.php" );
+include_once( INSTALL_PATH . "/recLog.inc.php" );
+include_once( INSTALL_PATH . "/reclib.php" );
+
+// 後方互換性
+
+if( ! defined( RECORDER_CMD ) ) {
+	define( "RECORDER_CMD", INSTALL_PATH . "/recorder.php" );
+}
+
+
+// ノンブロッキングメッセージ受信
+
+function epgrec_get_message() {
+	global $msgh_r, $reserve_id;
+	
+	$r = msg_receive($msgh_r, (int)$reserve_id , $msgtype, 1024, $message, TRUE, MSG_IPC_NOWAIT | MSG_NOERROR);
+	if( $r ) return $message;
+	
+	return null;
+}
+
+// メッセージ送信
+
+function epgrec_send_message( $msg ) {
+	global $msgh_w, $reserve_id;
+	
+	msg_send( $msgh_w, (int)$reserve_id, $msg );
+	sleep(1);	// 相手が受信してくれそうな時間だけ待つ
+}
+
+
+function epgrec_exec( $cmd ) {
+	$descspec = array(
+                        0 => array( 'file','/dev/null','r' ),
+                        1 => array( 'file','/dev/null','w' ),
+                        2 => array( 'file','/dev/null','w' ),
+	);
+	$p = proc_open( $cmd, $descspec, $pipes );
+	if( is_resource( $p ) ) return $p;
+	
+	return false;
+}
+
+// 指定したプロセスハンドルが生成した子プロセスのpidリストを返す
+// こういうやり方しかないのか?
+//
+function epgrec_childproc( $p )
+{
+	$st = proc_get_status( $p );
+	$cpids = epgrec_childproc( $st['pid'] );
+	
+	// ps を実行する
+	$d = array(
+			0 => array( 'file','/dev/null','r' ),
+			1 => array( 'pipe','w' ),
+			2 => array( 'file','/dev/null','w' ),
+	);
+	
+	$ps = proc_open( "/bin/ps -o pid,ppid -ax" , $d, $pipes );
+	do {
+		$st = proc_get_status( $ps );
+	}while( $st['running'] );
+	
+	// 標準出力を読む
+	$cpids = array();
+	while( ! feof( $pipes[1] ) ) {
+		$line = fgets( $pipes[1] );
+		$pids = preg_split( "/[\s]+/", $line );
+		if( $pids[1] == $ppid ) {
+			array_push( $cpids, $pids[0] );
+		}
+	}
+	fclose( $pipes[1] );
+	proc_close( $ps );
+	
+	return $cpids;
+}
+
+// 指定したプロセスハンドルを子プロセスを含め終了させる
+
+function epgrec_termproc( $p )
+{
+	$cpids = epgrec_childproc( $p );
+	
+	@proc_terminate( $p );
+	sleep(1);
+	@proc_terminate( $p );	// 2度送る
+	
+	foreach( $cpids as $cpid ) {
+		@posix_kill( $cpid, SIGTERM );	// sigterm
+		usleep(100);
+		@posix_kill( $cpid, SIGKILL );	// sigkill
+	}
+	
+	return true;	// 保証できない
+}
+
+////// ここから本編
+
+$settings = Settings::factory();
+$reserve_id = $argv[1];
+$msgh_r = null;		// 受信用メッセージハンドラ
+$msgh_w = null;		// 送信用メッセージハンドラ
+
+// メッセージハンドラを得る
+$ipc_key = ftok( RECORDER_CMD, "R" );
+$msgh_r = msg_get_queue( $ipc_key );
+
+$ipc_key = ftok( RECORDER_CMD, "W" );
+$msgh_w = msg_get_queue( $ipc_key );
+
+try{
+	$rrec = new DBRecord( RESERVE_TBL, "id" , $reserve_id );
+	
+	// 時刻を得る
+	$starttime = toTimestamp($rrec->starttime);
+	$endtime   = toTimestamp($rrec->endtime);
+	
+	if( time() > $starttime ) {
+		// 過去の録画予約
+		$rrec->complete = 1;	// 終わったことにする
+		throw new RecException("recorder:: なぜか過去の録画予約が実行された", EPGREC_ERROR );
+	}
+	reclog("recorder:: 録画ID".$rrec->id .":".$rrec->type.$rrec->channel.$rrec->title."の録画ジョブ開始" );
+	
+	// 録画開始まで待つ
+	while( time() < $starttime ) {
+		if( ($message = epgrec_get_message() ) != null ) {
+			switch( $message ) {
+				case "terminate":			// 終了指示
+					epgrec_send_message("success");
+					$rrec->complete = 1;	// 終わったことにする
+					throw new RecException("recorder:: 録画ID".$rrec->id .":".$rrec->type.$rrec->channel.$rrec->title."の録画が中断された" );
+					break;
+				
+				case "stat":
+					epgrec_send_message("alive");
+					break;
+					
+				default:
+					break;
+			}
+		}
+		usleep( 50 * 1000 );				// 50ミリ秒待つ
+	}
+	
+	// 録画開始
+	
+	$proch = false;
+	if( ( $proch = epgrec_exec(DO_RECORD) ) !== false ) {
+		// 録画完了待ち
+		$rec_cont = true;
+		while( $rec_cont ){
+			$st = proc_get_status($proch);
+			if(! $st['running'] ) $rec_cont = false;    // 録画完了
+			
+			if( ($message = epgrec_get_message() ) != null ) {
+				switch( $message ) {
+					case "terminate":	// 終了指示
+						if( epgrec_termproc( $proch ) == false ) {
+							epgrec_send_message("error");
+							reclog( "録画コマンドを停止できません", EPGREC_WARN );
+						}
+						else {
+							epgrec_send_message("success");
+							reclog("recorder:: 録画ID".$rrec->id .":".$rrec->type.$rrec->channel.$rrec->title."の録画が中断された" );
+						}
+						break;
+					
+					case "stat":
+						epgrec_send_message("alive");
+						break;
+				
+					default:
+						break;
+				}
+			}
+			sleep(1);
+		}
+		proc_close( $proch );
+		$proch = false;
+	}
+	else {
+		$rrec->complete = 1;	// 終わったことにする
+		throw new RecException("recorder:: 録画コマンドの実行に失敗した", EPGREC_ERROR );
+	}
+	
+	// 予定より短いようなら終了時間を現在に書き換える
+	
+	if( time() < $endtime ) {
+		$rrec->endtime = toDatetime( time() );
+	}
+	
+	// 完了フラグを立てておく
+	$rrec->complete = '1';
+	
+	// ちょっと待った方が確実っぽい
+	sleep(15);
+	@exec("sync");
+	
+	if( file_exists( INSTALL_PATH .$settings->spool . "/". $rrec->path ) ) {
+		// 予約完了
+		reclog( "recorder:: 予約ID". $rrec->id .":".$rrec->type.$rrec->channel.$rrec->title."の録画終了" );
+	
+		// サムネール作成
+		if( $settings->use_thumbs == 1 ) {
+			$gen_thumbnail = INSTALL_PATH."/gen-thumbnail.sh";
+			if( defined("GEN_THUMBNAIL") ) 
+				$gen_thumbnail = GEN_THUMBNAIL;
+			@exec($gen_thumbnail);
+		}
+		
+		if( $settings->mediatomb_update == 1 ) {
+			$dbh = mysql_connect( $settings->db_host, $settings->db_user, $settings->db_pass );
+			if( $dbh !== false ) {
+				$sqlstr = "use ".$settings->db_name;
+				@mysql_query( $sqlstr );
+				// 別にやらなくてもいいが
+				$sqlstr = "set NAME utf8";
+				@mysql_query( $sqlstr );
+				$sqlstr = "update mt_cds_object set metadata='dc:description=".mysql_real_escape_string($rrec->description)."&epgrec:id=".$reserve_id."' where dc_title='".$rrec->path."'";
+				@mysql_query( $sqlstr );
+				$sqlstr = "update mt_cds_object set dc_title='".mysql_real_escape_string($rrec->title)."(".date("Y/m/d").")' where dc_title='".$rrec->path."'";
+				@mysql_query( $sqlstr );
+			}
+		}
+	}
+	else {	// 予約失敗
+		reclog( "recomplete:: 予約ID". $rrec->id .":".$rrec->type.$rrec->channel.$rrec->title."の録画に失敗した模様", EPGREC_ERROR );
+	}
+}
+catch( Exception $e ) {
+	reclog( "recorder:: ".$e->getMessage(), $e->getLevel() );
+}
+
+msg_remove_queue( $msgh_r );	// メッセージハンドラ開放
+msg_remove_queue( $msgh_w );	// メッセージハンドラ開放
+?>