ラベル recman の投稿を表示しています。 すべての投稿を表示
ラベル recman の投稿を表示しています。 すべての投稿を表示

2015/07/28

クエリレコード作成と番組自動予約

手動で予約~録画、視聴の流れはできたかな。次は自動予約だ。
前回はRedmineプラグインだったので制約が結構あったが、今回は自由がある。
その代わり面倒なところもある。

Queryモデル作成

$ rails g  scaffold query title:string start_time:time end_time:time query_type_id:integer category_id:integer sub_category_id:integer priority:integer

Queryレコード作成・変更・削除が必要なのでscaffoldで生成したコードを全部使用する。

query_type_idは、予約:1か検索:2を示す部分で、自動予約処理で使用する。
予約したいわけじゃなく「今日のゴールデンタイムのドラマは?」 を見たいときは検索タイプ。

start_timeとend_timeは、日時ではなく時間帯を指定したいので datetime ではなく time 型である。

priorityは将来的なもので、自動予約で時間がバッティングした場合、どちらを優先するのか?を設定しておくことで勝ち負けも自動でやらせようかという思惑。

models/query.rbの中身
# -*- coding: utf-8 -*-
class Query < ActiveRecord::Base
  belongs_to :category, -> { where cate_type: 0 }
  belongs_to :sub_category, -> { where cate_type: 1 }, class_name: 'Category'
  belongs_to :query_type

  def list
    programs = Program.all
    programs = programs.where(category_id: category_id) if category.present?
    programs = programs.where(category2_id: sub_category_id) if sub_category.present?
    programs = programs.where("title like :name", name: "%#{title}%") if title.present?

    # todo: 時間がセットされていないと判断する賢い方法はないのか?
    unless start_time.strftime("%H%M") + end_time.strftime("%H%M") == '00000000'
      programs = programs.where("TIME(start_at) >= TIME(?) and TIME(start_at) <= TIME(?)",
                                start_time, end_time)
    end
    return programs
  end

end

queryのlistメソッドで条件にマッチした番組リストを返す。条件が設定されているかどうかを値が入っているかどうかで判断したいけど、time型の場合が厄介。

f.time_selectでblankありにして、blank設定のままでも"0:00"になってしまう。nilにならない。

これをどうしたら良いの?という記事はいくつか見つかる。
http://stackoverflow.com/questions/14367705/time-select-blank-field-saves-a-default-time-when-form-is-submitted
とかで語られているソリューションがほとんど。だけど自分の肌に合わなかったので、
start_time.strftime("%H%M") + end_time.strftime("%H%M") == '00000000'
と、若干強引な方法で確認する羽目となった。

