1126 lines
33 KiB
Plaintext
Executable File
1126 lines
33 KiB
Plaintext
Executable File
#!/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 ts.class
|
|
|
|
|
|
proc log {msg {level 1}} {
|
|
if {$level > $::loglevel} return
|
|
puts $::logfd "[\
|
|
clock format [clock seconds] -format "%d/%m/%Y %H:%M:%S"\
|
|
] - SC $msg"
|
|
flush $::logfd
|
|
# Generate webif alerts for changes/errors
|
|
if {!$::opts(noalert) && $level == 0} {
|
|
system notify "SchedChk: $msg"
|
|
}
|
|
}
|
|
|
|
## let's mess with the internals - needs to be moved to rsv.class
|
|
#rsv method set {ivName val} {
|
|
# set $ivName $val
|
|
#}
|
|
#
|
|
#rsv method issplit {} {
|
|
# if {$ucRecKind == 2} { return 1 } else { return 0 }
|
|
#}
|
|
|
|
# Parse command options and apply defaults
|
|
proc checkopts {argv} {
|
|
global optarray
|
|
set settings [settings]
|
|
set parmerror 0
|
|
|
|
set ::autologlevel [$settings _nval_setting "autolog"]
|
|
set ::loglevel $::autologlevel
|
|
if {[info exists ::auto::logfd]} {
|
|
set logfd ::auto::logfd
|
|
}
|
|
|
|
# Read List of options with default values
|
|
source "/mod/webif/plugin/schedchk/optlist.jim"
|
|
# Override default from settings DB
|
|
foreach optl $optarray {
|
|
lassign $optl desc key defval helptxt
|
|
set key [string tolower $key]
|
|
if {$defval eq "n"} {set defval 0}
|
|
if {$defval eq "y"} {set defval 1}
|
|
set ::opts($key) [$settings _nval_setting "schedchk_$key"]
|
|
if {$::opts($key)==0} {set ::opts($key) $defval}
|
|
}
|
|
|
|
# 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 $::opts $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} { "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(d)} {
|
|
set ll $::loglevel
|
|
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
|
|
}
|
|
|
|
log "Current options" 1
|
|
foreach optl $optarray {
|
|
lassign $optl desc key default helptxt
|
|
log "[format "-%-11s %-4s" $key $::opts([string tolower $key])]" 1
|
|
}
|
|
}
|
|
|
|
|
|
proc svcmap {} {
|
|
# establish rsv <-> epg service_id mapping
|
|
global svcmap svcdef
|
|
set svcmap {}
|
|
set svcdef {}
|
|
set svcdef(0) 0
|
|
lmap i \
|
|
[$::channeldb query {select hSvc, usSvcid, eVideoType from TBL_SVC}] \
|
|
{
|
|
set svcmap([lindex $i 1]) [lindex $i 3]
|
|
set svcdef([lindex $i 1]) [lindex $i 5]
|
|
}
|
|
set svckeys [array names svcmap]
|
|
}
|
|
|
|
proc conflict-list {} {
|
|
global conflicts
|
|
if {$::opts(noconflict)} {
|
|
log "-noconflict Bypassing automatic conflict resolution" 2
|
|
set conflicts {}
|
|
return
|
|
}
|
|
|
|
set conflicts [rsv newconflicts [system tuners] "xlist"]
|
|
if {[llength $conflicts] > 1} {
|
|
log "++++ [llength $conflicts] Conflicts exist +++" 1
|
|
log "$conflicts" 3
|
|
}
|
|
}
|
|
|
|
proc rsvscan {} {
|
|
global svcmap conflicts svcdef now thresh slot name def
|
|
svcmap
|
|
conflict-list
|
|
set sttimer [clock seconds]
|
|
|
|
set resvs [rsv list]
|
|
|
|
# for each reservation
|
|
#
|
|
foreach resv $resvs {
|
|
# Ignore manual recordings & reminders
|
|
if {[$resv get ersvtype] != 3} { continue}
|
|
|
|
set name [$resv name]
|
|
set def $svcdef([$resv get hsvc])
|
|
set s [$resv start]
|
|
set ds "[clock format $s -format {%d/%m/%y %H:%M}]"
|
|
set d [$resv get nduration]
|
|
set e $($s + $d)
|
|
set slot [$resv get ulslot]
|
|
set lasttime $now
|
|
set dresv "$ds [clock format $d -format {%H:%M}] === slot $slot === $name === [$resv channel_name]"
|
|
lassign [$resv padded 1] sp ep
|
|
set rstatus [$resv status]
|
|
|
|
# Has event passed
|
|
if {$now > $e + $ep
|
|
&& $rstatus == "unknown" } {
|
|
set ended 1
|
|
incr num_ended
|
|
} else {
|
|
set ended 0
|
|
}
|
|
|
|
if {!$ended} {
|
|
log "Reservation - $dresv" 2
|
|
|
|
# Check whether active recording really is recording
|
|
if {$s - $sp < $now} {
|
|
chkActive $resv $dresv $rstatus
|
|
# don't check active recordings further
|
|
continue
|
|
}
|
|
|
|
# If split recording check for repeats
|
|
if {[$resv issplit]} {
|
|
if {[chkSplit $resv $dresv]} {continue}
|
|
}
|
|
|
|
# add any missing episodes
|
|
if {[$resv isseries]} {
|
|
miss_epsd $resv $dresv
|
|
set s [$resv start]
|
|
set d [$resv get nduration]
|
|
set e $($s + $d)
|
|
}
|
|
|
|
if {$s > $thresh} {
|
|
log "Start > Threshold - $dresv" 2
|
|
# check for new series in same time slot
|
|
if {[$resv isseries]} {
|
|
scrid_chg $resv $dresv $lasttime
|
|
}
|
|
continue
|
|
}
|
|
|
|
set elist [$resv aul]
|
|
set enum -1
|
|
set ecrids [split [string toupper [$resv get szEventToRecord]] "|"]
|
|
set ecrid [string toupper [$resv get szCRID]]
|
|
set ps $s
|
|
foreach epsd $elist {
|
|
lassign $epsd service_id start end event_id
|
|
if {$start < $ps} {
|
|
log "AUL not in time order - $dresv" 0
|
|
}
|
|
set ps $start
|
|
}
|
|
|
|
if {[llength $elist] > 0} {
|
|
lassign [lindex $elist 0] service_id start end event_id
|
|
|
|
# Check 1st aul entry matchs current resv details, should always match?
|
|
if {$service_id != [$resv get hsvc]} {
|
|
log "Service id Mismatch $service_id != [$resv get hsvc] = $dresv" 1
|
|
# what to do?
|
|
}
|
|
if {$start != $s} {
|
|
log "Start time id Mismatch $start != $s = $dresv" 1
|
|
# what to do?
|
|
}
|
|
if {$end != $e} {
|
|
log "End time Mismatch $end != $e = $dresv" 1
|
|
# what to do?
|
|
}
|
|
if {$event_id != [$resv get usevtid]} {
|
|
log "Event_id Mismatch $event_id != [$resv get usevtid] = $dresv" 1
|
|
# what to do?
|
|
}
|
|
|
|
#
|
|
# check each episode scheduled
|
|
#
|
|
foreach epsd $elist {
|
|
lassign $epsd service_id start end event_id
|
|
incr enum
|
|
set ecrid [lindex $ecrids $enum]
|
|
set epgcrid [string range $ecrid [string first "/" $ecrid] end]
|
|
# set ecrid [string range $ecrid 1 end]
|
|
set dur $($end-$start)
|
|
set lasttime $end
|
|
set deps "[clock format $start -format {%d/%m/%y %H:%M}] [clock format $dur -format {%H:%M}] === slot $slot === $name === [$resv channel_name]"
|
|
|
|
if {$start > $thresh} {
|
|
log "Start > Threshold - $deps" 2
|
|
break
|
|
}
|
|
chkEpisode $resv $epsd $ecrid
|
|
}
|
|
|
|
} else {
|
|
log "+++ No episodes scheduled in aulEventToRecordInfo +++ $dresv" 1
|
|
# use the event info in the main part of the reservation
|
|
if {![$resv isseries]} {
|
|
chkEpisode $resv [list [$resv get hsvc] $s $e [$resv get usevtid]] [$resv get szCRID]
|
|
}
|
|
}
|
|
} else {
|
|
log "Completed - $dresv" 2
|
|
}
|
|
|
|
# check for new series in same time slot
|
|
if {[$resv isseries]} {
|
|
scrid_chg $resv $dresv $lasttime
|
|
}
|
|
}
|
|
if {[llength $conflicts] > 1} {
|
|
log "++++ [llength $conflicts] Unresolved conflicts remain +++" 1
|
|
}
|
|
|
|
epg cleanup
|
|
set endtimer [clock seconds]
|
|
log "=== All done - run time [clock format $($endtimer - $sttimer) -format {%H:%M:%S}] ===" 1
|
|
}
|
|
#
|
|
# Should be recording now, check if it is
|
|
#
|
|
proc chkActive {resv dresv rstatus} {
|
|
global now svcmap svcdef
|
|
log "Already started? Status = $rstatus - $dresv" 1
|
|
if {$::opts(nonotrec)} {
|
|
log "-noNotRec - Not checking for failed recording" 2
|
|
return
|
|
}
|
|
if {$::opts(recwait)+[$resv start] > $now} {
|
|
log "-recWait - Still within wait interval" 2
|
|
return
|
|
}
|
|
set svc $svcmap([$resv get hsvc])
|
|
set event_id [$resv get usevtid]
|
|
# Retrieve epg record for the episode
|
|
set record [lindex [\
|
|
epg dbfetch dump -service $svc -event $event_id -sort ""] 0]
|
|
set epgname [$record get name]
|
|
set ok true
|
|
set file [find_file $resv $epgname]
|
|
|
|
# need to check whether it has actually started and schedule alternate if not recording
|
|
if {$file !=0} {
|
|
set ifsz [file size "$file.ts"]
|
|
set cfsz $ifsz
|
|
for {set i 0} {$i < 5} {incr i} {
|
|
sleep 1
|
|
set cfsz [file size "$file.ts"]
|
|
if {$cfsz > $ifsz} {break}
|
|
}
|
|
if {!($cfsz > $ifsz)} {
|
|
log "$file.ts size size $cfsz not growing - $dresv" 1
|
|
set ok false
|
|
} else {
|
|
log "Recording OK - $dresv" 1
|
|
return
|
|
}
|
|
}
|
|
|
|
set rstatus [$resv status]
|
|
switch -exact -- $rstatus {
|
|
"recording" {
|
|
set ok false
|
|
log "*** Status recording but Not recording *** - $dresv" 0
|
|
}
|
|
"ready" -
|
|
"unknown" -
|
|
"arwatch" {
|
|
set ok false
|
|
log "*** Recording not started *** - $dresv" 0
|
|
}
|
|
default {
|
|
log "*** Status $rstatus Unexpected *** - $dresv" 1
|
|
return
|
|
}
|
|
}
|
|
|
|
#Find Alternate
|
|
set epgcrid [$record get event_crid]
|
|
set def $svcdef([$record get channel_hsvc])
|
|
set start [$record get start]
|
|
set others [epg dbfetch dump -crid $epgcrid -sort "start" ]
|
|
set other [findAlternate $resv $others $start $def 1]
|
|
if {$other != ""} {
|
|
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 {%d/%m/%y %H:%M}] [clock format $odur -format {%H:%M}] === $oname === $ocname"
|
|
# attempt to schedule the alternate (always 1-off)
|
|
schedule $other $dother "Alternate for non-recording"
|
|
conflict-list
|
|
return
|
|
} else {
|
|
log "Unable to schedule alternate for non-recording programme -$dresv " 0
|
|
}
|
|
|
|
}
|
|
|
|
# Find file name matching recordings
|
|
# {There must be a better way of doing it than name matching)
|
|
proc find_file {resv name} {
|
|
set files [split [system nugget recordings] "\n"]
|
|
set nl [split $name {}]
|
|
foreach file $files {
|
|
set fl [split [file tail $file] {}]
|
|
lmap n $nl f $fl {
|
|
if {$n==$f} {continue}
|
|
if {$n == {}} {
|
|
#names matchs check channel and start times match
|
|
set ts [ts fetch "$file.ts"]
|
|
if {[$ts get channel_num] != [$resv get usLcn] } {break}
|
|
if {[$ts get schedstart] != [$resv start] } {break}
|
|
#puts "$name - $file match"
|
|
return $file
|
|
}
|
|
if {![string is alnum $n]} {continue}
|
|
break
|
|
}
|
|
|
|
}
|
|
return 0
|
|
}
|
|
|
|
#
|
|
# Split recording, check for repeats incorrectly scheduled
|
|
#
|
|
proc chkSplit {resv dresv} {
|
|
global svcmap conflicts svcdef now thresh slot name def
|
|
log "Split recording - $dresv" 2
|
|
if {$::opts(nosplit} {
|
|
log "-noSplit - Not checking for split recordings" 2
|
|
return 0
|
|
}
|
|
# need to check whether any portion is greater than 3hrs after previous
|
|
set elist [$resv aul]
|
|
set problems false
|
|
if {[llength $elist] <= 2} {return 0}
|
|
set enum -1
|
|
set pend [$resv end]
|
|
set pstart [$resv start]
|
|
set 3hrs $(3*60*60)
|
|
set epg {}
|
|
set first 0
|
|
set last 0
|
|
set ok true
|
|
foreach epsd $elist {
|
|
lassign $epsd service_id start end event_id
|
|
#puts "$start $end $pend $3hrs"
|
|
incr enum
|
|
if {$start > $pend+$3hrs } {
|
|
log "Split recording >3hrs gap - $dresv" 0
|
|
set problems true
|
|
if {$ok} {
|
|
break
|
|
}
|
|
# try the next repeat showing
|
|
set ok true
|
|
set first $enum
|
|
}
|
|
set last $enum
|
|
set pend $end
|
|
set svc $svcmap($service_id)
|
|
# Check if segment conflicts
|
|
if {"$slot$end" in $conflicts} {
|
|
set ok 0
|
|
}
|
|
# Retrieve epg record for the episode
|
|
set epg($enum) [lindex [\
|
|
epg dbfetch dump -service $svc -event $event_id -sort ""] 0]
|
|
if {$epg($enum) ==0} {
|
|
set ok 0
|
|
}
|
|
}
|
|
if {$problems} {
|
|
set crid "1[$resv get szCRID]#|"
|
|
set aul {}
|
|
set torec {}
|
|
if {$::opts(noinplace)} {
|
|
cancel $resv $dresv "Replace Split recording"
|
|
}
|
|
loop enum $first $($last+1) {
|
|
lappend aul [lindex $elist $enum]
|
|
set torec "$torec$crid"
|
|
if {$::opts(noinplace)} {
|
|
# Mark as Unscheduled
|
|
epg update_lookup_tab $service_id $event_id 0
|
|
# Reschedule as 1-off event
|
|
schedule $epg($enum) $dresv "Replace Split recording"
|
|
}
|
|
}
|
|
if {!$::opts(noinplace)} {
|
|
$resv set szEventToRecord $torec
|
|
$resv setaul [$resv buildaul $aul]
|
|
log "$aul $torec" 2
|
|
update_event $resv {} {} $dresv "Replace Split recording"
|
|
}
|
|
conflict-list
|
|
}
|
|
|
|
return $problems
|
|
}
|
|
|
|
#
|
|
# check each episode for changes and conflicts
|
|
#
|
|
proc chkEpisode {resv epsd ecrid} {
|
|
global svcmap conflicts svcdef now thresh slot name def
|
|
lassign $epsd service_id start end event_id
|
|
set epgcrid [string range $ecrid [string first "/" $ecrid] end]
|
|
set ecrid [string range $ecrid 1 end]
|
|
set dur $($end-$start)
|
|
set deps "[clock format $start -format {%d/%m/%y %H:%M}] [clock format $dur -format {%H:%M}] === slot $slot === $name === [$resv channel_name]"
|
|
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 $ecrid $epgcrid" 3
|
|
|
|
if {$record==""} {
|
|
#
|
|
# No epg rcord found for the episode - find alternate showing
|
|
#
|
|
log "+++ No matching epg entry ++++ $deps" 1
|
|
if {$::opts(nomissepg)} {
|
|
log "-noMissEPG - Not checking for alternate" 2
|
|
return
|
|
}
|
|
|
|
# look for an alternate showing
|
|
set others [epg dbfetch dump -crid $epgcrid -sort "start" ]
|
|
|
|
# Check what is currently scheduled for that time
|
|
# If nothing, hole in epg data, dont look to reschedule
|
|
set showingnow [epg dbfetch dump -service $svc -time "$(($start+$end)/2)" -sort "start" -debug 0 ]
|
|
if {$showingnow == {}} {
|
|
log "+++ Nothing in epg forhis time +++ $deps" 1
|
|
} else {
|
|
foreach snow $showingnow {
|
|
set sname [$snow get name]
|
|
log "Now scheduled: $sname " 1
|
|
if {[string first [strip_new $name] $sname] >= 0
|
|
&& ![$snow scheduled]} {
|
|
ladd others $snow
|
|
}
|
|
}
|
|
|
|
#puts $others
|
|
set other [findAlternate $resv $others $start $def 1]
|
|
if {$other != ""} {
|
|
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 {%d/%m/%y %H:%M}] [clock format $odur -format {%H:%M}] === $oname === $ocname"
|
|
# attempt to schedule the alternate (inplace)
|
|
if {!$::opts(noinplace)} {
|
|
if {[$other scheduled]} {
|
|
log "Alternate already scheduled "$deps--->$dother" 1
|
|
set other {}
|
|
}
|
|
update_event $resv $other $epsd "$deps--->$dother" "Event Changed"
|
|
conflict-list
|
|
return
|
|
}
|
|
# attempt to schedule the alternate (new event+skip)
|
|
if {[schedule $other $dother "Event Changed"]} {
|
|
if {![$resv isseries]} {
|
|
# delete single recording
|
|
cancel $resv $deps "Event Changed"
|
|
} else {
|
|
# move crid from scheduled to recorded
|
|
replaceskip $resv $ecrid $deps "Event Changed"
|
|
#refresh $resv $deps
|
|
}
|
|
conflict-list
|
|
return
|
|
}
|
|
} else {
|
|
# is it a duplicate entry for the sametime and channel?
|
|
foreach aul [$resv aul] {
|
|
if {$epsd == $aul} {continue}
|
|
lassign $aul aservice_id astart aend aevent_id
|
|
if {$service_id == $aservice_id
|
|
&& $start == $astart} {
|
|
update_event $resv 0 $epsd "$deps" "Duplicate Event"
|
|
conflict-list
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
} else {
|
|
#
|
|
# Epg entry for the episode found
|
|
#
|
|
|
|
# Check that details match
|
|
|
|
set epgname [$record get name]
|
|
set def $svcdef([$record get channel_hsvc])
|
|
set deps "[clock format $start -format {%d/%m/%y %H:%M}] [clock format $dur -format {%H:%M}] === slot $slot === $epgname === [$record get channel_name]"
|
|
|
|
log "Episode $deps" 2
|
|
set ok 1
|
|
set reason "no reason"
|
|
if {$start != [$record get start]} {
|
|
set ok 0
|
|
set reason "Start Mismatch: $start != [$record get start]"
|
|
log "+++ $reason +++ $deps" 1
|
|
}
|
|
if {$dur != [$record get duration]} {
|
|
set ok 0
|
|
set reason "Duration Mismatch: $dur != [$record get duration]"
|
|
log "+++ $reason +++ $deps" 1
|
|
}
|
|
set evt_crid [string toupper [$record get event_crid]]
|
|
if {[$resv issplit]} {
|
|
set cl [string first "#" $evt_crid]
|
|
} else {
|
|
set cl [string length $evt_crid]
|
|
}
|
|
if {![string equal -length $cl $epgcrid $evt_crid]
|
|
&& $epgcrid != ""} {
|
|
set ok 0
|
|
set reason "Crid Mismatch: $epgcrid != $evt_crid"
|
|
log "+++ $reason +++ $deps" 1
|
|
}
|
|
if {!$ok && !$::opts(noepgchg)} {
|
|
if {!$::opts(noinplace)} {
|
|
update_event $resv $record $epsd $deps $reason
|
|
} else {
|
|
refresh $resv $deps $reason
|
|
}
|
|
conflict-list
|
|
}
|
|
|
|
# Check for conflicts ($conflicts is null if checking supressed)
|
|
|
|
if {"$slot$end" in $conflicts} {
|
|
set ok 0
|
|
log "+++ Confict exists +++ $deps" 1
|
|
if {[$resv issplit]} {
|
|
# dont conflict check split recordings
|
|
# (might end up with 2 first half or parts out of order)
|
|
# also cant find alternates with different #IMI
|
|
return
|
|
}
|
|
#set others [$record othertimes]
|
|
set others [epg dbfetch dump -crid $epgcrid -nocase 1 -sort "start"]
|
|
set other [findAlternate $resv $others $start $def 0]
|
|
if {$other != ""} {
|
|
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 {%d/%m/%y %H:%M}] [clock format $odur -format {%H:%M}] === $oname === $ocname"
|
|
|
|
# attempt to schedule the alternate
|
|
if {!$::opts(noinplace)} {
|
|
if {[$other scheduled]} {
|
|
log "Alternate already scheduled "$deps--->$dother" 1
|
|
set other {}
|
|
}
|
|
update_event $resv $other $epsd "$deps--->$dother" "Confict resolution"
|
|
conflict-list
|
|
continue
|
|
}
|
|
if {[schedule $other $dother "Confict resolution"]} {
|
|
if {![$resv isseries]} {
|
|
# delete single recording
|
|
cancel $resv $deps "Conflict resolution"
|
|
} else {
|
|
# skip conflicted epidode
|
|
skip $resv $svc $event_id $deps "Conflict resolution"
|
|
}
|
|
conflict-list
|
|
return
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
# Scan others for a suitable alternate episode
|
|
proc findAlternate {resv others start definition sametime} {
|
|
# look for alternate with same definition HD/SD/radio
|
|
set other [scanAlternates $resv $others $start $definition $sametime 1]
|
|
if {$other == ""} {
|
|
# scan again wihout requiring matching definition
|
|
set other [scanAlternates $resv $others $start $definition $sametime 0]
|
|
}
|
|
return $other
|
|
}
|
|
|
|
# Scan others for a suitable alternate episode
|
|
proc scanAlternates {resv others start definition sametime samedef} {
|
|
global svcdef
|
|
|
|
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 odef $svcdef([$other get channel_hsvc])
|
|
set dother "[clock format $ostart -format {%d/%m/%y %H:%M}] [clock format $odur -format {%H:%M}] === $oname === $ocname"
|
|
# same start time not suitable match for confilcts
|
|
if {$ostart == $start && !$sametime} {continue}
|
|
if {$ostart <= [clock seconds]} {continue}
|
|
# should favour same definition
|
|
if {$odef != $definition && $samedef} {continue}
|
|
log "Alternate Episode $dother" 2
|
|
set sched [$other scheduled]
|
|
if {$sched >2} {
|
|
log "Already skipped - $dother" 2
|
|
continue
|
|
}
|
|
set oconflicts [rsv checkconflict \
|
|
$ostart $odur \
|
|
[system tuners]]
|
|
# Should ignore conflicts with self
|
|
set cl [llength $oconflicts]
|
|
if {"$oname" in $oconflicts} {incr cl -1}
|
|
if {$cl >= [system tuners]} {
|
|
# Alternate has Conflicts
|
|
log "Alternate conflicts $oconflicts - $dother" 2
|
|
continue
|
|
}
|
|
return $other
|
|
}
|
|
return ""
|
|
}
|
|
|
|
# Strip new: prefix from name prior to search
|
|
proc strip_new {name} {
|
|
foreach pfx {"new:" "new series." "new "} {
|
|
if {[string first $pfx [string tolower $name]] == 0} {
|
|
set name [string range $name [string length $pfx] end]
|
|
break
|
|
}
|
|
}
|
|
return [string trim $name]
|
|
}
|
|
|
|
# Check for prossible changes to series CRID
|
|
# Unscheduled series at same time/channel as an existing Series
|
|
# Also one-off programmes matching series name
|
|
proc scrid_chg {resv desc lasttime} {
|
|
global thresh svcmap now
|
|
set name [$resv name]
|
|
set fudge 60
|
|
if {$::opts(noscrid)
|
|
&& $::opts(no1off) } {
|
|
log "-noSCRID/-no1off - Not checking for SCRID change/ one-off recordings" 2
|
|
return
|
|
}
|
|
set scrid [$resv get szCRID]
|
|
set svc $svcmap([$resv get hsvc])
|
|
set start [$resv start]
|
|
set timeslot [clock format $start -format "%H:%M"]
|
|
#if {$lasttime < $now} {set lasttime $now}
|
|
set lasttime $now
|
|
|
|
set others [epg dbfetch dump -service $svc -term [strip_new $name] -after $lasttime -before $thresh -sort "start" -debug 0]
|
|
foreach other $others {
|
|
|
|
set ostart [$other get start]
|
|
set ostarttime [clock format $ostart -format "%H:%M"]
|
|
set odur [$other get duration]
|
|
set oend $($ostart+$odur)
|
|
set oname [$other get name]
|
|
set ocname [$other get channel_name]
|
|
set occrid [$other get channel_crid]
|
|
set oscrid [$other get series_crid]
|
|
set oecrid [$other get event_crid]
|
|
set dother "[clock format $ostart -format {%d/%m/%y %H:%M}] [clock format $odur -format {%H:%M}] === $oname === $ocname"
|
|
log "Potential event - $dother" 2
|
|
if {$ostart <= $now} {
|
|
log "Already started - $dother" 2
|
|
continue
|
|
}
|
|
if {$oscrid !=""} {
|
|
# Event is part of a series, check start time matches
|
|
if {$ostarttime == $timeslot} {
|
|
if {$::opts(noscrid)} {
|
|
log "-noSCRID - Not applying SCRID change" 2
|
|
continue
|
|
}
|
|
if {[string equal -nocase "$occrid$oscrid" $scrid]} {
|
|
# found episode of current series
|
|
log "Current series - $dother" 1
|
|
continue
|
|
}
|
|
if {[$other scheduled]} {
|
|
log "Already scheduled - $dother" 2
|
|
continue
|
|
}
|
|
set oconflicts [rsv checkconflict \
|
|
$ostart $odur \
|
|
[system tuners]]
|
|
set cl [llength $oconflicts]
|
|
if {$cl >= [system tuners]} {
|
|
log "Potential New series conflicts $oconflicts - $dother " 1
|
|
continue
|
|
}
|
|
log "Found new series matching $name - $dother - $oscrid" 1
|
|
|
|
# szCrid not currently updated by rsvsync so cant update inplace
|
|
if {false && !$::opts(noinplace)} {
|
|
$resv set szCRID [string toupper "[$other get channel_crid]$oscrid"]
|
|
puts [$resv get szCRID]
|
|
set aul1 [lindex [$resv aul] 0]
|
|
# only remove 1st AUL entry if it is for an expired event
|
|
lassign $aul1 service_id start end event_id
|
|
if {$end > $now} {set aul1 {}}
|
|
update_event $resv $other $aul1 "$desc-->$dother" "sCRID changed"
|
|
} else {
|
|
schedule $other "$dother" "sCRID changed" 2
|
|
# only schedule 1st episode, allow humax to add others
|
|
break
|
|
}
|
|
}
|
|
} else {
|
|
# Single event, but matches series name
|
|
if {$::opts(no1off) } {
|
|
log "-no1off - Not scheduling one-off recordings" 2
|
|
continue
|
|
}
|
|
if {[$other scheduled]} {
|
|
log "Potential 1-off already scheduled - $dother" 2
|
|
continue
|
|
}
|
|
|
|
set oconflicts [rsv checkconflict \
|
|
$ostart $odur \
|
|
[system tuners]]
|
|
set cl [llength $oconflicts]
|
|
if {$cl >= [system tuners]} {
|
|
log "Potential 1-off conflicts $oconflicts - $dother" 1
|
|
continue
|
|
}
|
|
log "Found 1-off matching $name - $dother" 1
|
|
|
|
if {!$::opts(noinplace)} {
|
|
update_event $resv $other {} "$desc-->$dother" "1-off recording"
|
|
} else {
|
|
schedule $other "$desc-->$dother" "1-off recording" 1
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
# Check for missing episoder of a series CRID
|
|
proc miss_epsd {resv desc} {
|
|
if {$::opts(nounsched) } {
|
|
log "-noUnsched - Not checking for missing episodes" 2
|
|
return
|
|
}
|
|
global thresh svcmap now
|
|
set name [$resv name]
|
|
|
|
set svc $svcmap([$resv get hsvc])
|
|
set start [$resv start]
|
|
set timeslot [clock format $start -format "%H:%M"]
|
|
set ecrid [$resv get szCRID]
|
|
set scrid [string range $ecrid [string first "/" $ecrid] end]
|
|
|
|
set others [epg dbfetch dump -after $now -before $thresh -scrid $scrid -nocase 1 -sort "start"]
|
|
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 ocrid [string toupper [$other get event_crid]]
|
|
|
|
set dother "[clock format $ostart -format {%d/%m/%y %H:%M}] [clock format $odur -format {%H:%M}] === $oname === $ocname"
|
|
|
|
if {[string first $ocrid [$resv get szEventToRecord]] >= 0} {
|
|
log "Already scheduled episode $desc-->$dother" 2
|
|
continue
|
|
}
|
|
if {[string first $ocrid [$resv get szRecordedProgCrid]] >= 0} {
|
|
log "Already recorded/skipped episode $desc-->$dother" 2
|
|
continue
|
|
}
|
|
set skiplist [$resv skiplist]
|
|
set ohsvc [$other get channel_hsvc]
|
|
set oevtid [$other get event_id]
|
|
log "svc $ohsvc evt $oevtid crid $ocrid Skiplist $skiplist" 3
|
|
if {[string first "$ohsvc:$oevtid" $skiplist] >= 0} {
|
|
log "Already skipped episode $desc-->$dother" 2
|
|
continue
|
|
}
|
|
if {[$other scheduled]} {
|
|
log "Already scheduled elsewhere $desc-->$dother"
|
|
continue
|
|
}
|
|
|
|
log "Found Unscheduled episode $ocrid $desc-->$dother" 1
|
|
update_event $resv $other {} "$desc-->$dother" "Unscheduled episode"
|
|
}
|
|
}
|
|
|
|
# Create a new reservation
|
|
proc schedule {event desc {reason ""} {type 1} } {
|
|
global svcmap
|
|
if {[$event scheduled]} {
|
|
log "*** Already scheduled *** $desc === $reason" 0
|
|
return 1
|
|
}
|
|
# mark new event as scheduled
|
|
set service_id [$event get channel_hsvc]
|
|
set event_id [$event get event_id]
|
|
epg update_lookup_tab [$event get service_id] $event_id 1
|
|
epg update_lookup_tab $service_id $event_id 1
|
|
|
|
if {$::opts(test)} {
|
|
log "*** Test mode =scheduled *** $desc === $reason" 0
|
|
return 1
|
|
}
|
|
set r [rsv construct $event $type]
|
|
if {[$r issplit]} {
|
|
# turn split rcording into 1off by removing #imi from url
|
|
#$r set ucRecKind 1
|
|
$r set szEventToRecord "1[$r get szCRID]|"
|
|
}
|
|
if {[catch {$r insert pending} msg]} {
|
|
log "+++ Error while scheduling: $msg ++++ $desc === $reason" 0
|
|
return 0
|
|
} else {
|
|
log "*** scheduled *** $desc === $reason" 0
|
|
system restartpending
|
|
rsv commit
|
|
return 1
|
|
}
|
|
}
|
|
|
|
# Refresh a reservation
|
|
proc refresh {resv desc {reason ""}} {
|
|
if {$::opts(test)} {
|
|
log "*** Test mode =refreshed *** $desc === $reason" 0
|
|
return 1
|
|
}
|
|
set type 1
|
|
if {[catch {
|
|
$resv clear_ulslot
|
|
$resv set_refresh
|
|
$resv insert
|
|
} msg]} {
|
|
log "+++ Error while refreshing: $msg ++++ $desc === $reason" 0
|
|
return 0
|
|
} else {
|
|
log "*** refreshed *** $desc === $reason" 0
|
|
system restartpending
|
|
rsv commit
|
|
return 1
|
|
}
|
|
}
|
|
|
|
# Cancel reservation
|
|
proc cancel {resv desc {reason ""}} {
|
|
global svcmap
|
|
# mark event as not scheduled
|
|
set service_id [$resv get hsvc]
|
|
set event_id [$resv get usevtid]
|
|
epg update_lookup_tab $service_id $event_id 0
|
|
epg update_lookup_tab $svcmap($service_id) $event_id 0
|
|
|
|
if {$::opts(test)} {
|
|
log "*** Test mode =cancelled *** $desc === $reason" 0
|
|
return 1
|
|
}
|
|
set type 1
|
|
if {[catch {
|
|
$resv clear_ulslot
|
|
$resv set_delete
|
|
$resv insert pending 0 1
|
|
} msg]} {
|
|
log "+++ Error while cancelling: $msg ++++ $desc === $reason" 0
|
|
return 0
|
|
} else {
|
|
log "*** cancelled *** $desc === $reason" 0
|
|
system restartpending
|
|
rsv commit
|
|
return 1
|
|
}
|
|
}
|
|
|
|
# Skip an episode - with apply_skip
|
|
proc skip {resv xservice xevent desc {reason ""}} {
|
|
if {$::opts(test)} {
|
|
log "*** Test mode =skipped *** $desc === $reason" 0
|
|
return 1
|
|
}
|
|
if {[catch {
|
|
$resv apply_skip $xservice $xevent
|
|
} msg]} {
|
|
log "+++ Error while skipping: $msg ++++ $desc === $reason" 0
|
|
return 0
|
|
} else {
|
|
log "*** skipped *** $desc === $reason" 0
|
|
system restartpending
|
|
rsv commit
|
|
return 1
|
|
}
|
|
}
|
|
|
|
# Skip an episode - with replace_skip
|
|
proc replaceskip {resv skipcrid desc {reason ""}} {
|
|
if {$::opts(test)} {
|
|
log "*** Test mode =skipped *** $desc === $reason" 0
|
|
return 1
|
|
}
|
|
if {[catch {
|
|
$resv clear_ulslot
|
|
$resv replace_skip [list $skipcrid]
|
|
$resv insert
|
|
} msg]} {
|
|
log "+++ Error while skipping: $msg ++++ $desc === $reason" 0
|
|
return 0
|
|
} else {
|
|
log "*** skipped *** $desc === $reason" 0
|
|
system restartpending
|
|
rsv commit
|
|
return 1
|
|
}
|
|
}
|
|
|
|
|
|
# Update reservation with new event details
|
|
proc update_event {resv other origaul desc {reason ""}} {
|
|
if {$origaul != {}} {
|
|
# mark event not scheduled
|
|
lassign $origaul service_id start end event_id
|
|
epg update_lookup_tab $service_id $event_id 0
|
|
}
|
|
if {$other != {}} {
|
|
# mark new event as scheduled
|
|
set service_id [$other get channel_hsvc]
|
|
set event_id [$other get event_id]
|
|
epg update_lookup_tab $service_id $event_id 1
|
|
}
|
|
if {$::opts(test)} {
|
|
log "*** Test mode =Updated *** $desc === $reason" 0
|
|
return 1
|
|
}
|
|
$resv update_aul $other $origaul
|
|
|
|
# update header with next event
|
|
$resv set_next_event
|
|
log "$desc - [$resv aul]-[$resv get ucVolume] " 3
|
|
if {[catch {
|
|
$resv insert
|
|
} msg]} {
|
|
log "+++ Error while updating: $msg ++++ $desc === $reason" 0
|
|
return 0
|
|
} else {
|
|
log "*** Updated *** $desc === $reason" 0
|
|
system restartpending
|
|
# Ensure our copy matches the updated
|
|
$resv reset_next_event
|
|
}
|
|
}
|
|
|
|
#----------------------------------------------------------------------------------------------------------
|
|
# Start of mainline
|
|
|
|
set ::optlist ""
|
|
set ::opt "-h"
|
|
set ::debug 0
|
|
global optarray
|
|
|
|
set logfd stdout
|
|
if {[info exists ::auto::logfd]} {
|
|
set logfd $::auto::logfd
|
|
}
|
|
|
|
# validate parameters
|
|
checkopts $argv
|
|
|
|
set now [clock seconds]
|
|
if {$::opts(thresh)} {
|
|
set thresh $($now + ($::opts(thresh)*60*60))
|
|
} else {
|
|
set thresh $($now + (9*24*60*60))
|
|
}
|
|
log "Threshold $::opts(thresh) = $thresh [clock format $thresh -format {%d/%m/%Y %H:%M:%S}]" 2
|
|
|
|
# process command
|
|
switch -- $opt {
|
|
-rsv { # "scan the recording schedule"
|
|
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 "Options Default (unless changed in Settings) "
|
|
foreach optl $optarray {
|
|
lassign $optl desc key default helptxt
|
|
puts [format "-%-11s %-4s = %s" $key $default $helptxt]
|
|
}
|
|
puts "\n Note: Options are not case sensitive"
|
|
}
|
|
|
|
}
|