version 1 - handle conflicts and schedule changes

This commit is contained in:
Bob Buxton 2020-04-02 14:58:52 +01:00
commit d7b6477d68
2 changed files with 475 additions and 0 deletions

11
CONTROL/control Normal file
View File

@ -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

View File

@ -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) "
}
}