webif/var/mongoose/lib/bin/auto

386 lines
8.3 KiB
Plaintext
Raw Normal View History

#!/mod/bin/jimsh
source /mod/webif/lib/setup
require lock system.class ts.class tdelete pretty_size
set debug 0
if {![acquire_lock webif_auto]} {
puts "Cannot acquire exclusive lock, terminating."
exit
}
set logfile "/mod/tmp/auto.log"
# Rotate log file if large enough.
if {[file exists $logfile] && [file size $logfile] > 2097152} {
file copy -force $logfile "/mod/tmp/auto_old.log"
file delete $logfile
}
set logfd [open "/mod/tmp/auto.log" "a+"]
proc log {msg {always 0}} {
if {!$::debug && !$always} return
puts $::logfd "[\
clock format [clock seconds] -format "%d/%m/%Y %H:%M"\
] - $msg"
flush $::logfd
}
proc elapsed {start} {
return $(([clock milliseconds] - $start) / 1000.0)
}
proc startclock {} {
set ::startclock_s [clock milliseconds]
}
proc endclock {size} {
set el [elapsed $::startclock_s]
set rate $($size / $el)
return "[pretty_size $size] in $el seconds - [pretty_size $rate]/s"
}
set scanstart [clock milliseconds]
log "-------------------------------------------------------" 1
# is_listening is relatively expensive so it is checked once globally at
# the start and then if the server is not listening then no decrypt
# operations will be attempted for this run, even if the server starts
# up halfway through. Otherwise the server is checked for every decryption
# and if it goes away then decryption will not be attempted for the rest
# of the run.
if {[system is_listening 9000]} {
set dlnaok 1
if {$::debug} { log "DLNA Server is running." }
} else {
set dlnaok 0
if {$::debug} { log "DLNA Server is NOT running." }
}
log "Media scan starting, DLNA server status: $dlnaok" 1
proc dsc {{size 0}} {
set free [system diskfree]
# Required disk space is 1GiB + 3 times the file size.
set req $($size * 3 + 1073741824)
if {$free < $req} {
log "Insufficient disk space. Require=$req, Free=$free" 1
exit
}
}
dsc
set tmp "/mod/tmp/webif_auto"
if {![file exists $tmp]} {
if {[catch {file mkdir $tmp} msg]} {
log "Cannot create temporary directory - $tmp ($msg)" 1
exit
}
} elseif {![file isdirectory $tmp]} {
log "Cannot create temporary directory - $tmp (file exists)" 1
exit
}
# Clean-up the temporary directory
foreach file [readdir -nocomplain $tmp] { tdelete "$tmp/$file" }
if {[system pkginst undelete]} {
set dustbin "[system dustbin]"
} else {
set dustbin ""
}
log "Dustbin: $dustbin"
proc bindir {file binroot} {
set dir [file dirname $file]
regsub "^[system mediaroot]" $dir $binroot ndir
if {$dir eq $ndir} { set ndir $binroot }
system mkdir_p $ndir
return $ndir
}
proc dedup {dir} {
log "DEDUP: \[$dir]"
loop i 0 2 {
foreach line [split \
[exec /mod/webif/html/dedup/dedup -yes -auto $dir] "\n"] {
log $line 1
}
}
}
proc do_shrink {ts} {
global tmp dustbin tsgroup
set file [file rootname [$ts get file]]
if {[catch {
set perc [exec /mod/bin/stripts -aq $file]
} msg]} {
log " Error: $msg" 1
return
}
if {[string match {*%} $perc]} {
set perc [string range $perc 0 end-1]
} else {
set perc 0
}
if {$perc == 0} {
log " Already shrunk."
return
}
set size [$ts size]
dsc $size
startclock
log " SHRINK: $file" 1
log " Estimate $perc% saving." 1
log " Shrinking..." 1
if {[catch {
foreach line [split \
[exec nice -n 19 /mod/bin/stripts -q $file $tmp/shrunk] \
"\n"] {
log $line 1
}
} msg]} {
log "Error during shrink: $msg" 1
return
}
# The following steps are structured to minimise the risk of
# things being left in an inconsistent state if the system goes
# into standby. Renames within the same filesystem are very
# quick so the risk is small, but even so...
# Move the shrunken version back to the local directory.
foreach f [glob "$tmp/shrunk.*"] {
set ext [file extension $f]
file rename $f "${file}_shrunk${ext}"
}
# Move the old recording to the bin if undelete is installed.
if {$dustbin ne ""} {
$ts move [bindir $file "$dustbin/webif_autoshrink"] 1 1
} else {
# Delete otherwise.
if {[$ts delete]} {
log "Successfully deleted $file."
} else {
log "Problem deleting $file, [$ts get error]" 1
return
}
}
# Finally, rename the shrunken recording again.
foreach ext $tsgroup {
set f "${file}_shrunk.$ext"
if {[file exists $f]} {
file rename $f "${file}.$ext"
}
}
log "Done... [endclock $size]" 1
}
proc do_decrypt {ts} {
global tmp dustbin
set file [$ts get file]
set rfile [file rootname $file]
set bfile [file tail $file]
if {![$ts flag "ODEncrypted"]} {
log " Already decrypted."
return
}
lassign [$ts dlnaloc] url
if {$url eq ""} {
log " Not yet indexed."
return
}
if {![system is_listening 9000]} {
log " DLNA Server not running."
set ::dlnaok 0
return
}
set size [$ts size]
dsc $size
startclock
log " DECRYPT: $rfile" 1
log " DLNA: $url" 1
exec wget -O "$tmp/$bfile" $url
if {[file size $file] != [file size "$tmp/$bfile"]} {
log " File size mismatch." 1
return
}
# Move the encrypted file out of the way.
file rename $file "$rfile.encrypted"
# Move the decrypted copy into place.
file rename "$tmp/$bfile" $file
# Patch the HMT - quickest way to get back to a playable file.
exec /mod/bin/hmt -encrypted "$rfile.hmt"
log " Removing/binning old copy."
# Move the old recording to the bin if undelete is installed.
if {$dustbin ne ""} {
set bin [bindir $file "$dustbin/webif_autodecrypt"]
set tail [file tail $rfile]
file rename "$rfile.encrypted" "$bin/$tail.ts"
foreach ext {nts hmt thm} {
if {[file exists "$rfile.$ext"]} {
file copy $rfile.$ext "$bin/$tail.$ext"
if {$ext eq "hmt"} {
# Patch the binned HMT back
exec /mod/bin/hmt +encrypted \
"$bin/$tail.hmt"
}
}
}
} else {
tdelete "$rfile.encrypted"
}
log "Done... [endclock $size]" 1
}
proc do_mpg {ts} {
global tmp tsgroup
set file [file rootname [$ts get file]]
if {[file exists $file.mpg]} {
# Already done.
return
}
if {[$ts flag "ODEncrypted"]} {
log " Not decrypted."
return
}
if {[$ts get definition] eq "HD"} {
# Cannot extract a useful MP3 from a HD recording.
return
}
dsc [$ts size]
log " MPG: $file" 1
log " Converting..." 1
if {[catch {
foreach line [split \
[exec nice -n 19 /mod/bin/ffmpeg -y -benchmark -v 0 \
-i $file.ts \
-map 0:0 -map 0:1 \
-vcodec copy -acodec copy $tmp/mpg.mpg] "\n"] {
log $line 1
}
} msg]} {
log "Error during mpg extract: $msg" 1
return
}
# Move the MPG into the local directory
file rename $tmp/mpg.mpg $file.mpg
}
proc entries {dir callback} {
foreach entry [readdir -nocomplain $dir] {
if {![string match {*.ts} $entry} continue
if {[catch {set ts [ts fetch "$dir/$entry"]}]} continue
if {$ts == 0} continue
if {[system inuse [file rootname "$dir/$entry"]]} {
log "$entry - in use\n" 1
continue
}
#dsc [file size "$dir/$entry"]
$callback $ts
}
}
proc shrink {dir} {
log "SHRINK: \[$dir]"
entries $dir do_shrink
}
proc decrypt {dir} {
log "DECRYPT: \[$dir]"
if {$::dlnaok} { entries $dir do_decrypt }
}
proc mpg {dir} {
log "MPG: \[$dir]"
entries $dir do_mpg
}
proc scan {dir attr {force 0}} {{indent 0}} {
global dustbin
incr indent 2
log "[string repeat " " $indent]\[$dir]"
if {$dir eq $dustbin} {
log "Dustbin, skipping."
return
}
if {[string match {\[*} [file tail $dir]]} {
# Special folder
file stat "$dir/" st
if {$st(dev) != $::rootdev} {
log "Special folder on different device, skipping."
return
}
if {$force} {
set force 0
log "Special folder, overriding recursion."
}
}
# Recursion
if {!$force && [file exists "$dir/.auto${attr}r"]} {
log "[string repeat " " $indent] (R)"
set force 1
}
dsc
if {$force || [file exists "$dir/.auto$attr"]} { $attr $dir }
foreach entry [readdir -nocomplain $dir] {
if {[file isdirectory "$dir/$entry"]} {
scan "$dir/$entry" $attr $force
}
}
incr indent -2
}
set root [system mediaroot]
file stat "$root/" rootstat
set rootdev $rootstat(dev)
#log "Root device: $rootdev" 1
if {[llength $argv] > 0} {
if {[lindex $argv 0] eq "test"} { set debug 1 }
foreach arg $argv { scan $root $arg }
} else {
foreach arg {dedup decrypt shrink mpg} {
set st [clock milliseconds]
scan $root $arg
log "$arg scan completed in [elapsed $st] seconds." 1
}
}
release_lock webif_auto
log "Media scan completed in [elapsed $scanstart] seconds." 1