#!/mod/bin/jimsh # Retrieve recording via dlna (using curl) whilst recording is still in progress and write it to stdout for use in pipelines source /mod/webif/lib/setup require lock system.class ts.class settings.class rsv.class set loglevel 0 set status 0 catch { # Use same logging option as auto log set settings [settings] set loglevel [$settings _nval_setting "autolog"] set status [$settings _nval_setting "chaseget_status" ] } if {[lindex $argv 0] eq "-d"} { set argv [lrange $argv 1 end] set loglevel 2 } proc log {msg {level 2}} { if {$level > $::loglevel} return system plog $::logfile "CG([pid])- $msg" #puts $::logfd "[\ # clock format [clock seconds] -format "%d/%m/%Y %H:%M:%S"\ # ] CG([pid])- $msg" #flush $::logfd } proc help_text {} { puts "chaseget - Retrieve recording via dlna (using curl) whilst recording is still in progress and write it to stdout for use in pipelines" puts "" puts "chaseget -d options = Detailed debug output" puts "chaseget recording.ts start_offset logfile = Retrieve recording" puts "chaseget -standby ignoreTerminals logfile = Check whether safe to put system back into standby (ignoreTerminals 0=No 1=Yes)" puts "chaseget -help = This help text" puts "Author MymsMan see http://hummy.tv/forum/forums/hd-hdr-fox-t2-customised-firmware.28/ for more information" } set file [lindex $argv 0] set fs [lindex $argv 1] if {$fs eq ""} {set fs 0} set logfile [lindex $argv 2] if {$logfile eq ""} {set logfile "chaseget"} #set logfd [open $logfile "a+"] set ::awfile "/mod/etc/chaseget_awakened" proc getfile {file {fs 0}} { set ts [ts fetch $file] log "ChaseGet $file" 0 set blksize $(256*1024) # extract recording info & calculate # - full recording size when complete # - remaining recording time and retrieval rate set stime [$ts get start] set etime [$ts get end] log "Recording start: [clock format $stime -format "%H:%M:%S"] end: [clock format $etime -format "%H:%M:%S"]" set csize [file size "$file"] set dlnaretry 0 set dlnaalert 0 while {$csize > $fs} { if {[system dlnastatus]} { set dlnaok 1 set dlnaretry 0 set dlnaalert 0 #log "DLNA Server is running." 2 } else { set dlnaok 0 incr dlnaretry log "DLNA Server is NOT running." 0 # Attempt to turn box fully on using IR package if {[system instandby]} { awake_from_standy } # Check file sharing enabled if {![system param DMS_START_ON]} { log "Content Sharing Disabled" 0 if {!$dlnaalert} { set dlnaalert 1 system notify "ChaseGet: Contents sharing disabled, Enable via Humax Settings menu" } } if {$dlnaretry >10} { log "DLNA Server is still NOT running. Giving up" 0 return "DLNA Server is still NOT running. Giving up" } sleep 60 continue } set ctime [clock seconds] set elapsed $($ctime-$stime) set durn $($etime-$stime) set remain $($etime-$ctime) log "duration: $durn elapsed: $elapsed remaining: $remain" 2 if {$remain > 0} { # Now using time slicing rather than throttling # set totsize $($csize*$durn/$elapsed) # set recrate $($csize/$elapsed) # set drate $(($totsize-$fs)/($remain)) # log "est total size: $totsize recording rate: $recrate download rate limit: $drate" # set lrate "--limit-rate $($drate*1)" # sleep 10 set lrate "" } else { set lrate ""} set retrct 0 lassign [$ts dlnaloc "127.0.0.1"] url if {$url ne ""} { log " $file - has been indexed." set helper 0 } else { log " $file - Not yet indexed, trying helper." if {[catch { lassign [system dlnahelper [file normalize $file]] url } msg]} { log " $file - $msg" system dlnahelper -release } if {$url eq ""} { log " $file - Can't use helper. retry" # system dlnahelper -release if {$remain > 30} {sleep 15} else {sleep 5} continue } set helper 1 } log "Start at offset $fs cur recording size: $csize DLNA: $url" 2 if {!$::status} { # show sctive on webif status display set statustok [system startop -multiple chaseget $file] } if {[catch -break -eval -signal -- { set input [open "|curl -C $fs $lrate $url 2> /dev/null "] } msg opts]} { log "Open caught: $msg $opts" 1 break } log "Curl pid [pid $input]" 2 while {1} { if {[catch -break -eval -signal -- { set block [read $input $blksize] puts -nonewline $block flush stdout } msg opts]} { log "caught: $msg $opts" 0 # set ps [exec ps -Af] # log $ps break } incr fs [string bytelength $block] incr retrct [string bytelength $block] if {[eof $input]} { log "EOF detected $fs $retrct" 2 break } } close $input # Short sleep to allow pipeline to drain and for more data to accumulate on input # Helper lock still held so parallel recording doesn't start getting and overload CPU sleep 3 # Release the helper lock once finished. if {$helper} { system dlnahelper -release } if {!$::status} { # not active for status system endop $statustok } if {$retrct==0} { log "No data returned" 1 break } set ctime [clock seconds] set remain $($etime-$ctime) if {$remain > 60} { # Sleep if not close to recording end to allow other recordings a chance to get in and for more data to accumulate log "snoozzzing" 2 sleep 30 } else { sleep 3 } set csize [file size "$file"] } if {[file size $file] >$fs } { log " Failed to retrieve entire file $([file size $file] -$fs) bytes missing" 0 log " Attempt to recreate failure message" 1 set curlout ""; set msg ""; set opts "" catch {set curlout [exec curl -C $fs $url > /dev/null]} msg opts log "Curl message: $msg" 1 log "Curl error: $opts" 1 log "Curl output: $curlout" 1 return -code error $msg } log "ChaseGet end $file Size $fs bytes" 0 #log "-----------------------------------------------" 2 return $fs } # Check whether now is within time range, time formats hh:mm proc inTimeRange {start end} { set now [clock format [clock seconds] -format "%H:%M"] log "Now $now Range $start - $end" 2 if {$start eq $end} {return 0} if {$start <= $end} { # start and stop times are in the same day if {$now >= $start && $now <= $end} { # current time is between start and stop return 1 } } else { # start and stop times are in different days if {$now >= $start || $now <= $end} { # current time is between start and stop return 1 } } # outside range return 0 } proc awake_from_standy {} { catch {exec ir POWER} log "System power on attempted" 0 set settings [settings] set mute [$settings _nval_setting "chaseget_mute" ] set standby_start [$settings _tval_setting "chaseget_standby_start1"] set standby_end [$settings _tval_setting "chaseget_standby_end1"] if {$mute && ![inTimeRange "$standby_start" "$standby_end"]} { sleep 2 # Mute in case it comes on in middle of night and to provoke button push by passive viewer catch {exec ir MUTE} log "Sound mute attempted" 1 } catch {exec touch $::awfile} #[settings new] _nval_setting "chaseget_awakened" [clock seconds] } proc awakened_by_chaseget {} { #set settings [settings] #set ::awakened [$settings _nval_setting "chaseget_awakened"] if {[file exists $::awfile]} { set ::awakened [file mtime $::awfile] log "Awakened by ChaseGet at [clock format $::awakened -format %T]" 2 return 1 } else { set ::awakened 0 return 0 } } proc clear_awakened_status {} { # [settings new] _nval_setting "chaseget_awakened" 0 file delete $::awfile set ::awakened 0 } proc standby_check {{ignoreTerm 1}} { # Quit quietly if nothing to do set settings [settings] set standby_start [$settings _tval_setting "chaseget_standby_start1"] set standby_end [$settings _tval_setting "chaseget_standby_end1"] #set lastboot $([clock seconds] - round([system uptime])) #log "Last boot time [clock format $lastboot -format %T]" 2 if {[inTimeRange "$standby_start" "$standby_end"]} { log "Standby check disabled - quit" 2 exit } if {![awakened_by_chaseget]} { log "Not awakened by chaseget - quit" 2 exit } log "ChaseGet - standby_check starting" 1 if {[system instandby]} { log "System in standby - quit" 1 clear_awakened_status exit } # foreach {process in detectads, auto, ...} { # # process list should probably be in a config file to allow easy update # # what others? # if {process active} { exit } # } # open and read configuration file set cf "/mod/etc/chaseget.conf" if {![catch {set fp [open $cf r]}]} { set clist [split [read $fp] "\n"] } else { set clist {} } # Check if proces in list is active foreach process $clist { if {![string length $process]} continue log "Checking entry: $process" 2 set status [catch {exec pgrep -x $process} ] if {$status == 0} { log "Active process $process - quit" 1 return 0 } } # # can putty/telnet users be detected ? set termct [exec ls /dev/pts | wc -l] log "Pseudo terminals: $termct" 2 if {!$ignoreTerm && $termct > 0 } { log "Active terminals $termct - quit" 1 return 0 } # # if { last boot time > chaseget awakened time} { # Clear awakend by chaseget status # exit # } # Ideally should be: set lastboot [system lastboottime] #set lastboot [file mtime "/tmp/if-up"] set lastboot $([clock seconds] - round([system uptime])) log "Last boot time [clock format $lastboot -format %T]" 2 if {$lastboot > $::awakened} { log "Boot time [clock format $lastboot -format %T] > Awakened time [clock format $::awakened -format %T] - quit" 1 clear_awakened_status return 0 } # # if { any .ts in use (except recording) } { exit } # set ret {} # Get a list of unique in-use .ts files if {[catch {set data [exec /mod/webif/lib/bin/lsof -Fns | grep {\.ts} | sort -u ]} msg]} { set ret {} } else { set flist "" foreach line [split $data "\n"] { set file [string range $line 1 end] if {[file tail $file] eq "0.ts"} continue set csize [file size "$file"] log "In use $file size $csize" 2 lappend flist [list $file $csize] } sleep 2 # Check for files size change over last few seconds - indicates recording in progress foreach line $flist { lassign $line file csize set nsize [file size "$file"] log "In use $file size $csize -> $nsize" 2 if {$csize == $nsize} { log "In use $file not recording -quit" 1 return } else { log "In use $file is recording OK" 2 } } } # # # Need check for within a reminder period? set now [clock seconds] set events [rsv list tbl_reservation \ " where ersvtype = 2 and nsttime < $now and nsttime + nduration > $now "] if {[llength $events]} { foreach event $events { log [concat \ "Reminder '[$event name]' " \ "on [$event channel_name] at " \ "[clock format [$event get nsttime] -format {%H:%M}]" \ " duration [clock format [$event get nduration] -format {%H:%M}]" \ ] 2 } log "Current reminders= [llength $events] - quit" 1 return 0 } else {log "No reminders found" 2} # if {idle time < now - chaseget awakened time} { # # Buttons pressed since woken # if {last button != 'power'} { # Clear awakend by chaseget status # exit # } # } set idletime [system idletime] set now [clock seconds] if {$idletime < $now - $::awakened} { set lastir 0 if {[file readable /tmp/.lastir]} { set lastir [file read /tmp/.lastir -nonewline] } # Buttons pressed since woken log "Remote Buttons pressed (last button $lastir) - quit" 1 clear_awakened_status return 0 } # # No current activity since awakened # # ir power # Clear awakend by chaseget status # Check system hasn't re-entered standby of it its own accord whilst we werent looking if {[system instandby]} { log "System in standby - quit" 1 clear_awakened_status exit } log "System power Off attempted, ChaseGet power On was at [clock format $::awakened -format %T] " 0 clear_awakened_status catch {exec ir POWER} } switch -- $file { "-standby" { standby_check $fs } "--help" - "-help" - "--h" - "-h" - "" { help_text } default { getfile $file $fs } } exit