variable compat "2.1.b3" # extract.itcl - avd-29-02-2004 # # A modified version of Riley's extraction module # assumes working MFS_FTP standard installation. # standard disclaimer: don't bother Riley if it dosen't work # support here: http://www.dealdatabase.com/forum/showthread.php?t=32049 ########################################################################################### ## rc3105's ULTRA simple module for extracting recordings # ## via tivoweb. requires TiVoWebPlus 1.0rc4 or newer # ## registers file handlers for tmf/ty & produces links # # apologies to => # copyright (C) 2002-2004 Riley Cassel (rc3105@dealdatabase.com/forum) # ## ######################################################################## ## tmf/ty/asx links produce {title}{episode}{channel}{fsid}.tmf/ty/asx ## ty is actually ty+ (with xml appended) & insertable via mfs_ftp ## # Modifications by BTUx9 for TWP2.2 # - unified tmf and ty support # - improved filesize calculation (files are no longer truncated under normal circumstances) # - added support for .part.ty files to get single parts (from mfsbrowser, primarily) # Modifications by SteveT # - removed note about making TWP unresponsive (since BTUx9 fixed that above) # - changed mfs_tarstream checks and calls to call mfs_uberexport directly # - added bignum support for large file sizes proc fsid_from_name { rec_name } { set id "" regexp {\{([0-9]+)\}[^\{]*$} $rec_name dum id return $id } aproc .asx { #~ global db eval_env set fsid [fsid_from_name $path] set title [file root [file tail $path]] if { $title != "" } { print_html_header_200 $chan "video/x-ms-asx" $last_modified puts $chan "" puts $chan "TiVoWeb ASX file" puts $chan "" puts $chan "$title" puts $chan "TiVo Stream" puts $chan "" puts $chan "" puts $chan "" } else { #~ print_html_header_404 $chan return 404 } } ### djl - add support for streaming to vlc 0.9+ from mfs_ftp aproc .pls { eval_env catch {set p [exec ps | grep "mfs_ftp" | grep -v grep]} if {![info exists p]} { puts $chan [html_start][tag h3 "mfs_ftp not currently running"] return 0 } set fsid [fsid_from_name $path] set title [file root [file tail $path]] if { $title != "" } { print_html_header_200 $chan "audio/x-scpls" $last_modified puts -nonewline $chan "ftp://$::ip_address:3105/ty/$fsid.ty" } else { return 404 } } variable mfs_bin_dir 0 if [has mfs_uberexport] { set mfs_bin_dir "" } else { foreach dir {hack/MFS_FTP hack/mfs_ftp mfs_ftp} { if {[file isdirectory "/var/$dir"]} { set mfs_bin_dir "/var/$dir/" break } } } #puts mfs_bin_dir if {$mfs_bin_dir != 0} { proc action_extract {chan path envr} { # variable mfs_path #DJL replaced with global value #RetryTransaction { set version [dbobj [db $::db open "/SwSystem/ACTIVE"] get Name] } #DJL { [string range $version 0 0] < "3" } set version $::version if { $version < "3" } { set mfs_path "/Recording/NowShowing" } else { set mfs_path "/Recording/NowShowingByClassic" } if {[string index $path 0] == "/"} { set path [string range $path 1 end] } if {$path == ""} { puts $chan [html_start "Download/Extract TMF/TY/ASX"] puts $chan [html_table_start "class='sortable'" "" ""] puts $chan [tr "" [th "\\[Date Recorded\]\"] [th "Title: Episode \(Chan\)"] [th "align=right" "Size(MB)"] [th "tmf"] [th "ty"] [th "asx"] [th "vlc"]] logit 0 mfs_path ForeachMfsFile fsid name type "$mfs_path" "" { foreach {r_name d_name s_name streamsize} [build_rec_info $fsid] break set rowstr "" append rowstr [td $d_name] [td [html_link "/$r_name.ty" "$s_name"]] [td "align=right" " $streamsize"] append rowstr [td [html_link "/$r_name.tmf" "tmf"]] [td [html_link "/$r_name.ty" "ty"]] [td [html_link "/$r_name.asx" "asx"]] [td [html_link "/$r_name.pls" "vlc"]] puts $chan [tr "" $rowstr] } puts $chan [html_table_end] } } proc build_rec_info rec_fsid { global tzoffset foreach var "title callsign episode recdate r_name d_name s_name streamsize" {set $var "" } RetryTransaction { set rec [db $::db openid $rec_fsid] set showing [dbobj $rec get Showing] set streamsize [dbobj $rec get StreamFileSize] set program [dbobj $showing get Program] set title [sn [strim [dbobj $program get Title]]] set Date [dbobj $showing get Date] set Time [dbobj $showing get Time] catch { set episode [sn [strim [dbobj $program get EpisodeTitle]]] } catch { set station [dbobj $showing get Station] } catch { set callsign [sn [strim [dbobj $station get CallSign]]] } } # Find and set the Record Time and Date if { $Date != "" } { set recsec [expr $Date * 86400 + $Time + $tzoffset] } else { set recsec "" } set recdate "[clock format $recsec -format "%m/%d/%Y %I\:%M%p"]" if {$streamsize != ""} { set streamsize [expr $streamsize / 1024] } set r_name "\{$title\}\{$episode\}\{$callsign\}\{$rec_fsid\}" set d_name "\$recdate\" set s_name "$title: $episode \\($callsign\)\" return [list $r_name $d_name $s_name $streamsize] } ######## # XML Recording Object Dumper by the glorious embeem # updated by rc3105 proc dump_object { obj {id ""} {depth 10} {prefix ""}} { # variable info set p 4 #outd $p "dump_object: \"$obj\" \"$id\" \"$depth\" \"$prefix\" " if { $depth == 0 } {return "** max depth **\n"} set output "" ; set fsid [dbobj $obj fsid] ; set subid [dbobj $obj subobjid] if { [info exists ::seen($fsid/$subid)] } { return {} } set ::seen($fsid/$subid) "$id" set otype [dbobj $obj type] if {[dbobj $obj primary]} { append output "$prefix\n" set endtag "" } else { append output "$prefix\n" set endtag "" } foreach attr [lsort [dbobj $obj attrs]] { if { [catch { dbobj $obj get $attr } attrs] } { #outd 1 "dump_obj: whoops, wacky db attr \"$attr\"" } else { set atype [dbobj $obj attrtype $attr] switch -glob $attr { AuxInfos - ActualShowing - CancelReason - Deletion* - Expiration* - ErrorString - Index* - NSecondsWatched - NVisit - ProgramSource - RecordingBehavior - Score - State - Version {continue} } foreach a $attrs { switch $atype { object { append output [dump_object $a $attr [expr $depth-1] "$prefix "]; set a "" } } if { $a != "" } { append output "$prefix <$attr>$a\n" } } } } append output "$prefix$endtag\n" return "$output" } proc dump_xml { obj } { set version [dbobj [db $::db open "/SwSystem/ACTIVE"] get Name] set output "\n" append output [dump_object $obj "_top"] unset ::seen return $output } proc print_errmsg {chan msg} { puts $chan "HTTP/1.1 200 Ok" puts $chan "Server: tivoweb/fun\nConnection: close" puts $chan "Accept-Ranges: bytes" puts $chan "Content-type: text/html" puts $chan "" puts $chan $msg } proc isInt { val } { return [regexp {^-?[1-9][0-9]*$|^-?0[xX][0-9a-fA-F]+$|^-?0[0-7]*$} $val] } ####### # file handlers serve_tmf & serve_ty based on tivodvlpr's ty-tar server aproc .tmf { lappend env tmf 1 return [action_.ty $chan $path $env] } aproc .ty { variable mfs_bin_dir set tmf 0 eval_env set filename [file tail $path] set fsid [file root [file tail $filename]] set is_part [string match *.part.ty $path] set xml "" if $tmf { #~ set hsize 512 set h "" } else { #~ set hsize 0 # set h "#" # for {set i 10} [incr i -1] {} {append h $h} # 6.3 system didn't like above syntax set h "" for {set i 1} {$i <= 512} {incr i} {append h "#"} } set fsid [fsid_from_name $fsid] logit fsid set total_size 0 try { RetryTransaction { if $is_part { set part_files $fsid; set xml "" } else { set part_files {} set rec [db $::db openid $fsid] set xml "$h[dump_xml $rec]$h" set parts [dbobj $rec get Part] foreach part $parts { set file [dbobj $part get File] lappend part_files $file } } foreach f $part_files { foreach {blocksize blocks} [mfs streamsize $f] break set total_size [bignum_add $total_size [expr $blocks * $blocksize]] } } } catch errCode {logit errCode; return 404} set xsize [string length $xml] if $tmf { # add in tar headers and round up xml to blocksize (512) set xsize [expr (($xsize+1023) & 0xfffffe00)] set hsize 512 } else { #~ incr xsize 1024 set hsize 0 } set total_size [bignum_add $total_size $xsize] set total_size [bignum_add $total_size [expr (128*1024+$hsize)*[llength $part_files]]] puts $chan "HTTP/1.1 200 Ok" puts $chan "Server: tivoweb/fun\nConnection: close" puts $chan "Accept-Ranges: none" #~ puts $chan "Accept-Ranges: bytes" puts $chan "Content-Length: $total_size" puts $chan "Content-type: application/[lindex {ty tmf} $tmf]" puts $chan "" set xmlf "/tmp/$fsid.xml" set pout [open $xmlf w] puts -nonewline $pout $xml close $pout if $tmf { exec bash -c "${mfs_bin_dir}mfs_uberexport -t -x $part_files" <$xmlf >@$chan & } else { exec bash -c "${mfs_bin_dir}mfs_uberexport -R $fsid ; cat $xmlf" >@$chan & } return 0 } proc sn { line } { regsub -all -nocase {[^A-Z0-9_~@\#&\-\'{ }]} $line {} new_line return $new_line } #setup_em_vars register_module "extract" "Extract" "Download/Stream recordings as TMF/TY/ASX" }