# TivoWebPlus ## index.itcl -- init_channelindex() modified by SteveT ## index.itcl -- init_channelindex() modified by MelvinPurvis ## added and maintained global init_logoindex_complete to init_logoindex so that ## its initialization state is unambiguous (resolves double initialization from ## init_channelindex) ## changed the main per-channel loop to allow early exit from channel processing when ## the current channel falls in a culled group proc get_recordindexsearch {indexfsid startoffset maxoffset recsize incrsize searchkey searchtype matchsize} { set match 0 for {set offset $startoffset} {$offset < $maxoffset} {incr offset $incrsize} { set end $incrsize if { $maxoffset - $offset < $incrsize } { set end [expr $maxoffset - $offset] } RetryTransaction { set indexdata [mfs getpart $indexfsid $offset $end] } set slen [expr $end / $recsize] set top $slen set current [expr $top / 2] set bottom 0 set count 0 while { $top >= $bottom } { set data [string range $indexdata [expr $current * $recsize] [expr $current * $recsize + $matchsize]] #binary scan $data H8 fsidnum #puts "$searchtype $offset $top $bottom $current [expr 0x$fsidnum]" set compare [string compare $data $searchkey] if {$compare == 0 || ($searchtype == 0 && $compare == 1)} { if { $current <= 0 } { set match 1 break } set data2 [string range $indexdata [expr ($current-1) * $recsize] [expr ($current-1) * $recsize + $matchsize]] if {[string compare $data2 $searchkey] == -1} { set match 1 break } else { set top [expr $current - 1] } } elseif {$compare == -1} { set bottom [expr $current + 1] } else { set top [expr $current - 1] } set current [expr $bottom + ($top - $bottom) / 2] incr count if { $count > $slen } { error "Error: infinite loop in binary search ($bottom $top $current $slen)" } } if { $match == 1} { if {$searchtype == 2} { set data [string range $indexdata [expr $current * $recsize + $matchsize + 1] [expr ($current+1) * $recsize - 1]] return $data } else { return [expr ($offset-$startoffset)/$recsize + $current] } } } if {$searchtype == 2} { return "" } else { return -1 } } proc get_programshowings {progfsid watch args} { global db global guideindexdir global programindexstartday global channeltablefsid global channeltableindex global channeltableindex_alt global version set nowsecs [clock seconds] if {[llength $args] > 0} { set genres [lindex $args 0] } else { set genres "" } set reload_index 0 RetryTransaction { set channelfsid [lindex [mfs find $guideindexdir/ChannelTable] 0] if { ![info exists channeltablefsid] || $channeltablefsid != $channelfsid } { set reload_index 1 } if {$genres != ""} { set showingkeyfsid [lindex [mfs find $guideindexdir/Showing.key] 0] set showingindex [lindex [mfs find $guideindexdir/Showing.index] 0] set showingkeydata [mfs getpart $showingkeyfsid 0x0 0x18] } set programkeyfsid [lindex [mfs find $guideindexdir/Program.key] 0] set programindex [lindex [mfs find $guideindexdir/Program.index] 0] set programkeydata [mfs getpart $programkeyfsid 0x0 0x18] } binary scan [string range $programkeydata 0x00 0x03] I recsize binary scan [string range $programkeydata 0x04 0x07] I totalnumkeys binary scan [string range $programkeydata 0x08 0x0B] I incrsize binary scan [string range $programkeydata 0x10 0x13] I numkeys binary scan [string range $programkeydata 0x14 0x17] I programindexstartday if {$genres != ""} { binary scan [string range $showingkeydata 0x00 0x03] I showingrecsize binary scan [string range $showingkeydata 0x04 0x07] I showingtotalnumkeys binary scan [string range $showingkeydata 0x08 0x0B] I showingincrsize binary scan [string range $showingkeydata 0x10 0x13] I showingnumkeys } if { $reload_index } { init_channelindex init_genreindex init_ns_cache } set fsidbin [binary format I $progfsid] set startoffset 24 set maxoffset [expr $startoffset + $recsize * $numkeys] set first [get_recordindexsearch $programkeyfsid $startoffset $maxoffset $recsize $incrsize $fsidbin 0 3] if { $first == -1 } { # This error will occur when viewing upcoming showings # for manual recordings as well as multipart merges. return "" #error "Error couldn't find starting offset in program key" } set startoffset [expr $first * $incrsize] set maxoffset [expr $startoffset + $incrsize] if { $maxoffset > $recsize * $totalnumkeys } { set maxoffset [expr $recsize * $totalnumkeys] } set current [get_recordindexsearch $programindex $startoffset $maxoffset $recsize $incrsize $fsidbin 1 3] set showings "" if {$current >= 0} { set i [expr ($current-1) * $recsize] set compare 0 set offset $startoffset set init 1 set prevstation "" set prevsecs "" while { $compare == 0 } { incr i $recsize if { $init == 1 || ($i >= $end && $compare == 0) } { if { $init == 0 } { set i 0 incr offset $incrsize } set end $incrsize if { $offset + $end > $recsize * $totalnumkeys } { set end [expr $recsize * $totalnumkeys - $offset] } RetryTransaction { set programdata [mfs getpart $programindex $offset $end] } set init 0 } set data [string range $programdata $i [expr $i + $recsize - 1]] set fsiddata [string range $data 0 3] set compare [string compare $fsiddata $fsidbin] if {$compare == 0} { binary scan $data x4H8 showingdata if {$::version >= 3} { set channel [expr 0x$showingdata & 0x07FF] } else { set channel [expr 0x$showingdata & 0x03FF] } if {[catch {set station $channeltableindex($channel)}] && [catch {set station $channeltableindex_alt($channel)}]} { continue } if {$::version >= 3} { set time [expr 0x$showingdata >> 11] } else { set time [expr 0x$showingdata >> 10] } set seconds [expr $programindexstartday * 86400 + $time] if { $seconds < $nowsecs } { continue } if {$seconds == $prevsecs && $prevstation == $station} { continue } set prevstation $station set prevsecs $seconds if {$genres != ""} { set showingdata2 [string range $data 4 7] set startoffset 24 set maxoffset [expr $startoffset + $showingrecsize * $showingnumkeys] set first [get_recordindexsearch $showingkeyfsid $startoffset $maxoffset $showingrecsize $showingincrsize $showingdata2 0 3] if { $first == -1 } { error "Error couldn't find starting offset in showing key" } set startoffset [expr $first * $showingincrsize] set maxoffset [expr $startoffset + $showingincrsize] if { $maxoffset > $showingrecsize * $showingtotalnumkeys } { set maxoffset [expr $showingrecsize * $showingtotalnumkeys] } set sdata [get_recordindexsearch $showingindex $startoffset $maxoffset $showingrecsize $showingincrsize $showingdata2 2 3] binary scan $sdata H16 sdata if {$sdata != ""} { if {[genrematch $sdata $genres] == 0} { continue } } } lappend showings [list $seconds $station] # puts "$seconds $station" } elseif {$compare == 1} { break } else { error "Error: fsid is less that search param" } } } return $showings } proc tms_to_fsid {tmsid} { global guideindexdir global db RetryTransaction { set tmskeyfsid [lindex [mfs find $guideindexdir/Tms.key] 0] set tmsindex [lindex [mfs find $guideindexdir/Tms.index] 0] set tmskeydata [mfs getpart $tmskeyfsid 0x0 0x18] } binary scan [string range $tmskeydata 0x00 0x03] I recsize binary scan [string range $tmskeydata 0x04 0x07] I totalnumkeys binary scan [string range $tmskeydata 0x08 0x0B] I incrsize binary scan [string range $tmskeydata 0x10 0x13] I numkeys set prefix [string range $tmsid 0 1] set number [string range $tmsid 2 end] if [expr [string length $number] == 10] { set mn 0 if {[string compare "4294967296" $number] <= 0} { set numbers [bignum_divide $number "4294967296"] set n1 [lindex $numbers 0] set n2 [lindex $numbers 1] } } else { set mn 256 set n1 0 set n2 $number } regsub ^0+(.+) $n1 \\1 n1 regsub ^0+(.+) $n2 \\1 n2 set search [binary format a2SII $prefix $mn $n1 $n2] # look through the key-file to get a starting point set startoffset 24 set maxoffset [expr $startoffset + $recsize * $numkeys] set first [get_recordindexsearch $tmskeyfsid $startoffset $maxoffset $recsize $incrsize $search 0 11] if { $first == -1 } { error "Error couldn't find starting offset in tms key" } # next, go throught index-file to get the exact match set startoffset [expr $first * $incrsize] set maxoffset [expr $startoffset + $incrsize] if { $maxoffset > $recsize * $totalnumkeys } { set maxoffset [expr $recsize * $totalnumkeys] } set fdata [get_recordindexsearch $tmsindex $startoffset $maxoffset $recsize $incrsize $search 2 11] if {[string length $fdata] == 0} { return "" } binary scan $fdata I fsid return $fsid } proc bignum_subtract {a b} { set la [string length $a] set lb [string length $b] if {$la < $lb} { set formatstring [format "%%0%s%s" $lb "s"] set a [format $formatstring $a] set la $lb } else { set formatstring [format "%%0%s%s" $la "s"] set b [format $formatstring $b] set lb $la } set result "" set carry 0 for {set index [expr $lb-1]} {$index >= 0} {incr index -1} { set current [expr [string index $a $index] - [string index $b $index]] if {$carry == 1} { incr current -1 } if {$current < 0} { set carry 1 incr current 10 } else { set carry 0 } set result [format "%s%s" $current $result] } if {$carry == 1} { set result [format "-%s" $result] } return $result } # calculate dividend / divisor proc bignum_divide {dividend divisor} { set result 0 set temp $dividend while {[string index $temp 0] != "-"} { incr result 1 set prevtemp $temp set temp [bignum_subtract $temp $divisor] } return "[expr $result - 1] $prevtemp" } proc genrematch {genrebytes genres} { global genretable set last "" set glist "" set glen [string length $genrebytes] for {set i 0} {$i < $glen} {incr i 2} { set gval [expr 0x[string range $genrebytes $i [expr $i+1]]] if {$last == $gval} { break } set last $gval set gval [lindex $genretable $gval] lappend glist $gval } foreach genre $genres { if {$genre != 0} { if { !$::dtivo || $genre > 14 || ($genre == 1 || $genre == 5 || $genre == 8) } { if {[lsearch $glist $genre] < 0} { return 0 } } } } return 1 } proc init_genreindex {} { global db global guideindexdir global genretable set genretable "" if {![MfsFileExists $guideindexdir/GenreTable]} { return } RetryTransaction { set genretablefsid [lindex [mfs find $guideindexdir/GenreTable] 0] set genretabledata [mfs get $genretablefsid] } binary scan [string range $genretabledata 0x4 0x7] I numkeys #set numkeys [expr ([string length $genretabledata] - 0x8) / 0x30] for {set i 0} {$i < $numkeys} {incr i} { binary scan [string range $genretabledata [expr $i*0x30 + 0x8] [expr $i*0x30 + 0xB]] I val lappend genretable $val } } proc init_logoindex {} { global db global logotableindex global logotablename global init_logoindex_complete foreach logotype {1 2} { set logotableindex($logotype) "" set logotablelogo($logotype) "" set logotablename($logotype) "" } set LogoGroups "" ForeachMfsFile fsid name type "/LogoGroup" "" { lappend LogoGroups $fsid } #fill logotablelogo and logotableindex foreach fsid $LogoGroups { RetryTransaction { set LogoGroupEntry [db $db openid $fsid] set LogoGroupSpace [dbobj $LogoGroupEntry get LogoSpace] set LogoGroupSize [dbobj $LogoGroupEntry get Size] set LogoGroupImages [dbobj $LogoGroupEntry gettarget Image] set LogoGroupIndexes [dbobj $LogoGroupEntry get Index] foreach targ $LogoGroupImages ndx $LogoGroupIndexes { set EncodedIndex [expr ($LogoGroupSpace << 16) | ($ndx & 0xffff)] lappend logotableindex($LogoGroupSize) $EncodedIndex lappend logotablelogo($LogoGroupSize) $targ } } } #fill the logotablename foreach logotype {1 2} imgsuffix {"-s1-p1" "-s2-p2"} { for {set x 0} {$x < [llength $logotablelogo($logotype)]} {incr x 50} { RetryTransaction { foreach fsid [lrange $logotablelogo($logotype) $x [expr $x+49]] { regexp {([0-9]*)/(.*)} $fsid junk id subid set logo [db $db openidconstruction $id $subid] set name [dbobj $logo get Name] regsub -- "$imgsuffix\$" $name {} name lappend logotablename($logotype) $name } } } } set init_logoindex_complete 1 } proc init_channelindex {} { global db global guideindexdir global channeltablefsid global channeltablestation global channeltableapgchannel global channeltablenum global channeltablestation_alt global channeltablenum_alt global channeltableindex global channeltableindex_alt global logotableindex global logotablename global init_logoindex_complete global hide_ppv global hide_not_received global hide_xm if {!$init_logoindex_complete && $logotableindex(1) == ""} { catch { puts "--init_channelindex forcing init_logoindex" } init_logoindex } if {![info exists hide_ppv]} { # Needed for Dailymail which uses this function without # these being set set hide_ppv 1 set hide_not_received 1 set hide_xm 1 } if {[info exists channeltablestation]} { unset channeltablestation unset channeltablenum } if {[info exists channeltablestation_alt]} { unset channeltablestation_alt unset channeltablenum_alt } if {[info exists channeltableindex]} { unset channeltableindex } if {[info exists channeltableindex_alt]} { unset channeltableindex_alt } if {[info exists channeltableapgchannel]} { unset channeltableapgchannel } if {[info exists channels]} { unset channels } if {[info exists signaltype]} { unset signaltype } set increment 20 # list copied from tvlib/tcl/tv/DbEnum.tcl # 0 1 2 3 4 5 6 7 set signaltypes [list Unknown Rooftop Cable DBS ATSC CableBox DirecTV LineInput ] # changed to remember SignalType -- SteveT RetryTransaction { set setup [db $db open /Setup] set sources [dbobj $setup get Source] foreach source $sources { set channels($source) [dbobj $source gettarget Channel] set signaltype($source) [dbobj $source get SignalType] } } #puts "channelcount = $channelcount" foreach source [array names channels] { set channelcount [llength $channels($source)] set signaltypedesc [lindex $signaltypes $signaltype($source) ] catch { puts "--caching $channelcount channels for source of type=$signaltype($source) ($signaltypedesc)" } set fullyProcessed 0 set amostFullyProcessed 0 for {set j 0} {$j < $channelcount} {incr j $increment} { set channels2 [lrange $channels($source) $j [expr $j + $increment - 1]] foreach channel $channels2 { regexp {([0-9]*)/(.*)} $channel junk id subid RetryTransaction { set channelobj [db $db openidconstruction $id $subid] set record [defaultval 1 [dbobj $channelobj get Record]] if {$hide_not_received > 0 && $record <= 0 } { continue } set station [dbobj $channelobj get Station] set callsign [dbobj $station get CallSign] if {[string first "LILS" $callsign] == 0} { continue } if {$hide_ppv > 0 && [string first "PPV" $callsign] == 0} { continue } set favorite [defaultval 0 [dbobj $channelobj get Favorite]] if {$favorite <= 0 && $hide_xm > 0 && [string first "XM" $callsign] == 0} { continue } set num [join [split [ReadableChannelNum [dbobj $channelobj get Number]] -] .] set stationfsid [dbobj $station fsid] set apgchannel "" set apgchannel [dbobj $channelobj get ApgChannel] ## just check for apgchannel, not dtivo -- SteveT if {$apgchannel == "" } { # set callsign [dbobj $station get CallSign] set stationname [dbobj $station get Name] set logoindex [dbobj $station get LogoIndex] set numberofstreams 1 } else { set apgchanneldef [dbobj $apgchannel get ChannelDefinition] ## NumberOfStreams is wrong on HD channels, so hard-code 1 if {[string match "*.*" $num]} { set numberofstreams 1 } else { set numberofstreams [dbobj $apgchanneldef get NumberOfStreams] if {$numberofstreams == 0} { continue } } set channeltableapgchannel($stationfsid) [dbobj $apgchannel fsid] # set callsign [dbobj $apgchannel get ShortName] set stationname [dbobj $apgchannel get LongName] set logoindex [dbobj $apgchannel get LogoIndex] } #catch { puts "--doing $callsign $num $stationname $logoindex $apgchannel $record $favorite" } } if {$numberofstreams > 0} { if { [string first "TIVO" $callsign] == 0} { incr num 1000 } if { $record > 0 } { incr fullyProcessed set channeltablenum($num) $stationfsid ## added $channel and signaltype to end of list in next line -- SteveT set channeltablestation($stationfsid) [list $num $favorite $callsign $stationname $logoindex $channel $signaltype($source)] } else { if { ![info exists channeltablenum($num)] && ![info exists channeltablenum_alt($num)] } { incr fullyProcessed set channeltablenum_alt($num) $stationfsid ## added $channel and signaltype to end of list in next line -- SteveT set channeltablestation_alt($stationfsid) [list $num $favorite $callsign $stationname $logoindex $channel $signaltype($source)] } } } } } catch { puts "--fully processed $fullyProcessed of $channelcount channels for source of type=$signaltype($source) ($signaltypedesc)" } } RetryTransaction { set channeltablefsid [lindex [mfs find $guideindexdir/ChannelTable] 0] set channeltabledata [mfs get $channeltablefsid] } binary scan [string range $channeltabledata 0 3] I numkeys for {set i 0} {$i < $numkeys} {incr i} { binary scan [string range $channeltabledata [expr $i*8 + 4] [expr $i*8 + 11]] ISS stationfsid dummy chnum if { [catch {set test $channeltablestation($stationfsid)}] == 0 } { set channeltableindex($i) $stationfsid } elseif { [catch {set test $channeltablestation_alt($stationfsid)}] == 0 } { set channeltableindex_alt($i) $stationfsid } } } proc init_ns_cache {} { global db global cache_ns_moddate global cache_ns_rec global cache_ns_series global nowshowingdir global version set cache_ns_rec "" set cache_ns_series "" set cache_ns_moddate "" if {$::version >= 3} { set nowshowingdir "/Recording/NowShowingByClassic" } else { set nowshowingdir "/Recording/NowShowing" } catch { RetryTransaction { set nsfsid [mfs find $nowshowingdir] set nsfsid [lindex $nsfsid 0] set cache_ns_moddate [mfs moddate $nsfsid] } } if {$cache_ns_moddate == ""} { return } ForeachMfsFileTrans fsid name type $nowshowingdir "" 20 { set rec [db $db openid $fsid] set showing [dbobj $rec get Showing] set program [dbobj $showing get Program] set seriesfsid [dbobj $program gettarget Series] regsub {/-1$} $seriesfsid {} seriesfsid #set series [dbobj $program get Series] if {$seriesfsid != ""} { # set seriesfsid [dbobj $series fsid] lappend cache_ns_rec $fsid lappend cache_ns_series $seriesfsid } } } proc init_sp_cache {} { global db global cache_sp_moddate global cache_sp_fsid global cache_sp_key global seasonpassdir set cache_sp_fsid "" set cache_sp_key "" set cache_sp_moddate "" catch { RetryTransaction { set spfsid [mfs find $seasonpassdir] set spfsid [lindex $spfsid 0] set cache_sp_moddate [mfs moddate $spfsid] } } if { $cache_sp_moddate == "" } { return } ForeachMfsFileTrans fsid name "tyDb" $seasonpassdir "" 20 { # puts "$fsid $name $type" set sp [db $db openid $fsid] set type [defaultval 1 [dbobj $sp get Type]] if { $type == 1 } { set series [dbobj $sp get Series] set seriesfsid [dbobj $series fsid] set station [dbobj $sp get Station] set stationfsid [dbobj $station fsid] lappend cache_sp_fsid $fsid lappend cache_sp_key "$seriesfsid|$stationfsid" } } } proc update_sp_cache {} { global db global cache_sp_moddate global cache_sp_fsid global cache_sp_key global seasonpassdir global init_logoindex_complete set spmoddate "" catch { RetryTransaction { set spfsid [mfs find $seasonpassdir] set spfsid [lindex $spfsid 0] set spmoddate [mfs moddate $spfsid] } } if {$spmoddate == "" || $spmoddate == $cache_sp_moddate} { return } set count 0 set newcache_sp_fsid $cache_sp_fsid set newcache_sp_key $cache_sp_key RetryTransaction { foreach fsid $cache_sp_fsid { if { [catch {db $db openid $fsid} sp] } { set newcache_sp_fsid [lreplace $newcache_sp_fsid $count $count] set newcache_sp_key [lreplace $newcache_sp_key $count $count] # puts "Removing $fsid from sp cache" } else { incr count } } } set cache_sp_fsid $newcache_sp_fsid set cache_sp_key $newcache_sp_key ForeachMfsFile fsid name type $seasonpassdir "" { if { [lsearch $cache_sp_fsid $fsid] == -1 } { RetryTransaction { set sp [db $db openid $fsid] set type [defaultval 1 [dbobj $sp get Type]] if { $type == 1 } { set series [dbobj $sp get Series] set seriesfsid [dbobj $series fsid] set station [dbobj $sp get Station] set stationfsid [dbobj $station fsid] lappend cache_sp_fsid $fsid lappend cache_sp_key "$seriesfsid|$stationfsid" # puts "Adding $fsid to sp cache" } } } } set cache_sp_moddate $spmoddate } if {$::version >= 6} { set guideindexdir "/GuideIndexV3" } elseif {[string match {3.5*} $::tivoswversion] == 1} { # New DTivo release (May 2006) set guideindexdir "/GuideIndexV3" } elseif {$::version >= 3} { set guideindexdir "/GuideIndexV2" } else { set guideindexdir "/GuideIndex" } if {$::version >= 3} { set seasonpassdir "/SeasonPass/User" } else { set seasonpassdir "/SeasonPass" } if { $reload == 0 || ![info exists init_logoindex_complete]} { set init_logoindex_complete 0 set count 1 set attempt "" while { ![info exists logotableindex] || ![info exists logotablename] || $count == 1 } { if { $count > 3 } { puts "-error initializing logo index; aborted" break } puts "-initializing logo index$attempt" incr count set attempt "; attempt $count" init_logoindex } set count 1 set attempt "" ## removed verification of channeltableapgchannel and *_alt since they won't exist on certain boxes -- SteveT while { ![info exists channeltablefsid] || ![info exists channeltablestation] || ![info exists channeltablenum] || ![info exists channeltableindex] || $count == 1 } { if { $count > 3 } { puts "-error initializing channel table; aborted" break } puts "-initializing channel table$attempt" incr count set attempt "; attempt $count" catch init_channelindex } set count 1 set attempt "" while { ![info exists genretable] || $count == 1 } { if { $count > 3 } { puts "-error initializing genre table; aborted" break } puts "-initializing genre table$attempt" incr count set attempt "; attempt $count" catch init_genreindex } set count 1 set attempt "" while { ![info exists cache_ns_moddate] || ![info exists cache_ns_rec] || ![info exists cache_ns_series] || $count == 1 } { if { $count > 3 } { puts "-error initializing now showing cache; aborted" break } puts "-initializing now showing cache$attempt" incr count set attempt "; attempt $count" catch init_ns_cache } set count 1 set attempt "" while { ![info exists cache_sp_moddate] || ![info exists cache_sp_fsid] || ![info exists cache_sp_key] || $count == 1 } { if { $count > 3 } { puts "-error initializing season pass cache; aborted" break } puts "-initializing season pass cache$attempt" incr count set attempt "; attempt $count" catch init_sp_cache } }