VLC Extensionsからサーバのrecpt1を起動、停止させる

# はじめに
家では録画サーバとしてPT3を使用している。
PT3をリアルタイム試聴するには、一度サーバにログインし、recpt1を実行しなければならないが、これがめんどくさい。
なので今回サーバのrecpt1を起動、停止させるVLC Extensionsを作った。

# BitBucket
https://bitbucket.org/mkit0031/watchtvbyvlcextensions

# ファイル構成
作成したのは以下のファイル
* startrecpt1.php
* stoprecpt1.php
* watchTV.lua

## startrecpt1.php
* アクセスするとrecpt1を実行する。レスポンスとしてpidを返す。実行に失敗した場合は何もしない。
* recpt1が既に実行済の場合はそのpidを返す。
* パラメータとして、?start=trueを受け取らないと開始しない(誤動作防止のため)。受け取らない場合は何もしない。

## stoprecpt1.php
* パラメータとして、?pid=pidを受け取る。受け取らない場合は何もしない。
* pid受け取り時、recpt1のpidと一致するか確認する。
* 受け取ったpidのプロセスを殺す。

## watchTV.lua
* 実行時にサーバ側でrecpt1が起動する。
* ドロップダウンからチャンネルを選択可能。VLCのプレイリストにもチャンネル一覧が追加される。
* 終了する場合は、地デジ視聴ダイアログを閉じるか、視聴中にQキーを押すようにする。

# コード
```php:startrecpt1.php
<?php

$start = false;
if( isset( $_GET['start'] ) ){
    $start = $_GET['start'];
}

// recpt1が既に実行されているか確認。
// 実行されていればpidを、されていなければfalseを返す。
function checkPidOfRecpt1(){
    $ret = false;
    $command = "ps aux | grep recpt | awk '$11 ~ /^recpt1/ && /http/{print $2}'";
    $handle = popen($command , "r");
    if($handle){
        $ret = fread($handle , 10);
    }
    pclose($handle);
    return $ret;
}

// recpt1実行。pidを出力する。
function startRecpt1(){
    $handle = popen("recpt1 --b25 --strip --sid hd --http 8080 2>&1" , "r");
    if($handle){
        while(!feof($handle)){
            $buffer = fgets($handle);
            if(preg_match('/pid\s*=\s*(\d+)/', $buffer, $m)){
                print($m[1]."\n");
                break;
            }
        }
    }
    pclose($handle);
}

if( $start == "true" ){
    if( $pid = checkPidOfRecpt1() ){
        print($pid);
    }else{
        startRecpt1();
    }
}
?>
```
```php:stoprecpt1.php
<?php

$pid = false;
if( isset( $_GET['pid'] ) ){
    $pid = intval($_GET['pid']);
}

// 届いたpidがrecpt1のpidか確認する
// pidが数字であることを確認してからこの関数を呼ぶこと
function isPidOfRecpt1($pid){
    $ret = false;
    $command = "ps aux | grep recpt | awk '$1 == \"www-data\" && $11 ~ /^recpt1/ && $2 == " . $pid . " && /http/{print $2}'";
    $handle = popen($command , "r");
    if($handle){
        $buffer = fread($handle , 10);
        if($buffer){
            $ret = true;
        }
    }
    pclose($handle);
    return $ret;
}

// 受け取ったpidを殺す
// pidが存在するか確認してからこの関数を呼ぶこと
function killPid($pid){
    $command = "kill -9 ". $pid . " 2>&1";
    $handle = popen($command , "r");
    if($handle){
        $buffer = fread($handle , 100);
        print($buffer . "<br>");        // デバッグ用
    }
    pclose($handle);
}

if( $pid ){
    if( isPidOfRecpt1($pid) ){
        killPid($pid);
        print($pid);                    // デバッグ用
    }
}

?>
```
```lua:watchTV.lua

channel_name_to_number = {}
channel_name_to_number["MBS毎日放送"] = 16
channel_name_to_number["ABCテレビ"]   = 15
channel_name_to_number["NHK総合"]     = 24
channel_name_to_number["NHK教育"]     = 13
channel_name_to_number["テレビ大阪"]  = 18
channel_name_to_number["関西テレビ"]  = 17
channel_name_to_number["読売テレビ"]  = 14
channel_name_to_number["サンテレビ"]  = 26
channel_name_to_number["KBS京都"]  = 23


pid = nil
dlg = nil
dropdown = nil
quit_key = 113 -- Qキー

function descriptor()
  return { title = "地デジ視聴" ,capabilities={} }
end

function activate()
	 pid = nil
	 dlg = nil
	 dropdown = nil

 	 local s = vlc.stream( "http://192.168.1.2/epgrec/startrecpt1.php?start=true" )
 	 pid = tonumber(s:read(10))

	 local path = "http://192.168.1.2:8080/15"
	 local name = "ABCテレビ"
	 local mytable = {path = path; name = name}
	 vlc.playlist.add({mytable})
   for name, number in pairs(channel_name_to_number) do
			local path = "http://192.168.1.2:8080/" .. number
			local mytable = {path = path; name = name}
      vlc.playlist.enqueue({mytable})
   end
	 create_dialog()
   vlc.var.add_callback(vlc.object.libvlc() , "key-pressed", key_press)
end

function deactivate()
	 local s = vlc.stream( "http://192.168.1.2/epgrec/stoprecpt1.php?pid=" .. pid )
	 vlc.playlist.clear()
	 vlc.var.del_callback(vlc.object.libvlc() , "key-pressed", key_press)
	 vlc.deactivate()
end

function close()
	 local s = vlc.stream( "http://192.168.1.2/epgrec/stoprecpt1.php?pid=" .. pid )
	 vlc.var.del_callback(vlc.object.libvlc() , "key-pressed", key_press)
	 vlc.deactivate()
end

function create_dialog()
	 dlg = vlc.dialog("地デジ視聴")

	 dropdown = dlg:add_dropdown(1,1,1,1)
   for name, number in pairs(channel_name_to_number) do
      dropdown:add_value(name, number)
   end

   dlg:add_button("視聴", click_select_button, 2,1,1,1)
end

function key_press(var , old , new , data)
	 if new == quit_key then
			dlg:hide()
			dlg:update()
			dlg:delete()
			vlc.deactivate()
	 end
end

function click_select_button()
   local channel = dropdown:get_value()
	 local path = "http://192.168.1.2:8080/" .. channel
	 local name = "テレビ"
	 local mytable = {path = path; name = name}
	 vlc.playlist.add({mytable})
end
```

# どうしてもできなかったこと
watchTV.luaを終了せずにVLCを終了した場合、サーバ側のrecpt1は起動したままになってしまう。
watchTV.lua内のclose()で終了するようにしているはずだが、なぜだろう。
VLCの終了時に呼ばれるメソッドはclose()じゃないのだろうか。
また、VLCの終了時に呼ばれるメソッドはもしかしたら存在しないのかもしれない。

# 参考
[VLCのエクステンションで地デジをリアルタイム視聴する](http://d.hatena.ne.jp/saitodevel01/20101004/1286140929)
[VLC Extensions Readme](http://www.videolan.org/developers/vlc/share/lua/README.txt)