chaseget/bin/chaseget

484 lines
16 KiB
Plaintext
Executable File

#!/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 fl [glob -nocomplain "/mod/tmp/*-inp.ts" ]
log "orphan file list $fl" 2
foreach f $fl {
if {[file mtime $f]+3600 < [clock seconds]} {
if {[safe_delete $file chaseget/orphans]} {
log "$f safe deleted" 0
} else {
log "$f safe_delete failed" 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
set bname [file rootname [file tail $file]]
set iname "$bname-inp"
set ipath "/mod/tmp"
set ifile "$ipath/$iname.ts"
file delete -force "$ipath/$iname.ts"
catch {file link -hard "$ipath/$iname.ts" "[file normalize [file rootname $file].ts]"}
file delete -force "$ipath/$iname.hmt"
catch {file link -hard "$ipath/$iname.hmt" "[file normalize [file rootname $file].hmt]"}
# Is file already indexed
lassign [$ts dlnaloc "127.0.0.1"] fileurl
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
set url $fileurl
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 $ifile]] url
} msg]} {
log " $ifile - $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 Files Size $fs Retrieved $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"]
}
# remove alias
file delete -force "$ipath/$iname.ts"
file delete -force "$ipath/$iname.hmt"
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" 0
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 {} {
# Usee zero intead of Power for wakeup. less risk
catch {exec ir ZERO}
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