diff --git a/webif/html/browse/index.jim b/webif/html/browse/index.jim
index 587f67eb..8e6f27c7 100755
--- a/webif/html/browse/index.jim
+++ b/webif/html/browse/index.jim
@@ -215,8 +215,8 @@ proc entry {file} {{i 0}} {
# Indexed
set dlna 0
- if {$::dlnaok && $::model eq "HDR" &&
- [llength [system dlnaurl $file]]} {
+ if {$::dlnaok && $::model eq "HDR" && [llength [
+ system dlnaurl [file normalize $file]]]} {
icon "/img/dlna.png" "Indexed by DLNA Server"
set dlna 1
}
diff --git a/webif/lib/system.class b/webif/lib/system.class
index 9da71279..cb19d188 100644
--- a/webif/lib/system.class
+++ b/webif/lib/system.class
@@ -316,10 +316,6 @@ proc {system dlnadb} {} {
proc {system _dlnaurl} {file urlbase} {
set mime "video/mp2t"
- set nfile $file
- if {![catch {set nfile [file normalize $file]}]} {
- set file $nfile
- }
if {[catch {set db [sqlite3.open [system dlnadb]]}]} {
return {}
}
diff --git a/webif/lib/system.class.orig b/webif/lib/system.class.orig
new file mode 100644
index 00000000..9da71279
--- /dev/null
+++ b/webif/lib/system.class.orig
@@ -0,0 +1,921 @@
+
+source /mod/webif/lib/setup
+
+package require xconv
+
+if {![exists -command class]} { package require oo }
+if {![exists -command sqlite3.open]} { package require sqlite3 }
+if {![exists -command binary]} { package require binary }
+
+class system {}
+
+proc {system model} {} {{model ""}} {
+ if {$model ne ""} { return $model }
+ if {[catch {set fp [open /etc/model r]}]} {
+ set model {HD[R]}
+ } else {
+ set model [string trim [read $fp]]
+ close $fp
+ }
+ return $model
+}
+
+proc {system hostname} {} {
+ if {[catch {set hostname [string trim [exec hostname]]}]} {
+ set hostname "humax"
+ }
+ return $hostname
+}
+
+proc {system ip} {} {
+ if {[catch {set fp [open /etc/hosts r]}]} {
+ set ip "127.0.0.1"
+ } else {
+ set ipl [lindex [split [$fp read] "\n"] 1]
+ regsub -- {[[:space:]].*} $ipl "" ip
+ $fp close
+ }
+ return $ip
+}
+
+proc {system modversion} {{short 0}} {{modver ""}} {
+ if {$modver eq ""} {
+ if {[catch {set fp [open /etc/modversion r]}]} {
+ set modver "102"
+ } else {
+ set modver [string trim [read $fp]]
+ close $fp
+ }
+ }
+ if {$short} { return $modver }
+ lassign [split $modver ""] a b c
+ return [format "%d.%d%d" $a $b $c]
+}
+
+proc {system modbuild} {} {{modbuild ""}} {
+ if {$modbuild ne ""} { return $modbuild }
+ if {[catch {set modbuild [string trim [file read /etc/modbuild]]}]} {
+ set modbuild 0
+ }
+ return $modbuild
+}
+
+proc {system lastbootreason} {{descr 1}} {{lbr -1}} {
+ if {$lbr == -1} {
+ set lbr 0
+ if {[file readable /tmp/.lbr]} {
+ set lbr [file read /tmp/.lbr]
+ }
+ }
+ if {!$descr} { return $lbr }
+ switch $lbr {
+ 1 { return "Front panel button" }
+ 2 { return "Remote control handset" }
+ 3 { return "Scheduled event" }
+ 4 { return "Power cycle" }
+ }
+ return "Unknown ($lbr)"
+}
+
+proc {system idletime} {} {
+ if {[file exists "/tmp/.lastir"]} {
+ return $([clock seconds] - [file mtime "/tmp/.lastir"])
+ }
+ return 0
+}
+
+proc {system fhtcpversion} {} {{ver ""}} {
+ if {$ver ne ""} { return $ver }
+ set file "/etc/fhtcpversion"
+ if {![file exists $file]} { set file "/root/fhtcpversion" }
+ if {[catch {set fp [open $file r]}]} {
+ set ver "?.??.??"
+ } else {
+ set ver [string trim [read $fp]]
+ close $fp
+ }
+ return $ver
+}
+
+# :c0400 System ID
+# 4:c8400 read 1128 bytes
+# 3:c8c00 MAC address
+# 2:c9000
+# 1:cb400 DTCP key material
+# 0:cb800 Serial Number
+# 5:cbc00
+
+proc {system systemid} {} {{id ""}} {
+ if {$id ne ""} { return $id }
+ set fd [open /dev/mtd3 r]
+
+ $fd seek 0xc0400
+ set bytes [$fd read 4]
+ $fd close
+ binary scan $bytes H* hex
+ set id [format "%s.%s" [string range $hex 0 3] [string range $hex 4 end]]
+ return $id
+}
+
+proc {system macaddr} {} {{mac ""}} {
+ if {$mac ne ""} { return $mac }
+ set fd [open /dev/mtd3 r]
+
+ $fd seek 0xc8c00
+ set bytes [$fd read 6]
+ $fd close
+ set tmac ""
+ foreach b [split $bytes ""] {
+ binary scan $b H* hex
+ if {$tmac ne ""} { append tmac : }
+ append tmac $hex
+ }
+ return [set mac $tmac]
+}
+
+proc {system serialno} {} {{serial ""}} {
+ if {$serial ne ""} { return $serial }
+ set fd [open /dev/mtd3 r]
+
+ $fd seek 0xcb800
+ set bytes [$fd read 14]
+ $fd close
+ set serial "[string range $bytes 0 1] [string range $bytes 2 8] [
+ string range $bytes 9 end]"
+ return $serial
+}
+proc {system keybytestostring} {key_bytes} {
+ binary scan $key_bytes H* key_str
+ if {[string length $key_str] == 32} {
+ return $key_str
+ }
+ return {}
+}
+
+proc {system encryptionkey} {} {{key ""}} {
+ if {$key ne ""} { return $key }
+ set fd [open /dev/mtd3 r]
+ $fd seek 0xc8c00
+ set bytes [$fd read 6]
+ $fd seek 0xcb800
+ append bytes [$fd read 10]
+ $fd close
+ return [system keybytestostring $bytes]
+}
+
+proc {system customencryptionkey} {{key ""}} {{keyfile "/mod/boot/cryptokey"}} {
+
+ set ck_fd {}
+ try {
+ if {$key eq ""} {
+ set ck_fd [open $keyfile r]
+ set ck_bytes [$ck_fd read 16]
+ return [system keybytestostring $ck_bytes]
+ } elseif {[string equal -nocase $key [system encryptionkey]]} {
+ file delete -force $keyfile
+ return $key
+ } else {
+ set ck_bytes [binary format H* $key]
+ set test [system keybytestostring $ck_bytes]
+ if {![string equal -nocase $test $key]} {
+ throw 1 "Invalid custom key"
+ }
+ if {[file exists $keyfile]} {
+ # attempt not to truncate on update until written
+ set access r+
+ } else {
+ set access w
+ }
+ set ck_fd [open $keyfile $access]
+ $ck_fd seek 0
+ $ck_fd puts -nonewline $ck_bytes
+ $ck_fd close
+ set ck_fd {}
+ return $key
+ }
+ } on error {msg opts} {
+ return {}
+ } finally {
+ if {$ck_fd ne {}} {
+ $ck_fd close
+ }
+ }
+}
+
+proc {system loaderver} {} {{ver ""}} {
+ if {$ver ne ""} { return $ver }
+ set fd [open /dev/mtd3 r]
+ $fd seek 0x20006
+ set bytes [$fd read 2]
+ $fd close
+ set ver [switch $bytes {
+ "\x04\x5f" { format "a7.30" }
+ "\xec\xe5" { format "a7.31" }
+ "\x59\x9d" { format "a7.33" }
+ "\x72\x5c" { format "a7.34" }
+ "\xe5\xe3" { format "L7.26" }
+ "\x8f\x7c" { format "L7.27" }
+ default {
+ binary scan $bytes H* hex
+ format "Unknown - $hex"
+ }
+ }]
+ return $ver
+}
+
+proc {system kernelver} {} {{ver ""}} {
+ if {$ver ne ""} { return $ver }
+ #1 SMP Sun Mar 25 18:30:38 KST 2012
+ set rver [string range [exec uname -v] 11 end]
+ set ver [switch $rver {
+ # HDR
+ "Sep 16 20:17:56 KST 2010" { format "HDR_1.01.05" }
+ "Oct 11 21:11:05 KST 2010" { format "HDR_1.01.09" }
+ "Jan 21 15:44:39 KST 2011" { format "HDR_1.02.07" }
+ "Jun 11 00:54:19 KST 2011" { format "HDR_1.02.20" }
+ "Mar 25 18:30:38 KST 2012" { format "HDR_1.02.27" }
+ "May 27 00:19:34 KST 2012" { format "HDR_1.02.28" }
+ "Jul 5 11:11:28 KST 2012" { format "HDR_1.02.29" }
+ "Jan 12 16:49:05 KST 2013" { format "HDR_1.02.32" }
+ "Mar 6 07:27:02 KST 2013" { format "HDR_1.03.06(a)" }
+ "May 8 14:32:30 KST 2013" { format "HDR_1.03.06(b)" }
+ "Dec 10 14:36:54 KST 2013" { format "HDR_1.03.11" }
+ "Feb 7 14:15:02 KST 2014" { format "HDR_1.03.12" }
+ "May 19 22:39:27 BST 2014" { format "HDR_CFW_3.00" }
+ "Feb 19 20:58:57 GMT 2015" { format "HDR_CFW_3.03" }
+ "Mar 11 23:34:27 GMT 2016" { format "HDR_CFW_3.10" }
+ "Mar 11 23:37:22 GMT 2016" { format "HDR_CFW_3.10d" }
+ "Jan 9 04:46:49 GMT 2017" { format "HDR_CFW_3.11" }
+ "Jan 9 04:50:13 GMT 2017" { format "HDR_CFW_3.11d" }
+ "Mar 23 19:36:14 GMT 2017" { format "HDR_CFW_3.12" }
+ "Mar 23 19:39:44 GMT 2017" { format "HDR_CFW_3.12d" }
+ "Apr 18 22:42:30 BST 2017" { format "HDR_CFW_3.13" }
+ "Apr 18 22:45:34 BST 2017" { format "HDR_CFW_3.13d" }
+ "Mar 29 13:48:27 BST 2020" { format "HDR_CFW_3.14" }
+ "Mar 29 13:52:16 BST 2020" { format "HDR_CFW_3.14d" }
+
+ # HD
+ "Oct 11 21:14:31 KST 2010" { format "HD_1.01.12" }
+ "May 17 14:16:20 KST 2011" { format "HD_1.02.18" }
+ "Jun 11 00:54:19 KST 2011" { format "HD_1.02.20" }
+ "Mar 25 07:09:19 KST 2012" { format "HD_1.02.27" }
+ "May 27 00:19:40 KST 2012" { format "HD_1.02.28" }
+ "Jul 5 19:41:17 KST 2012" { format "HD_1.02.29" }
+ "Oct 13 12:48:09 KST 2012" { format "HD_1.02.31" }
+ "Dec 9 14:15:07 KST 2014" { format "HD_1.03.02" }
+
+ default { format "Unknown - $rver" }
+ }]
+ return $ver
+}
+
+# Newer version below is over 100 times faster
+#proc {system pkgver} {{pkg webif}} {
+# return [lrange [split [exec opkg list-installed $pkg] " "] 2 end]
+#}
+
+proc {system pkgver} {{pkg webif}} {
+ if {[catch {set fp [open /mod/var/opkg/info/$pkg.control r]}]} {
+ return 0
+ }
+
+ set v 0
+ foreach line [split [$fp read] "\n"] {
+ if {[string equal -length 9 $line "Version: "]} {
+ lassign $line x v
+ break
+ }
+ }
+ $fp close
+ return $v
+}
+
+proc {system pkginst} {pkg} {
+ return [file exists "/mod/var/opkg/info/$pkg.control"]
+}
+
+proc {system mediaroot} {} {
+ switch [system model] {
+ HDR { return "/media/My Video" }
+ HD { return "/media/drive1/Video" }
+ }
+ return ""
+}
+
+proc {system dlnastatus} {} {
+ return [system is_listening 9000]
+}
+
+proc {system dlnadb} {} {
+ switch [system model] {
+ HDR { return "/mnt/hd2/dms_cds.db" }
+ HD { return "/media/drive1/dms_cds.db" }
+ }
+ return ""
+}
+
+proc {system _dlnaurl} {file urlbase} {
+ set mime "video/mp2t"
+ set nfile $file
+ if {![catch {set nfile [file normalize $file]}]} {
+ set file $nfile
+ }
+ if {[catch {set db [sqlite3.open [system dlnadb]]}]} {
+ return {}
+ }
+ set muri [$db query {
+ select tblresource.mimetype, contenturi
+ from tblresource join tblmedia using (mediaid)
+ where localurl = '%s'} $file]
+ if {[llength $muri]} {
+ lassign [lindex $muri 0] x maybemime x xuri
+ if {$maybemime ne "video/ts"} {
+ set mime $maybemime
+ }
+ } else {
+ # Try for partially linked entry
+ set muri [$db query {
+ select mediaid from tblmedia
+ where localurl = '%s'
+ } $file]
+ if {![llength $muri]} { return {} }
+ lassign [lindex $muri 0] x mediaid
+ set xuri "media/$mediaid.TS"
+ }
+
+ $db close
+
+ set url "http://${urlbase}:9000/web/$xuri"
+
+ return [list $url $mime]
+}
+
+proc {system dlnaurl} {file {urlbase "127.0.0.1"}} {
+ if {$urlbase eq ""} { set urlbase [system ip] }
+ set retries 5
+ set ret {}
+ while {$retries > 0 && [\
+ catch {set ret [system _dlnaurl $file $urlbase]}]} {
+ incr retries -1
+ sleep 1
+ }
+ return $ret
+}
+
+proc {system dlnahelper} {file {urlbase "127.0.0.1"}} {
+ set dir /mnt/hd2/mod/.dlnahelper
+ require lock
+
+ if {$file eq "-release"} {
+ release_lock dlnahelper
+ return {}
+ }
+
+ if {![acquire_lock dlnahelper 2]} { return {} }
+
+ if {![file isdirectory $dir]} { file mkdir $dir }
+
+ set base [file rootname $file]
+ foreach ext {hmt ts} {
+ file delete "$dir/helper.$ext"
+ file link -symbolic "$dir/helper.$ext" "$base.$ext"
+ }
+
+ set ret [system dlnaurl "$dir/helper.ts" $urlbase]
+ if {![llength $ret]} {
+ # Manually import the helper file.
+ if {![catch {set db [sqlite3.open [system dlnadb]]}]} {
+ $db query {
+ insert into tblmedia
+ (localurl,folder,mediatype,refcount)
+ values('%s','%s',0,1)
+ } "$dir/helper.ts" $dir
+ $db close
+ set ret [system dlnaurl "$dir/helper.ts" $urlbase]
+ }
+ }
+ if {![llength $ret]} {
+ release_lock dlnahelper
+ }
+ return $ret
+}
+
+proc {system dustbin} {{short 0}} {
+ set dustbin "\[Deleted Items\]"
+ if {![catch {set fd [open "/mod/boot/dustbin.name" r]}]} {
+ set dustbin [lindex [split [read $fd] "\n"] 0]
+ $fd close
+ }
+ if {$short} { return $dustbin }
+ return "[system mediaroot]/$dustbin"
+}
+
+proc {system dustbinsize} {} {
+ set bin [system dustbin]
+ set ret 0
+ if {[file isdirectory $bin]} {
+ if {[catch {
+ lassign [exec /mod/bin/busybox/du -s $bin] ret
+ }]} {
+ set ret 0
+ }
+ }
+ return $($ret * 1024)
+}
+
+proc {system diskpart} {} {
+ switch [system model] {
+ HDR { return "/mnt/hd2" }
+ HD { return "/media/drive1" }
+ }
+ return ""
+}
+
+proc {system diskdev} {} {
+ return [lindex [lsearch -regexp -inline \
+ [split [file read /proc/mounts] "\n\r"] " [system diskpart] "] 0]
+}
+
+proc {system disk} {} {
+ return [string range [system diskdev] 0 end-1]
+}
+
+proc {system disktemp} {} {
+ if {[catch {
+ set smart [exec /bin/smartctl -A [system disk] | grep ^194]
+ regsub -all -- {[[:space:]]+} $smart " " smart
+ set temp [lindex [split $smart] 9]
+ }]} {
+ set temp 0
+ }
+ return $($temp + 0)
+}
+
+proc {system tsrdir} {} {
+ switch [system model] {
+ HDR {
+ set tsrdir "/mnt/hd2/Tsr"
+ }
+ HD {
+ set tsrdir "/media/drive1/.tsr"
+ }
+ }
+ return $tsrdir
+}
+
+proc {system tsr} {} {
+ return [file join [system tsrdir] "0.ts"]
+}
+
+require pretty_size
+
+proc {system diskspace} {{raw 0}} {
+ set part [system diskpart]
+
+ lassign [exec /mod/bin/busybox/stat -f -c {%S %b %f} $part] \
+ bsize blocks fblocks
+
+ set size $($bsize * $blocks)
+ set free $($bsize * $fblocks)
+ set used $($size - $free)
+ set perc $($used * 100 / $size)
+ set fperc $(100 - $perc)
+
+ set tsrdir [system tsrdir]
+ switch [system model] {
+ HDR {
+ set tsrok [file isdirectory $tsrdir]
+ }
+ HD {
+ set tsrok [file exists "$tsrdir/0.ts"]
+ }
+ }
+
+ if {$tsrok} {
+ set tsrbuf 21474836480
+ if {[catch {
+ lassign [exec du -ks $tsrdir] tsrused
+ }]} {
+ set tsrused 0
+ set tsrbuf 0
+ } else {
+ set tsrused $($tsrused * 1024)
+ }
+ } else {
+ set tsrbuf 0
+ set tsrused 0
+ }
+
+ if {!$raw} {
+ set size [pretty_size $size]
+ set free [pretty_size $free]
+ set used [pretty_size $used]
+ set tsrbuf [pretty_size $tsrbuf]
+ set tsrused [pretty_size $tsrused]
+ }
+
+ return [list $size $used $perc $free $fperc $tsrbuf $tsrused]
+}
+
+proc {system diskfree} {} {
+ lassign [exec /mod/bin/busybox/stat -f -c {%S %f} [system diskpart]] \
+ bsize fblocks
+ return $($bsize * $fblocks)
+}
+
+proc {system busy} {} {
+ # Is humaxtv doing anything important?
+ if {[catch {set pid [exec /mod/bin/busybox/pgrep -n humaxtv]}]} {
+ return 0
+ }
+ set c 0
+ foreach line [split [\
+ exec /mod/webif/lib/bin/lsof -X -Fn -p $pid] "\n"] {
+ if {[string match {*Video*.ts} $line]} { incr c }
+ }
+ if {$c > 0} { return 1 }
+ return 0
+}
+
+proc {system inuse} {file} {
+ if {![file exists $file]} { return 0 }
+ set op [exec /mod/webif/lib/bin/lsof -X -Fn $file]
+ switch -glob -- $op {
+ "" -
+ "*status error*" { return 0 }
+ }
+ return 1
+}
+
+proc {system dirinuse} {dir} {
+ set files {}
+ foreach line [split [\
+ exec /mod/webif/lib/bin/lsof -X -Fn +d $dir] "\n"] {
+ if {[string index $line 0] ne "n"} continue
+ lappend files [file tail $line]
+ }
+ return $files
+}
+
+proc {system reboot} {{fast 0}} {
+ if {$fast} {
+ exec /mod/webif/lib/bin/reboot -f
+ } else {
+ exec /mod/webif/lib/bin/reboot
+ }
+}
+
+proc {system restartpending} {{mode 1}} {
+ if {$mode} {
+ close [open /tmp/.restartpending w]
+ } else {
+ file delete /tmp/.restartpending
+ }
+}
+
+proc {system param} {param {type Value} {tbl MENUCONFIG}} {
+ if {[catch {set db [sqlite3.open /var/lib/humaxtv/setup.db]} msg]} {
+ return 0
+ }
+ set val 0
+ set ret [$db query "
+ select item$type
+ from TBL_$tbl
+ where itemName = '$param'
+ "]
+ if {[llength $ret] == 1} {
+ lassign [lindex $ret 0] x val
+ }
+ $db close
+ return $val
+}
+
+proc {system padding} {} {
+ return [list \
+ [system param START_PADDING_TIME] \
+ [system param STOP_PADDING_TIME] \
+ ]
+}
+
+proc {system instandby} {} {
+ return [system param LAST_STANDBY Value USERCONFIG]
+}
+
+proc {system mkdir_p} {dir} {
+ exec /mod/bin/busybox/mkdir -p $dir
+}
+
+proc {system dirblockers} {dir {permitted {".*"}}} {
+ set fl {}
+ foreach e [readdir -nocomplain $dir] {
+ if {$e in {. ..}} continue
+ if {[file isdirectory "$dir/$e"]} {
+ lappend fl $e
+ continue
+ }
+ set flag 1
+ foreach p $permitted {
+ if {[string match -nocase $p $e]} {
+ set flag 0
+ break
+ }
+ }
+ if {$flag} {
+ lappend fl $e
+ }
+ }
+ return $fl
+}
+
+proc {system rmdir_if_empty} {dir {permitted {".*"}}} {
+ set fl [system dirblockers $dir $permitted]
+
+ if {![llength $fl]} {
+ file delete -force $dir
+ return 1
+ }
+ return 0
+}
+
+proc {system listening} {{mport 0}} {
+ set ret {}
+ foreach line [split [exec /mod/bin/busybox/netstat -lntpw] "\n"] {
+ # Remove repeated spaces
+ regsub -all -- {[[:space:]]+} $line " " line
+# tcp 0 0 0.0.0.0:9955 0.0.0.0:* LISTEN 169/humaxtv
+ lassign [split $line " "] x x x port x type app
+ if {$type ne "LISTEN"} { continue }
+ lassign [split $port ":"] x port
+
+ if {$mport && $mport != $port} { continue }
+ lappend ret [list $port [split $app "/"]]
+ }
+ return $ret
+}
+
+proc {system is_listening} {mport} {
+ return [llength [system listening $mport]]
+}
+
+proc {system logtimestamp} {} {
+ return [clock format [clock seconds] -format "%d/%m/%Y %H:%M:%S"]
+}
+
+proc {system _log} {log msg} {
+ set logfd [open $log "a+"]
+ puts $logfd "[system logtimestamp] - $msg"
+ $logfd close
+}
+
+proc {system log} {log msg} {
+ system _log "/var/log/$log.log" $msg
+}
+
+proc {system plog} {log msg} {
+ system _log "/mod/tmp/$log.log" $msg
+}
+
+proc {system notify} {msg} {
+ system plog notify $msg
+}
+
+proc {system display} {hdr hd} {
+ if {[system model] eq "HDR"} {
+ exec /sbin/display $hdr
+ } else {
+ exec /sbin/display "\$$hd"
+ }
+}
+
+proc {system tuners} {} {
+ if {[system model] eq "HDR"} {
+ return 2
+ } else {
+ return 1
+ }
+}
+
+proc {system uptime} {} {
+ set fd [open /proc/uptime r]
+ set uptime [lindex [split [read $fd]] 0]
+ $fd close
+ return $uptime
+}
+
+proc {system filename} {str {extra ""}} {
+ # Humax TV replaces these characters.
+ # "%*./:<>?\|
+ set chars "\"%*\./:<>?\\\\|$extra"
+ return [regsub -all -- "\[$chars]" $str "_"]
+}
+
+proc {system connectivity} {{site "hpkg.tv"} {port 80} {ret "0"}} {
+ set extra [lassign [exec /mod/bin/tcpping $site $port] result]
+ if {$ret ne "0"} { upvar $ret err }
+ switch $result {
+ OK { return 1 }
+ default {
+ set err [list $result $extra]
+ return 0
+ }
+ }
+}
+
+proc {system nuggeted} {} {
+ if {![system pkginst nugget]} { return 0 }
+ if {[system nugget ping] eq "PONG"} {
+ return 1
+ }
+ return 0
+}
+
+proc {system nugget} {args} {
+ if {[catch {set ret [exec /mod/bin/nugget {*}$args]} msg]} {
+ return ""
+ }
+ return $ret
+}
+
+proc {system strip} {str} {
+ if {[string range $str 1 2] eq "i7"} {
+ # ISO6937, convert to UTF-8
+ return [xconv [string range $str 3 end]]
+ }
+ if {[string first "\025" $str] == 0} {
+ # UTF-8
+ return [string range $str 1 end]
+ }
+ return [xconv $str]
+}
+
+proc {system usbdisks} {{full 0}} {
+ set disks [lmap i [glob -nocomplain \
+ -directory /sys/bus/usb/drivers/usb-storage *:*] {
+ file tail $i
+ }]
+ if {!$full} {
+ return [llength $disks]
+ }
+ if {![llength $disks]} { return {} }
+
+ # Build list of device to device ID
+ set map {}
+ foreach block [glob -nocomplain -directory /sys/block sd?] {
+ set link [file readlink $block/device]
+ foreach disk $disks {
+ if {[string first "/$disk/" $link] >= 0} {
+ set map([file tail $block]) $disk
+ }
+ }
+
+ }
+ return $map
+}
+
+proc {system usbmounts} {{full 0}} {
+ if {!$full} {
+ set num 0
+ catch {
+ set num [
+ exec grep -cE {^/dev/sd[a-z][1-9] /media/[^/]+ } \
+ /proc/mounts]
+ }
+ return $num
+ }
+ set ret {}
+ if {[catch {
+ set lines [exec grep -E {^/dev/sd[a-z][1-9] /media/[^/]+ } \
+ /proc/mounts]
+ }]} { return $ret }
+ foreach line [split $lines "\n"] {
+ lassign $line dev mp
+ set rec "DEV {$dev} MP {$mp} LABEL {NO NAME} TYPE Unknown"
+
+ set rec(VENDOR) Unknown
+ set rec(MODEL) Unknown
+
+ if {[regexp {/dev/(sd[a-z])} $dev x rdev]} {
+ set rec(RDEV) $rdev
+ if {[file readable [
+ set f /sys/block/$rdev/device/vendor]]} {
+ set rec(VENDOR) [string trim [file read $f]]
+ }
+ if {[file readable [
+ set f /sys/block/$rdev/device/model]]} {
+ set rec(MODEL) [string trim [file read $f]]
+ }
+ }
+
+ lassign [exec stat -f -c {%S %b %f} $mp] blocks blockc blockf
+ set rec(SIZE) $($blockc * $blocks)
+ set rec(USED) $(100 - $blockf * 100 / $blockc)
+
+ # /dev/sda1: LABEL="SAN" UUID="DBC1-1CF8" TYPE="vfat"
+ catch {
+ foreach field [
+ split [exec /mod/sbin/blkid -c /dev/null $dev] "\n"] {
+ while {[regexp { *([A-Z]+)="?([^"]+)"?(.*)} \
+ $field x k v field]} {
+ set rec($k) $v
+ }
+ }
+ }
+ # Test for EXFAT
+ if {$rec(TYPE) eq "Unknown" && [
+ file exists /mod/sbin/exfatlabel]} {
+ catch {
+ set label [exec /mod/sbin/exfatlabel $dev 2>/dev/null]
+ if {$label ne ""} {
+ set rec(TYPE) "EXFAT"
+ set rec(LABEL) $label
+ }
+ }
+ }
+
+ lappend ret $rec
+ }
+
+ return $ret
+}
+
+proc {system has} {comp} {
+ switch $comp {
+ wifi_dongle {
+ if {[catch {exec /mod/bin/iwgetid}]} { return 0 }
+ return 1
+ }
+ tvdb {
+ require settings.class
+ return [[settings] _nval_setting tvdb]
+ }
+ }
+ return 0
+}
+
+# Note that {system checkop} can be called from processes other than that
+# which started the operation. Hence no visilibty into the ::system::ops
+# dictionary.
+proc {system checkop} {op} {
+ if {![regexp -nocase -- {^[.a-z0-9]+$} $op]} { return 0 }
+ if {[catch {set fp [open "/tmp/.bgop.$op" "a"]}]} {
+ return 0
+ }
+ set ret 1
+ if {[$fp lock]} {
+ $fp unlock
+ set ret 0
+ }
+ $fp close
+ return $ret
+}
+
+set ::system::ops {}
+
+proc {system startop} {args op file} {
+ if {![regexp -nocase -- {^[a-z0-9]+$} $op]} { return 0 }
+ if {"-multiple" in $args} {
+ set i 0
+ append op "[pid]."
+ while {[dict exists $::system::ops "$op$i"]} { incr i }
+ append op $i
+ } elseif {[dict exists $::system::ops $op]} {
+ system endop $op
+ }
+ if {[catch {
+ set fp [open "/tmp/.bgop.$op" "a"]
+ if {[$fp lock]} {
+ $fp puts "$file\n[pid]"
+ $fp flush
+ set ::system::ops($op) $fp
+ } else {
+ $fp close
+ error "Could not lock op file."
+ }
+ }]} { return 0 }
+ return $op
+}
+
+proc {system endop} {op} {
+ if {![regexp -nocase -- {^[.a-z0-9]+$} $op]} { return 0 }
+ if {[dict exists $::system::ops $op]} {
+ catch {
+ set fp $::system::ops($op)
+ $fp unlock
+ $fp close
+ }
+ dict unset ::system::ops $op
+ }
+ if {[file exists "/tmp/.bgop.$op"]} {
+ file delete "/tmp/.bgop.$op"
+ return 1
+ }
+ return 0
+}
+
+proc {system specialdir} {dir} {
+ if {[string match {\[*} [string trimleft [file tail $dir]]]} {
+ return 1
+ }
+ return 0
+}
+
diff --git a/webif/lib/ts.class b/webif/lib/ts.class
index 05fd9d86..526814a5 100644
--- a/webif/lib/ts.class
+++ b/webif/lib/ts.class
@@ -339,7 +339,7 @@ ts method setgenre {newgenre} {
}
ts method dlnaloc {{urlbase ""}} {
- return [system dlnaurl $file $urlbase]
+ return [system dlnaurl [file normalize $file] $urlbase]
}
ts method cleanbmp {} {
diff --git a/webif/lib/ts.class.orig b/webif/lib/ts.class.orig
new file mode 100644
index 00000000..05fd9d86
--- /dev/null
+++ b/webif/lib/ts.class.orig
@@ -0,0 +1,899 @@
+
+if {![exists -command class]} { package require oo }
+if {![exists -command pack]} { package require pack }
+if {![exists -command xconv]} { package require xconv }
+if {![exists -command binary]} { package require binary }
+
+source /mod/webif/lib/setup
+require system.class tvdb.class classdump
+
+set tsgroup {ts nts hmt thm}
+
+class ts {
+ file ""
+ base ""
+ title ""
+ synopsis ""
+ definition ""
+ channel_num 0
+ channel_name ""
+ start 0
+ end 0
+ flags ""
+ error ""
+ guidance ""
+ bookmarks 0
+ schedstart 0
+ scheddur 0
+ genre 0
+ resume 0
+ status ""
+ series ""
+
+ seriescached 1
+ seriesnum 0
+ episodenum 0
+ episodetot 0
+ episodename ""
+ tvdb_method ""
+ tvdb_series {}
+ tvdb_data {}
+}
+
+ts method bfile {} {
+ return [file tail [file rootname $file]]
+}
+
+ts method dir {} {
+ return [file dirname $file]
+}
+
+ts method duration {{raw 0}} {
+ set d [expr $end - $start]
+ if {!$raw} { set d $($d / 60) }
+ return $d
+}
+
+ts method size {} {
+ return [file size $file]
+}
+
+ts method _parse {line} {
+ set vars [split $line "\t"]
+
+ lassign [split $line "\t"] \
+ title synopsis definition channel_num channel_name \
+ start end flags_list guidance bookmarks schedstart scheddur \
+ genre resume status seriesnum episodenum episodetot
+
+ set synopsis [xconv $synopsis]
+
+ set flags [split [string range $flags_list 0 end-1] ,]
+}
+
+ts method lastmod {} {
+ return [file mtime "[file rootname $file].hmt"]
+}
+
+ts method inuse {} {
+ return [system inuse $file]
+}
+
+ts method bookmarks {{aslist 0}} {
+ set marks [split [string trim [exec /mod/bin/hmt -bookmarks $file]]]
+ if {$aslist} { return $marks }
+ return [join $marks " "]
+}
+
+ts method setbookmarks {marks} {
+ exec /mod/bin/hmt +setbookmarks=[join $marks :] $file
+}
+
+ts method storeepisode {{data {}}} {
+ if {[llength $data]} {
+ set d [join $data ","]
+ } else {
+ set d "$seriesnum,$episodenum,$episodetot"
+ }
+ exec /mod/bin/hmt +setseries=$d $file
+}
+
+ts method clearepdata {} {
+ set seriesnum 0
+ set episodenum 0
+ set episodetot 0
+}
+
+ts method flag {f} {
+ if {$f in $flags} {return 1} else {return 0}
+}
+
+ts method unflag {f} {
+ lremove flags $f
+}
+
+ts method unlock {} {
+ set cmd [list /mod/bin/hmt -lock $file]
+ exec {*}$cmd
+ lremove flags "Locked"
+ return 1
+}
+
+ts method lock {} {
+ set cmd [list /mod/bin/hmt +lock $file]
+ exec {*}$cmd
+ ladd flags "Locked"
+ return 1
+}
+
+set ::ts::flagmap {
+ detectads Addetection
+ dedup Deduped
+ encrypted ODEncrypted
+ protect Encrypted
+}
+
+ts method setflag {flag {dummy 0}} {
+ if {!$dummy} {
+ set cmd [list /mod/bin/hmt +$flag $file]
+ if {[catch {exec {*}$cmd}]} {
+ throw 20 "Unknown flag."
+ }
+ }
+ if {[dict exists $::ts::flagmap $flag]} {
+ ladd flags $::ts::flagmap($flag)
+ } else {
+ ladd flags [string totitle $flag]
+ }
+ return 1
+}
+
+ts method unsetflag {flag {dummy 0}} {
+ if {!$dummy} {
+ set cmd [list /mod/bin/hmt -$flag $file]
+ if {[catch {exec {*}$cmd}]} {
+ throw 20 "Unknown flag."
+ }
+ }
+ if {[dict exists $::ts::flagmap $flag]} {
+ lremove flags $::ts::flagmap($flag)
+ } else {
+ lremove flags [string totitle $flag]
+ }
+ return 1
+}
+
+ts method set_shrunk {} {
+ set cmd [list /mod/bin/hmt +shrunk $file]
+ exec {*}$cmd
+ ladd flags "Shrunk"
+ return 1
+}
+
+ts method set_deduped {} {
+ set cmd [list /mod/bin/hmt +dedup $file]
+ exec {*}$cmd
+ ladd flags "Deduped"
+ return 1
+}
+
+ts method unset_deduped {} {
+ set cmd [list /mod/bin/hmt -dedup $file]
+ exec {*}$cmd
+ lremove flags "Deduped"
+ return 1
+}
+
+ts method unenc {} {
+ set cmd [list /mod/bin/hmt -protect $file]
+ exec {*}$cmd
+ lremove flags "Encrypted"
+ return 1
+}
+
+ts method enc {} {
+ set cmd [list /mod/bin/hmt +protect $file]
+ exec {*}$cmd
+ ladd flags "Encrypted"
+ return 1
+}
+
+ts method set_new {} {
+ set cmd [list /mod/bin/hmt +new $file]
+ exec {*}$cmd
+ ladd flags "New"
+ return 1
+}
+
+ts method set_watched {} {
+ set cmd [list /mod/bin/hmt -new $file]
+ exec {*}$cmd
+ lremove flags "New"
+ return 1
+}
+
+ts method setfile {f} { set file $f }
+
+proc {ts parse} {file line} {
+ set e [ts new]
+ $e setfile $file
+ $e _parse $line
+ return $e
+}
+
+proc {ts exec} {file} {
+ set raw 0
+ set cmd [list /mod/bin/hmt]
+ lappend cmd "-p"
+ lappend cmd $file
+
+ #puts "CMD -$cmd-"
+
+ return [exec {*}$cmd]
+}
+
+ts method fileset {} {
+ global tsgroup
+
+ set root [file rootname $file]
+ set fset {}
+ foreach ext $tsgroup {
+ if {[file exists "$root.$ext"]} {
+ lappend fset "$root.$ext"
+ }
+ }
+ return $fset
+}
+
+proc {ts fetch_or_error} {file {checked 0}} {
+ if {[catch {set ts [ts fetch $file $checked]}] || $ts == 0} {
+ print "Could not load ts file $file"
+ return 0
+ }
+ return $ts
+}
+
+proc {ts fetch} {file {checked 0}} {
+ # Check that this is a .ts file which has at least one sidecar
+ # file (.hmt)
+ if {!$checked} {
+ if {[file extension $file] ne ".ts"} { return 0 }
+ if {![file exists "[file rootname $file].hmt"]} { return 0 }
+ }
+
+ if {[file extension $file] ne ".ts"} {
+ set file "[file rootname $file].ts"
+ }
+
+ return [ts parse $file [ts exec $file]]
+}
+
+ts method delete {} {
+ foreach f [$self fileset] {
+ file tdelete $f
+ puts "Removed $f
"
+ }
+ return 1
+}
+
+ts method move {dst {touch 0} {force 0}} {
+ foreach f [$self fileset] {
+ set nf "$dst/[file tail $f]"
+ while {[file exists $nf]} {
+ set nf "$dst/_[file tail $nf]"
+ }
+ file rename $f $nf
+ if {$touch} {
+ exec /mod/bin/busybox/touch $nf
+ }
+ }
+ return 1
+}
+
+ts method copy {dst} {
+ foreach f [$self fileset] {
+ file copy $f "$dst/[file tail $f]"
+ }
+ return 1
+}
+
+ts method settitle {newtitle} {
+ if {[string length $newtitle] > 48} { return }
+
+ exec /mod/bin/hmt "+settitle=${newtitle}" $file
+ set title $newtitle
+}
+
+ts method setsynopsis {newsynopsis} {
+ if {[string length $newsynopsis] > 252} { return }
+
+ exec /mod/bin/hmt "+setsynopsis=${newsynopsis}" $file
+ set synopsis $newsynopsis
+}
+
+ts method setguidance {newguidance} {
+ if {[string length $newguidance] > 74} { return }
+
+ if {$newguidance eq ""} {
+ exec /mod/bin/hmt "-guidance" $file
+ } else {
+ exec /mod/bin/hmt "+setguidance=${newguidance}" $file
+ }
+ set guidance $newguidance
+}
+
+proc {ts genre} {genre} {
+ if {![string is integer $genre] || $genre < 0} {
+ set genre 0
+ }
+ if {$genre <= 15} {
+ set genre $($genre << 4)
+ }
+ return $genre
+}
+
+ts method setgenre {newgenre} {
+ set newgenre [ts genre $newgenre]
+ exec /mod/bin/hmt "+setgenre=-${newgenre}" $file
+ set genre $newgenre
+}
+
+ts method dlnaloc {{urlbase ""}} {
+ return [system dlnaurl $file $urlbase]
+}
+
+ts method cleanbmp {} {
+ set bfile [file rootname $file]
+ foreach f [glob -nocomplain "${bfile}*.bmp"] {
+ file delete $f
+ }
+}
+
+ts method mkbmps {{offset 0}} {
+ set bfile [file rootname $file]
+ if {[catch {
+ exec /mod/bin/ffmpeg -loglevel fatal -ss $offset -i $file \
+ -vf fps=fps=2 -frames 5 \
+ -pix_fmt argb -vf vflip -s 140x78 "${bfile}%d.bmp"
+ } msg]} {
+ puts "ERROR: $msg"
+ return 0
+ }
+ return 1
+}
+
+ts method mkbmp {{offset 0} {ext ""}} {
+ set bfile [file rootname $file]
+ set bmpfile "$bfile$ext.bmp"
+ set cmd [list /mod/bin/ffmpeg -loglevel fatal -ss $offset -i $file \
+ -frames 1 -pix_fmt argb -vf vflip -s 140x78 -y $bmpfile]
+ catch { exec {*}$cmd }
+ if {![catch { file stat $bmpfile stbmp }]} {
+ if {[dict get $stbmp size] != 0} { return 1 }
+ }
+ return 0
+}
+
+ts method mkthm {{offset 0}} {
+ if {![$self mkbmp $offset]} { return 0 }
+ set bfile [file rootname $file]
+ # Trim the bitmap header from the start of the file
+ if {[catch {
+ exec /bin/dd if=$bfile.bmp of=$bfile.thm~ bs=54 skip=1
+ } msg]} {
+ puts "ERROR: $msg"
+ return 0
+ }
+ exec /bin/echo -n " " >> $bfile.thm~
+ file rename -force $bfile.thm~ $bfile.thm
+ file tdelete $bfile.bmp
+ $self setflag thumbnail
+ return 1
+}
+
+# From MontysEvilTwin
+# - https://hummy.tv/forum/threads/7787/page-2#post-106826
+# ffmpeg -i "File 1.ts" -c:a mp3 -b:a 128k "File 1.mp3"
+# ffmpeg -i "File 1.ts" -c:a copy "File 1.mp2"
+# ffmpeg -i "File 1.ts" -c:a copy "File 1.loas"
+ts method mkmp3 {{slow false} {tmp ""} {v 0} {br 128}} {
+ set rfile [file rootname $file]
+
+ if {$slow} {
+ set opts [list -c:a mp3 -b:a ${br}k]
+ set ext mp3
+ } else {
+ set opts [list -c:a copy]
+ if {$definition eq "HD"} {
+ set ext loas
+ } else {
+ set ext mp2
+ }
+ }
+ set cmd [list /mod/bin/ffmpeg \
+ -y -benchmark -vn -v $v \
+ -i $file {*}$opts \
+ ]
+
+ if {$tmp eq ""} {
+ lappend cmd "${rfile}.$ext"
+ } else {
+ lappend cmd "$tmp.$ext"
+ }
+
+ set output [exec {*}$cmd]
+
+ if {$tmp ne ""} {
+ file rename "$tmp.$ext" "${rfile}.mp3"
+ } elseif {$ext ne "mp3"} {
+ file rename "${rfile}.$ext" "${rfile}.mp3"
+ }
+
+ exec /mod/bin/id3v2 \
+ --song $title \
+ --comment $synopsis \
+ --album $channel_name \
+ --year "[clock format $start -format {%Y}]" \
+ "${rfile}.mp3"
+
+ return $output
+}
+
+ts method mkmpg {{tmp ""}} {
+ set rfile [file rootname $file]
+
+ set cmd [list /mod/bin/ffmpeg \
+ -y -benchmark -v 0 \
+ -i $file \
+ -map 0:0 -map 0:1 \
+ -vcodec copy -acodec copy]
+
+ if {$tmp eq ""} {
+ lappend cmd "${rfile}.mpg"
+ } else {
+ lappend cmd "$tmp.mpg"
+ }
+
+ set output [exec {*}$cmd]
+
+ if {$tmp ne ""} {
+ file rename "$tmp.mpg" "${rfile}.mpg"
+ }
+
+ return $output
+}
+
+proc {ts renamegroup} {from to} {
+ global tsgroup
+
+ set dir [file dirname $from]
+ set root [file rootname $from]
+
+ # Catch from string without a . character in it
+ if {$root eq $from} { return }
+
+ foreach ext $tsgroup {
+ set f "$root.$ext"
+ if {![file exists $f]} continue
+ file rename $f "${dir}/${to}.${ext}"
+ }
+
+ exec /mod/bin/hmt "+setfilename=$to" "${dir}/${to}.hmt"
+
+# set ndir [file normalize $dir]
+#
+# if {![catch {set db [sqlite3.open $::dmsfile]}]} {
+# catch {
+# set x [lindex [$db query {select mediaid from tblMedia
+# where localUrl = '%s'} [file normalize $from]] 0]
+# lassign $x key mediaid
+# if {$mediaid ne ""} {
+# $db query {update tblMedia set localUrl = '%s'
+# where mediaid = %s} "${ndir}/{$to}.ts" $mediaid
+# $db query {update tblMedia set title = '%s'
+# where mediaid = %s} "{$to}.ts" $mediaid
+# }
+# }
+# $db close
+# }
+}
+
+proc {ts touchgroup} {target ref} {
+ global tsgroup
+
+ set dir [file dirname $target]
+ set root [file rootname $target]
+
+ # Catch from string without a . character in it
+ if {$root eq $target} { return }
+
+ foreach ext $tsgroup {
+ set f "$root.$ext"
+ if {![file exists $f]} continue
+ file touch $f $ref
+ }
+}
+
+proc {ts resetnew} {dir} {
+ if {![file isdirectory $dir]} return
+ if {![file exists "$dir/.series"]} {
+ set fd [open "$dir/.series" "w"]
+ puts -nonewline $fd [string repeat "\x0" 276]
+ close $fd
+ }
+
+ set tot 0
+ set watched 0
+ foreach file [readdir -nocomplain $dir] {
+ if {![string match {*.ts} $file]} { continue }
+ incr tot
+ if {[set ts [ts fetch "$dir/$file"]] != 0} {
+ if {![$ts flag "New"]} { incr watched }
+ }
+ }
+
+ if {!$tot} {
+ file delete "$dir/.series"
+ return
+ }
+
+ set fd [open "$dir/.series"]
+ set bytes [read $fd]
+ close $fd
+ set recs [unpack $bytes -uintle 0 32]
+ set played [unpack $bytes -uintle 32 32]
+
+ #puts "Current: $played/$recs"
+ #hexdump $bytes
+ #puts "Calculated: $watched/$tot"
+
+ pack bytes $tot -intle 32 0
+ pack bytes $watched -intle 32 32
+
+ #hexdump $bytes
+
+ set fd [open "$dir/.series" "w"]
+ puts -nonewline $fd $bytes
+ close $fd
+}
+
+proc {ts iterate} {callback {verbose 0} {dir ""} {nospecial 0}} {{rootdev 0}} {
+ require system.class
+ if {$dir eq ""} {
+ set dir [system mediaroot]
+ file stat "$dir/" rootstat
+ set rootdev $rootstat(dev)
+ }
+
+ if {$verbose} { puts "Scanning directory ($dir)" }
+
+ if {$rootdev != 0} {
+ file stat "$dir/" st
+ if {$st(dev) != $rootdev} return
+ }
+
+ if {$nospecial && [system specialdir $dir]} return
+
+ foreach entry [readdir -nocomplain $dir] {
+ if {[file isdirectory "$dir/$entry"]} {
+ ts iterate $callback $verbose "$dir/$entry" $nospecial
+ continue
+ }
+ if {![string match {*.ts} $entry]} continue
+ if {[catch {set ts [ts fetch "$dir/$entry"]}]} continue
+ if {$ts == 0} continue
+ $callback $ts
+ }
+}
+
+#
+# Attempt to extract the series/episode names using a variety of techniques
+#
+
+ts method series_name {} {
+
+ # For recorded series, use the folder name
+ set dir [file dirname $file]
+ if {[file exists "$dir/.series"]} {
+ set s [file tail $dir]
+ } else {
+ set s $title
+ }
+
+ foreach x {
+ {^new: *}
+ } {
+ regsub -nocase -all -- $x $s "" s
+ }
+
+ return $s
+}
+
+set ::ts::episode_prefixes {
+ {^new series\.* *}
+ {^cbeebies\.* *}
+ {^cbbc\.* *}
+ {^t4: *}
+ {^brand new series *[-:]* *}
+ {^\.+}
+ { *\(Part [0-9] of [0-9]\) *}
+ {, Part [0-9]}
+}
+
+ts method _tvdb_resolve {seriesid} {
+ # See if we can find a TVDB series for this recording.
+
+ set dir [file dirname $file]
+
+ set tvdb_series [set v [tvdb series "" $seriesid]]
+ if {[$v get seriesid] == 0} { return }
+
+ # Got one.
+
+ # Easiest case - we can explicitly request the episode.
+ if {$seriesnum && $episodenum} {
+ if {$seriescached} {
+ set tvdb_method "cached values"
+ } else {
+ set tvdb_method "series and episode number"
+ }
+ return [$v episodebynum $seriesnum $episodenum]
+ }
+
+ # Now try to find the episode using the current episode name
+ # (using series or episode number if available)
+ set k [$v episodebyname $episodename $seriesnum $episodenum]
+ if {[llength $k]} {
+ set tvdb_method "episode name ($episodename)"
+ return $k
+ }
+
+ # More problematic but can at least narrow the list of candidates
+ # using the episode or series numbers if we have them.
+
+ if {$episodenum} {
+ set tvdb_method "episode number"
+ return [$v episodebyepnum $episodenum $synopsis]
+ }
+
+ if {$seriesnum} {
+ set tvdb_method "series and synopsis"
+ return [$v episodebyseries $seriesnum $synopsis]
+ }
+
+ # Most difficult - try and match based on synopsis alone
+ set tvdb_method "synopsis text"
+ return [$v episodebysynopsis $synopsis]
+}
+
+proc {ts serieslist} {dir} {
+ set idfile "$dir/.tvdbseriesid"
+ if {![file exists $idfile]} { return {} }
+ return [lmap i [split [file read $idfile] "\n"] {
+ string trim $i
+ }]
+}
+
+ts method extract_numbers {} {
+
+ ######################################################################
+ # Check for embedded Series/Episode number.
+ # Thank you broadcasters for the variation!
+ # Least trustworthy first.
+
+ # Episode 5
+ regexp -nocase -- {Episode (\d+)} $synopsis x episodenum
+
+ # ^23/27.
+ regexp -nocase -- {^\s*(\d+)/(\d+)} $synopsis x episodenum episodetot
+
+ # (8/8)
+ regexp -nocase -- {\((\d+)/(\d+)\)} $synopsis x episodenum episodetot
+
+ # (Episode 5/10)
+ # (Ep5/10)
+ # (Ep 3 of 3)
+ # (Ep3)
+ regexp -nocase -- {Epi?s?o?d?e?\s*(\d+)\s*(of|/)?\s*(\d+)?} $synopsis \
+ x episodenum x episodetot
+
+ # (S2 Ep1)
+ # S.02 Ep.002
+ # S01 Ep52
+ # (S4 Ep 7)
+ # (S1, ep 2)
+ # (S8, Ep2)
+ # (S4 Ep22/24)
+ regexp -nocase -- {S\.*(\d+),?\s*Ep\.?\s*(\d+)(/(\d+))?} $synopsis \
+ x seriesnum episodenum x episodetot
+
+ # (Part 5/10)
+ # (Pt. 5/10)
+ # (Part 5 of 10)
+ # (Pt. 5 of 10)
+ # (Pt5)
+ regexp -nocase -- {P(art|t\.?)\s*(\d+)\s*(of|/)?\s*(\d+)?} $synopsis \
+ x x episodenum x episodetot
+
+ foreach v {seriesnum episodenum episodetot} {
+ if {[set $v] eq ""} {
+ set $v 0
+ } else {
+ incr $v 0
+ }
+ }
+
+}
+
+ts method episode_name {} {
+ set s $synopsis
+
+ ######################################################################
+ # Attempt to determine the episode name from the synopsis
+
+ # Strip common prefixes
+ foreach prefix $::ts::episode_prefixes {
+ regsub -nocase -all -- $prefix $s "" s
+ }
+
+ # Strip anything following a colon.
+ regsub -all -- { *[:].*$} $s "" s
+
+ # If the resulting string is longer than 40 characters then
+ # split around . and take the left hand side if appropriate.
+ if {[string length $s] > 40} {
+ lassign [split $s "."] v w
+ set s $v
+ if {[string length $s] < 6 && [string length $w] < 6} {
+ append s "_$w"
+ }
+ }
+
+ # Shorten if too long.
+ if {[string length $s] > 40} { set s [string range $s 0 39] }
+
+ set episodename $s
+
+ if {$episodenum == 0} {
+ $self extract_numbers
+ set seriescached 0
+ }
+
+ # Now see if TVDB has anything to add
+
+ set fbase "[file dirname $file]/.tvdb"
+
+ if {![system has tvdb] || ![file exists "${fbase}seriesid"]} {
+ return $s
+ }
+
+ if {!$seriesnum && [file exists "${fbase}series"]} {
+ set seriesnum [string trim [file read "${fbase}series"]]
+ }
+ foreach seriesid [ts serieslist [$self dir]] {
+ set tvdb_data [$self _tvdb_resolve $seriesid]
+ if {![dict exists $tvdb_data name]} continue
+ set flag 0
+ if {!$seriesnum} {
+ set seriesnum $tvdb_data(series)
+ incr flag
+ }
+ if {!$episodenum} {
+ set episodenum $tvdb_data(episode)
+ incr flag
+ }
+ if {$flag} { $self storeepisode }
+
+ return $tvdb_data(name)
+ }
+
+ return $s
+}
+
+ts method epstr {{format "s%se%E/%n"}} {
+ set map {}
+ if {$seriesnum eq 0} {
+ set map(%s) "?"
+ set map(%S) "??"
+ } else {
+ set map(%s) $seriesnum
+ set map(%S) [format "%02d" $seriesnum]
+ }
+ if {$episodenum eq 0} {
+ set map(%e) "?"
+ set map(%E) "??"
+ } else {
+ set map(%e) $episodenum
+ set map(%E) [format "%02d" $episodenum]
+ }
+ if {$episodetot eq 0} {
+ set map(%n) "?"
+ set map(%N) "??"
+ } else {
+ set map(%n) $episodetot
+ set map(%N) [format "%02d" $episodetot]
+ }
+ return [string map $map $format]
+}
+
+ts method tsr {} {
+ set fd [open "[file rootname $file].nts"]
+ set bytes [read $fd 0x20]
+ close $fd
+ set tsr [unpack $bytes -uintle $(8 * 0x1f) 8]
+ return $tsr
+}
+
+ts method genrenib {} {
+ if {[catch {set v $($genre >> 4)}]} { set v 0 }
+ return $v
+}
+
+ts method genre_info {} {
+ set g [$self genrenib]
+ lassign $::epg::genrelist($g) txt img
+ if {$img eq "Unclassified"} {
+ set img "/images/173_3_26_G3_$img.png"
+ } else {
+ set img "/images/173_3_00_G3_$img.png"
+ }
+ return [list $txt $img]
+}
+
+proc {ts genrelist} {} {
+ require epg.class
+ set glist {}
+ foreach {k v} $::epg::genrelist {
+ lappend glist $($k << 4) $v
+ }
+ return $glist
+}
+
+# return the key that will decrypt the file in the mode, or nothing
+ts method getkey {mode} {
+ # mode: dlna (active key), direct
+
+ set rfile [file rootname $file]
+
+ set keys {}
+ # the active key
+ set key [string range [system nugget cryptokey -key] 0 31]
+ if {$key ne ""} {
+ lappend keys $key
+ }
+ if { $mode ne "dlna" } {
+ # also try other keys, such as this - same as active?
+ set key [system customencryptionkey]
+ if {$key ne ""} {
+ ladd keys $key
+ }
+
+ # the native key
+ if {![catch {set key [system encryptionkey]}]} {
+ ladd keys $key
+ }
+
+ # support a file listing other keys in hex, one-per-line
+ # eg, for recordings imported from a broken box
+ try {
+ set fd [open "/mod/etc/keys" r]
+ foreach key [split [$fd read -nonewline] "\n"] {
+ ladd keys $key
+ }
+ } on error {} {
+ } finally {
+ catch {$fd close}
+ }
+ }
+
+ foreach key $keys {
+ if {[catch {
+ set ret [exec /mod/bin/stripts -q/ $key $rfile]
+ }]} continue
+ if {$ret eq "1"} {
+ return $key
+ }
+ }
+ return
+}
+