検索結果表示は、番組リスト(Program#index)で済ませる
内部処理はこんな↓
    @q = Program.where(video_id: nil).ransack(params[:q])
    if params[:query_id].present?
      @programs = Query.find_by(params[:query_id]).list
    else
      @programs = @q.result
    end
    @programs = @programs.order(:start_at).page(params[:page])
query_id付きで呼ばれた場合と普通の時で処理分け。
ransackも機能する必要があるので、@qの処理はどっちでもやる。

自動予約

繰り返し実行なのでActiveJobではなく、rake task にして whenever で回す。
タスクはすごく単純。 予約タイプのQueryレコードから条件にマッチする番組の予約をするだけ。
# -*- coding: utf-8 -*-
namespace :query do

  desc "自動予約"
  task :reserve => :environment do
    Query.where(query_type_id: 1).each do |q|
      q.list.where(video_id: nil).where('start_at > ?',Time.now+60).each do |program|
        program.reserve
      end
    end
  end

end

予約済みや、今より過去の番組を予約しないように、若干の条件付けを施す程度。
これだけで結構ちゃんと機能している。
以前に書いた処理に比べれば大分シンプルにまとまったような気がする。

これで全体機能は一段落ですが、実際に運用し始めるとまた色々問題が出るんですよねぇ。

2015/07/25

予約レコード周りのその他こまごましたところ

大筋の機能は何とかイメージ通りに実装できたのですが、細かいところでも知らないがゆえに
引っかかりながらの実装となった点をまとめておく。

flashメッセージ表示

予約成功!、予約失敗などを表示するのをflashメッセージを使ってやろうとしたんだけれど、
bootstrapの場合、Rails標準のflashタイプじゃなくてbootstrap後に変換が必要なことを知らず
ちょっとはまってしまった。

通常 redirect_to うにゃうにゃ, notice: "成功!" ってやると思うんだけれど
この"notice"をbootstrap的にはclass "alert alert-success"とかに変換しないと出ない。
その辺りの記事は沢山あった。よくあるパターンとしては
notice => alert-info、alert => alert-warning、error => alert-danger とかに対応させている。
この通りやるのだが、メッセージが表示されない。もしくは無色で表示される。
違う。バックが青とか赤で囲われて欲しいのだよ。結局自分の場合、

notice => alert-success、alert => alert-danger とした。
バックカラーが、successがグリーン、dangerが赤で表示されるから。それ以外は白?だった。
それに、redirect_toのオプションで付けられるのは、notice、alert、flash => {ゴニョゴニョ} の3つ。
noticeとalertだけなら簡単だし。

予約時間の重複チェック

チューナー1つしか無いので、予約しようとした時間帯に引っかかる予約済みレコード確認をして、引っかかっしまう場合はエラーにしたいわけです。
検索としては、既存レコードの開始時間がその時間帯内である もしくは 終了時間がその時間帯内にある場合は・・・です。ということは ”OR” でつなげるわけです。
Program.where(ゴニョゴニョ)で一気に書こうとするとSQL文をなが~く書かないとならない。
嫌なので、"OR"はRubyでやることで全体コードをシンプルにした。

video_on = Program.where.not(video_id: nil)
start_on = video_on.where(start_at: (@program.start_at..@program.end_at))
end_on = video_on.where(end_at: (@program.start_at..@program.end_at))
if start_on.present? or end_on.present?
  redirect_to program_url, alert: 'Reserved violation another videos'
return
end
こんな感じ。BETWEEN生成はwhereに任せ、面倒なORはRubyで。
連続しているものは許したいので実際にはstat,endは微妙に調整する。

録画ジョブ開始タイミングは番組時間の30秒前

ActiveJobのperformが開始されて実際の録画を開始するまでには結構時間がかかる。
なのでJenkins処理でやっていた時のように、少し早めにスタートさせて sleep でタイミング調整
をするようにしている。
recfsusb2n自体にも--waitで待つ機能があるようだけど正確に働かない感じだったので
スクリプト内のsleepで待つようにした。

録画時間は15秒前終了

重なってはいないけど録画時間が連続している場合、多少のギャップを作ってチューナー
を空けておかないと録画開始時にデバイスエラーになってしまうので、若干短めにしておく。


とまあ、細かいところだけれど、録画失敗してがっかりとならないようにやっておかないとね。
で実際の録画ジョブコードは以下のとおり。
  def perform(video)
    if video
      # write path
      path = video.program.start_at.strftime("%Y%m")
      fdir = "#{VIDEO_ROOT}/#{path}"
      Dir.mkdir(fdir, 0777) unless Dir.exist?(fdir)

      fname = "#{path}/#{video.id}"
      fpath = "#{VIDEO_ROOT}/#{fname}"

      # recording
      channel = video.program.channel.ch
      duration = video.program.duration - 15
      wait = (video.program.start_at - Time.now).to_i

      sleep wait if wait>0

      p "------recording start: #{fname}"
      begin
        `tvoff`
        # recode
        `recfsusb2n -b -i hd #{channel} #{duration} #{fpath}.ts`
        `tvon`
        # encode
        p `ffmpeg -loglevel 8 -y -i #{fpath}.ts -c:v libx264 -c:a libfaac -preset superfast -f\
 mp4 -threads 2 -b:v 2000k #{fpath}.mp4`
        # poster
        p `ffmpeg -loglevel 8 -i #{fpath}.mp4 -ss 12 -vframes 1 -an -s 320x180 #{fpath}.jpg`
        # successed
        video.update_attributes(status_id: 2, filename: "#{fname}") # successfully.
        File.delete("#{fpath}.ts")

      rescue => e
        p e.message
        video.update_attributes(status_id: 3) # recording job failed.
      end
      p "------recording end: #{fpath}"
    end
  end

録画の前後でtvon、tvoffっていうのを実行するようになっていますが、これは自分が適当に
作ったシェルスクリプトでして、以下の様なシロモノです。
tvon
#!/bin/bash
recfsusb2n -b -i hd -H 8888 1>/dev/null 2>/dev/null
tvoff
#!/bin/bash
PID=`pidof recfsusb2n`
if test "$PID" != "" ; then
    kill -9 $PID
fi
録画していない時はリアルタイム視聴できる状態にスタンバイさせておこうというものです。
朝方とか結構重宝します。家族食事中は子供にTVを見せないポリシーなもんで。

2015/07/08

rvm + rails4 でサーバーアプリ開発環境

いつもの流れではあるので特出すべきことはないですが・・・
Shuttleで録画システム再開発ということで、前回の反省点を踏まえながら進めていく。

まずは、rvmとrails4.2 railsは仕事では3までしか使ってないんで4は自分的に初!

rvm

$ sudo apt-get intall curl

-- ここからは全部自分環境なので sudo じゃないよ --

$ curl -sSL https://rvm.io/mpapis.asc | gpg --import -
$ curl -sSL https://get.rvm.io | bash -s stable
$ source /home/shuttle/.rvm/scripts/rvm ← shuttleというユーザ名でやってます
$ rvm list known ←選択可能なrubyバージョンを確認
$ rvm install ruby-2.2.1 --default

これが長いんだ・・・暫し待つ。
この時点で gemとgem基本パッケージが入っている

rails4.2

$ gem i rails --no-ri --no-doc

これも長いよねぇ・・・


アプリ作成

$ rails new recman ←という名のアプリ作成
$ cd recman
$ bundle install
$ rails s

とりあえずWebrickでHTTPサーバー起動するかな?

あぁ、そうだった。developmentでWebrick起動するためには、'therubyracer'が必要でした。
Gemfileに、gem 'therubyracer' を追記。

もう一度
$ bundle install
$ rails s

別のPCから、http:://shuttle:3000/ へアクセス。およ?アクセス出来ない・・・?

最初は、ufw とか考えたけれど、いやいやEnforceしてないし・・・結局調べたら
何と rack version 1.6から仕様が変わったらしく、デフォルトが http://localhost:3000 となるらしい。
要するに自分自身以外からのアクセスが出来ないと。

これだと困るので

$ rails s -b 0.0.0.0

ってやると、どこからでもアクセスできるようになる。むぅ、ちょっとめんどくさいな。
rails4.2環境は初めてなので、こんなところでも引っかかってしまう。