version 1 - handle conflicts and schedule changes
This commit is contained in:
commit
d7b6477d68
|
@ -0,0 +1,11 @@
|
|||
Package: schedchk
|
||||
Priority: optional
|
||||
Section: misc
|
||||
Version: 0.1.0-0
|
||||
Architecture: mipsel
|
||||
Maintainer: mymsman
|
||||
Depends: webif(>=1.4.8-1),
|
||||
Description: Check scheduled recordings against EPG and reschedule, if possible, entries that don't match the EPG
|
||||
or cause conflicts
|
||||
|
||||
Tags: http://wiki.hummy.tv/wiki/schedchk
|
|
@ -0,0 +1,464 @@
|
|||
#!/mod/bin/jimsh
|
||||
# "schedchk = Check for recording schedule issues and attempt to fix them"
|
||||
# author MymsMan based on webif functions by af123
|
||||
|
||||
source /mod/webif/lib/setup
|
||||
require rsv.class epg.class system.class
|
||||
|
||||
proc log {msg {level 1}} {
|
||||
if {$level > $::loglevel} return
|
||||
puts $::logfd "[\
|
||||
clock format [clock seconds] -format "%d/%m/%Y %H:%M:%S"\
|
||||
] SC([pid])- $msg"
|
||||
flush $::logfd
|
||||
}
|
||||
|
||||
|
||||
# Parse command options and apply defaults
|
||||
proc ::schedchk::checkopts {argv} {
|
||||
set settings [settings]
|
||||
set parmerror 0
|
||||
|
||||
set ::autologlevel [$settings _nval_setting "autolog"]
|
||||
set ::loglevel $::autologlevel
|
||||
if {[info exists ::auto::logfd]} {
|
||||
set logfd ::auto::logfd
|
||||
}
|
||||
|
||||
# List of options with default values
|
||||
set optarray {
|
||||
d 0
|
||||
debug 0
|
||||
alert 0
|
||||
test 0
|
||||
noconflict 0
|
||||
}
|
||||
|
||||
# Override default from settings DB
|
||||
foreach {key defvalue} [array get optarray] {
|
||||
set ::opts($key) [$settings _nval_setting "schedchk_$key"]
|
||||
if {$::opts($key)==0} {set ::opts($key) $defvalue}
|
||||
}
|
||||
|
||||
# Handle text setting for oher options
|
||||
set otheropts [$settings _tval_setting "schedchk_otheropts"]
|
||||
if {$otheropts == 0} {set otheropts ""}
|
||||
|
||||
# Parse argument lists
|
||||
foreach argl [list $otheropts $argv] {
|
||||
set ::optlist ""
|
||||
log "arg list $argl" 2
|
||||
for {set ix 0} {$ix < [llength $argl]} {incr ix} {
|
||||
set arg [lindex $argl $ix]
|
||||
|
||||
#check if option in optarray list
|
||||
if {[string range $arg 0 0] == "-"} {
|
||||
set argx [string tolower [string range $arg 1 end]]
|
||||
if {[dict exists $optarray $argx]} {
|
||||
incr ix
|
||||
set val [lindex $argl $ix]
|
||||
set nval $val
|
||||
if {$val eq "y"} {set nval 1}
|
||||
if {$val eq "n"} {set nval 0}
|
||||
if {![string is double -strict $nval]} {
|
||||
if {[string length $nval] == 0 ||
|
||||
[string range $nval 0 0] == "-"} {
|
||||
# Value omitted assume true
|
||||
set nval 1
|
||||
set val "y"
|
||||
incr ix -1
|
||||
} else {
|
||||
log "Option $arg value ($val) is not y, n or numeric" 0
|
||||
incr ix -1
|
||||
set parmerror 1
|
||||
continue
|
||||
}
|
||||
}
|
||||
lappend ::optlist $arg
|
||||
lappend ::optlist $val
|
||||
set ::opts($argx) $nval
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
# check other options
|
||||
switch -- $arg {
|
||||
-rsv -
|
||||
--help -
|
||||
-h {
|
||||
set ::opt $arg
|
||||
}
|
||||
-hmt {
|
||||
set ::opt $arg
|
||||
incr ix
|
||||
set file [lindex $argl $ix]
|
||||
set ::file $file
|
||||
|
||||
if {[file isfile $file]} {
|
||||
set ::ts [ts fetch $file]
|
||||
if {$::ts == 0} {
|
||||
log "Cannot process ($file) file is not valid recording" 0
|
||||
set parmerror 1
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
log "Cannot process ($file) file does not exist" 0
|
||||
set parmerror 1
|
||||
continue
|
||||
}
|
||||
}
|
||||
default {
|
||||
log "Unrecognized option: $arg" 0
|
||||
set parmerror 1
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if {$::opts(debug) || $::opts(d)} {
|
||||
set ll $::loglevel
|
||||
if {$::opts(debug) > $ll} {set ll $::opts(debug)}
|
||||
if {$::opts(d) > $ll} {set ll $::opts(d)}
|
||||
set ::debug 1
|
||||
set ::loglevel $ll
|
||||
set ::auto::loglevel $ll
|
||||
}
|
||||
|
||||
if {$parmerror} {
|
||||
log "Parameter errors found"
|
||||
exit
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
proc ::schedchk::svcmap {} {
|
||||
# establish rsv <-> epg service_id mapping
|
||||
global svcmap
|
||||
set svcmap {}
|
||||
lmap i \
|
||||
[$::channeldb query {select hSvc, usSvcid from TBL_SVC}] \
|
||||
{
|
||||
set svcmap([lindex $i 1]) [lindex $i 3]
|
||||
}
|
||||
set svckeys [array names svcmap]
|
||||
}
|
||||
proc ::schedchk::conflicts {} {
|
||||
global conflicts
|
||||
if {$::opts(noconflict)} {
|
||||
log "-noconflict Bypassing automatic conflict resolution"
|
||||
set conflicts {}
|
||||
return
|
||||
}
|
||||
|
||||
set conflicts [rsv newconflicts [system tuners] "xlist"]
|
||||
if {[llength $conflicts] > 1} {
|
||||
log "++++ [llength $conflicts] Conflicts exist +++" 1
|
||||
log "$conflicts" 2
|
||||
}
|
||||
}
|
||||
|
||||
proc ::schedchk::rsvscan {} {
|
||||
global svcmap conflicts
|
||||
::schedchk::svcmap
|
||||
::schedchk::conflicts
|
||||
|
||||
|
||||
set events [rsv list]
|
||||
|
||||
# for each reservation
|
||||
foreach event $events {
|
||||
set name [$event name]
|
||||
set s [$event start]
|
||||
set ds "[clock format $s -format {%a %d %b %Y %H:%M}]"
|
||||
set d [$event get nduration]
|
||||
set e $($s + $d)
|
||||
set now [clock seconds]
|
||||
lassign [$event padded 1] sp ep
|
||||
|
||||
# Ignore manual recordings & reminders
|
||||
if {[$event get ersvtype] != 3} { continue}
|
||||
# Has event passed
|
||||
if {$now > $e + $ep} {
|
||||
set ended 1
|
||||
incr num_ended
|
||||
} else {
|
||||
set ended 0
|
||||
}
|
||||
|
||||
set devent "$ds [clock format $d -format {%H:%M}] === $name === [$event channel_name]"
|
||||
|
||||
if {!$ended} {
|
||||
log "Reservation - $devent" 2
|
||||
set elist [$event aul]
|
||||
set enum -1
|
||||
set ecrids [split [$event get szEventToRecord] "|"]
|
||||
set ecrid [$event get szCRID]
|
||||
|
||||
if {[llength $elist] > 0} {
|
||||
# check each episode scheduled
|
||||
foreach epsd $elist {
|
||||
lassign $epsd service_id start end event_id
|
||||
incr enum
|
||||
|
||||
set svc $svcmap($service_id)
|
||||
# Retrieve epg record for the episode
|
||||
set record [lindex [\
|
||||
epg dbfetch dump -service $svc -event $event_id -sort ""] 0]
|
||||
log "$service_id $start $end $svc $event_id $record" 3
|
||||
if {$record==""} {
|
||||
log "+++ No matching epg entry ++++ $devent" 0
|
||||
# look for an alternate showing
|
||||
set ecrid [lindex $ecrids $enum]
|
||||
set ecrid [string range $ecrid [string first "/" $ecrid] end]
|
||||
set others [epg dbfetch dump -crid $ecrid -nocase 1 -debug 1]
|
||||
#param "collate nocase"
|
||||
log "$ecrid $ecrids $others" 0
|
||||
foreach other $others {
|
||||
set ostart [$other get start]
|
||||
set odur [$other get duration]
|
||||
set oend $($ostart+$odur)
|
||||
set oname [$other get name]
|
||||
set ocname [$other get channel_name]
|
||||
set dother "[clock format $ostart -format {%a %d %b %Y %H:%M}] [clock format $odur -format {%H:%M}] === $name === $ocname"
|
||||
if {$ostart <= $now} {continue}
|
||||
## should favour same definition
|
||||
log "Alternate Episode $dother" 2
|
||||
set oconflicts [rsv checkconflict \
|
||||
$ostart $odur \
|
||||
[system tuners]]
|
||||
# Should ignore conflicts with self
|
||||
if {[llength $oconflicts]} {
|
||||
# Alternate has Conflicts
|
||||
log "Alternate conflicts $oconflicts" 2
|
||||
continue
|
||||
}
|
||||
# attempt to schedule the alternate
|
||||
if {[::schedchk::schedule $other $dother]} {
|
||||
if {![$event isseries]} {
|
||||
# delete single recording
|
||||
::schedchk::cancel $event $deps
|
||||
} else {
|
||||
# refresh since can't skip without epg entry
|
||||
::schedchk::refresh $event $deps
|
||||
}
|
||||
::schedchk::conflicts
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
# Check that details match
|
||||
set dur $($end-$start)
|
||||
set epgname [$record get name]
|
||||
set deps "[clock format $start -format {%a %d %b %Y %H:%M}] [clock format $dur -format {%H:%M}] === $epgname === [$record get channel_name]"
|
||||
|
||||
log "Episode $deps" 2
|
||||
set ok 1
|
||||
if {$start != [$record get start]} {
|
||||
set ok 0
|
||||
log "+++ Start Mismatch: $start != [$record get start] +++ $deps" 1
|
||||
}
|
||||
if {$dur != [$record get duration]} {
|
||||
set ok 0
|
||||
log "+++ Duration Mismatch: $dur != [$record get duration] +++ $deps" 1
|
||||
}
|
||||
# Refresh event since we cant update individual entry
|
||||
if {!$ok} {
|
||||
::schedchk::refresh $event $deps
|
||||
::schedchk::conflicts
|
||||
}
|
||||
|
||||
# Check for conflicts
|
||||
if {"[$event get ulslot]$end" in $conflicts} {
|
||||
set ok 0
|
||||
log "+++ Confict exists +++ $deps" 1
|
||||
set others [$record othertimes]
|
||||
foreach other $others {
|
||||
set ostart [$other get start]
|
||||
set odur [$other get duration]
|
||||
set oend $($ostart+$odur)
|
||||
set oname [$other get name]
|
||||
set ocname [$other get channel_name]
|
||||
set dother "[clock format $ostart -format {%a %d %b %Y %H:%M}] [clock format $odur -format {%H:%M}] === $name === $ocname"
|
||||
if {$ostart == $start} {continue}
|
||||
if {$ostart <= $now} {continue}
|
||||
## should favour same definition
|
||||
log "Alternate Episode $dother" 2
|
||||
set oconflicts [rsv checkconflict \
|
||||
$ostart $odur \
|
||||
[system tuners]]
|
||||
# Should ignore conflicts with self
|
||||
if {[llength $oconflicts]} {
|
||||
# Alternate has Conflicts
|
||||
log "Alternate conflicts $oconflicts" 2
|
||||
continue
|
||||
}
|
||||
# attempt to schedule the alternate
|
||||
if {[::schedchk::schedule $other $dother]} {
|
||||
if {![$event isseries]} {
|
||||
# delete single recording
|
||||
::schedchk::cancel $event $deps
|
||||
} else {
|
||||
# skip conflicted epidode
|
||||
::schedchk::skip $event $svc $event_id $deps
|
||||
}
|
||||
::schedchk::conflicts
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log "+++ No episodes scheduled +++ $devent" 0
|
||||
}
|
||||
} else {
|
||||
log "Completed - $devent" 2
|
||||
# check for new series in same time slot
|
||||
}
|
||||
}
|
||||
if {[llength $conflicts] > 1} {
|
||||
log "++++ [llength $conflicts] Unresolved conflicts remain +++" 0
|
||||
}
|
||||
log "===============================================" 1
|
||||
epg cleanup
|
||||
}
|
||||
|
||||
# Create a new reservation
|
||||
proc ::schedchk::schedule {event desc} {
|
||||
if {[$event scheduled]} {
|
||||
log "*** Already scheduled *** $desc" 0
|
||||
return 1
|
||||
}
|
||||
if {$::opts(test)} {
|
||||
log "*** Test mode =scheduled *** $desc" 0
|
||||
return 1
|
||||
}
|
||||
set type 1
|
||||
set r [rsv construct $event $type]
|
||||
if {[catch {$r insert pending} msg]} {
|
||||
log "+++ Error encountered while scheduling: $msg ++++ $desc" 0
|
||||
return 0
|
||||
} else {
|
||||
log "*** Successfully scheduled *** $desc" 0
|
||||
system restartpending
|
||||
rsv commit
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
# Refresh a reservation
|
||||
proc ::schedchk::refresh {event desc} {
|
||||
if {$::opts(test)} {
|
||||
log "*** Test mode =refreshed *** $desc" 0
|
||||
return 1
|
||||
}
|
||||
set type 1
|
||||
if {[catch {
|
||||
$event clear_ulslot
|
||||
$event set_refresh
|
||||
$event insert
|
||||
} msg]} {
|
||||
log "+++ Error encountered while refreshing: $msg ++++ $desc" 0
|
||||
return 0
|
||||
} else {
|
||||
log "*** Successfully refreshed *** $desc" 0
|
||||
system restartpending
|
||||
rsv commit
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
# Cancel reservation
|
||||
proc ::schedchk::cancel {event desc} {
|
||||
if {$::opts(test)} {
|
||||
log "*** Test mode =cancelled *** $desc" 0
|
||||
return 1
|
||||
}
|
||||
set type 1
|
||||
if {[catch {
|
||||
$event clear_ulslot
|
||||
$event set_delete
|
||||
$event insert pending 0 1
|
||||
} msg]} {
|
||||
log "+++ Error encountered while cancelling: $msg ++++ $desc" 0
|
||||
return 0
|
||||
} else {
|
||||
log "*** Successfully cancelled *** $desc" 0
|
||||
system restartpending
|
||||
rsv commit
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
# Skip an episode
|
||||
proc ::schedchk::skip {event xservice xevent desc} {
|
||||
if {$::opts(test)} {
|
||||
log "*** Test mode =skipped *** $desc" 0
|
||||
return 1
|
||||
}
|
||||
if {[catch {
|
||||
$event apply_skip $xservice $xevent
|
||||
} msg]} {
|
||||
log "+++ Error encountered while skipping: $msg ++++ $desc" 0
|
||||
return 0
|
||||
} else {
|
||||
log "*** Successfully skipped *** $desc" 0
|
||||
system restartpending
|
||||
rsv commit
|
||||
return 1
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#----------------------------------------------------------------------------------------------------------
|
||||
# Start of mainline
|
||||
|
||||
set ::optlist ""
|
||||
set ::opt "-h"
|
||||
set ::debug 0
|
||||
set logfd stdout
|
||||
|
||||
# validate parameters
|
||||
::schedchk::checkopts $argv
|
||||
|
||||
# Open log file / use auto logfile
|
||||
if {[info exists ::auto::logfile]} {
|
||||
set logfile $::auto::logfile
|
||||
} else {
|
||||
set logfile "/mod/tmp/schedchk.log"
|
||||
}
|
||||
if {![info exists ::auto::logfd]} {
|
||||
if {$debug} {
|
||||
set logfd stdout
|
||||
puts "DEBUG ON level $::loglevel"
|
||||
} else {
|
||||
set logfd [open $logfile "a+"]
|
||||
}
|
||||
}
|
||||
|
||||
# process command
|
||||
switch -- $opt {
|
||||
-rsv { # "scan the recording schedule"
|
||||
::schedchk::rsvscan
|
||||
}
|
||||
-h -
|
||||
--help -
|
||||
default { # Help
|
||||
puts "schedchk = Check for recording schedule issues and attempt to fix them"
|
||||
puts " "
|
||||
puts "schedchk -h = produce this help"
|
||||
puts "schedchk -rsv = scan the recording schedule"
|
||||
|
||||
puts ""
|
||||
puts "Option Default (unless changed in Settings) "
|
||||
puts "-d 0 = produce detailed debug on Stdout (0, 1, 2) "
|
||||
puts "-test n = produce detailed debug on Stdout (0, 1, 2) "
|
||||
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue