diff --git a/webif/html/browse/download.jim b/webif/html/browse/download.jim index 41447e7..dcf113f 100755 --- a/webif/html/browse/download.jim +++ b/webif/html/browse/download.jim @@ -10,7 +10,7 @@ set urlbase [cgi_get base ""] # Default to just downloading the raw file. set url $file -set mime "video/ts" +set mime "video/mp2t" if {[string match {*.ts} $file]} { if {![catch {set ts [ts fetch $file]}]} { diff --git a/webif/html/browse/file.jim b/webif/html/browse/file.jim index e35e86d..17527bf 100755 --- a/webif/html/browse/file.jim +++ b/webif/html/browse/file.jim @@ -13,10 +13,14 @@ if {$file == 0} exit set sz [pretty_size [file size $file]] +set flags {} +set url "" + +# assumption: the type is only ts if fetch has already been checked if {$type eq "ts"} { require epg.class ts.class - set ts [ts fetch $file] + set ts [ts fetch $file 1] # Causes other series information to be automatically populated set epname [$ts episode_name] @@ -199,7 +203,7 @@ eval_plugins browsetsfile puts " Flags - [$ts get flags] + [set flags [$ts get flags]] " @@ -215,9 +219,6 @@ if {[$ts get bookmarks]} { " } -puts " - -" puts "" puts { } - exit } # Otherwise, for a general file. -puts " +if {$type ne "ts"} { + puts " @@ -269,20 +270,33 @@ puts " - +" +} + +set hasffmpeg 0 +if {$type ne "ts" || ("ODEncrypted" ni $flags && $url eq "") } { + puts " - -
File
Size $sz
Info Loading...
-" - -set url "/browse/ffmpeg.jim?file=[cgi_quote_url $file]" -puts { +" + set hasffmpeg 1 +} elseif {$type eq "ts" && $url ne ""} { + puts [format { + } $url] } +puts " + + " +if {$hasffmpeg} { + set url "/browse/ffmpeg.jim?file=[cgi_quote_url $file]" + puts [format { + } $url] +} diff --git a/webif/html/browse/index.jim b/webif/html/browse/index.jim index be17858..587f67e 100755 --- a/webif/html/browse/index.jim +++ b/webif/html/browse/index.jim @@ -21,7 +21,7 @@ header set nicesplice [system pkginst nicesplice] set ignore {.nts .thm .hmi} -set include {.ts .avi .mpg .mpeg .wmv .mkv .mp3 .mp4 .mov .hmt .m4v .m4a} +set include {.ts .avi .mpg .mpeg .wmv .mkv .mp3 .mp4 .mov .hmt .m4v .m4a .webm} if {![dict exists $env SCRIPT_NAME]} { set env(SCRIPT_NAME) "" @@ -139,6 +139,14 @@ proc entry {file} {{i 0}} { set img Video_Failed } set omenu opt + if {[$ts get definition] eq ""} { + set type gen + set ts 0 + set img Video_Other + set omenu oopt + } else { + set omenu opt + } if {[file exists "${base}.thm"]} { set thmok 1 } } elseif {$ext eq ".hmt"} { if {[file exists "${base}.ts"]} { return } @@ -207,8 +215,8 @@ proc entry {file} {{i 0}} { # Indexed set dlna 0 - if {$::dlnaok && $::model eq "HDR" && [llength [ - system dlnaurl [file normalize $file]]]} { + if {$::dlnaok && $::model eq "HDR" && + [llength [system dlnaurl $file]]} { icon "/img/dlna.png" "Indexed by DLNA Server" set dlna 1 } diff --git a/webif/html/browse/play.jim b/webif/html/browse/play.jim new file mode 100755 index 0000000..3eb8dc3 --- /dev/null +++ b/webif/html/browse/play.jim @@ -0,0 +1,62 @@ +#!/mod/bin/jimsh + +package require cgi +source /mod/webif/lib/setup +require system.class +require ts.class + +set file [cgi_get file] +set urlbase [cgi_get base ""] +set duration [cgi_get duration 1] +set fmts [split [cgi_get fmts ""] ","] +set vc [cgi_get vc ""] + +# Default to just downloading the raw file. +set url $file + +# Prefer to use DLNA server ... (necessary if encrypted) +set dlna [system dlnaurl $url $urlbase] +if {[llength $dlna]} { + set url [lindex $dlna 0] +} elseif {[regexp {^(https?://(.+:.*@)?[[:alnum:].]+(:[[:digit:]]+)?)/} $urlbase x y]} { + set url "$y$url" +} else { + set url "http://[system ip]$url" +} + +if {[file extension $file] in {.ts .TS}} { + if {![catch {set ts [ts fetch $file]}] && $ts != 0} { + set duration [$ts duration 1] + } +} + +set file [file tail $file] +set playlist [file tempfile "[env "TMPDIR" [env "TMP" "/tmp"]]/playXXXXXX"] +set pl "" +try { + set pl [open $playlist w] + $pl puts "#EXTM3U" + $pl puts "#EXTINF:$duration,$file" + $pl puts "#PLAYLIST:$file" + $pl puts $url +} finally { + if {$pl ne ""} { + $pl close + } +} + +httpheader "application/x-mpegurl" 0 [list \ + "Content-Disposition" "attachment; filename=\"[file rootname $file].m3u\"" \ + "Content-Length" "[file size $playlist]" \ + ] +set pl "" +try { + set pl [open $playlist r] + $pl copyto stdout +} finally { + if {$pl ne ""} { + $pl close + } +} +catch {file delete $playlist} + diff --git a/webif/html/browse/script.js b/webif/html/browse/script.js index 5269fa0..11bb2cd 100755 --- a/webif/html/browse/script.js +++ b/webif/html/browse/script.js @@ -10,7 +10,7 @@ var plugins = { dmenu_prepare: {} }; -// pattern matches directory path prefix +// pattern matches directory path prefix and suffix var pathre = /.*\/|\.[^.]*$/g; // IDs of size, img elements for folders use RFC4648 s5 encoding of name @@ -787,12 +787,14 @@ $('img.doopt').contextMenu( // Disable items which are not yet implemented. $('#optmenu').disableContextMenuItems('#title'); -var $buttons = { - "Close" : function() {$(this).dialog('close');} -}; -var $buttonsp = $.extend( - {"Play" : function() { doplay(); }}, - $buttons); +var $buttons = [ + { id: 'close', + text: 'Close', + click: function() {$(this).dialog('close');}}, + { id: 'play', + text: 'Play', + click: function() { doplay(this); }} +]; // Create reusable dialogue. var $dialog = $('#dialogue').dialog({ @@ -806,16 +808,56 @@ var $dialog = $('#dialogue').dialog({ 'Retrieving data...'); } }); -function doplay() +/* insert button-like Download link before Play */ +$('#play').before(function(i){ + var dl = document.createElement('a'); + dl.setAttribute('class', this.className); + dl.id = this.id + 'DL'; + dl.innerHTML = 'Download'; + return dl; +}); + +function doplay(it) { var file = $dialog.attr('file'); var type = $dialog.attr('type'); - disableall(); + var duration = 0; + var fmts = ""; + var vc = "" + var ff = $('#ffmpeg')[0]; + + if (ff) { + /* extract duration, container and video codec from ffmpeg output */ + ff = ff.innerHTML; + var match = /Duration:\s+([0-9.:]+),/.exec(ff); + if (match && match[1]) + duration = (new Date('1970-01-01T' + match[1] + 'Z')).getTime()/1000; + match = /Input #0,\s+([-A-Za-z0-9_,]+),\s/.exec(ff); + if (match && match[1]) fmts = match[1]; + match = /Stream #.+\sVideo:\s+([-A-Za-z0-9_]+)\s/.exec(ff); + if (match && match[1]) vc = match[1]; + } - window.location = '/play/play.jim?' + - 'dir=' + encodeURIComponent(dir) + - '&file=' + encodeURIComponent(file); + fmts = /mp4|webm/.exec(fmts); + if (fmts && fmts[0]) + vc = /h264|av1|vp9/.exec(vc); + else + vc = null; + + if (vc && vc[0]) { + /* base on page address to handle client on external network, etc */ + var hh = new URL(file, window.location.href); + window.open(hh.href, 'WebIf_Player'); + } else { + window.location = '/browse/play.jim?' + + 'dir=' + encodeURIComponent(dir) + + '&base=' + encodeURI(window.location.hostname) + + '&duration=' + duration + + '&file=' + encodeURIComponent(file); + } + + $(it).dialog('close'); } // Bind dialogue open to filenames. @@ -834,11 +876,24 @@ $('a.bf').click(function(e) { $dialog.attr('file', file); $dialog.attr('type', type); - if (type == 'ts' && - (opt.attr('odencd') == 0 || opt.attr('dlna') == 1)) - $dialog.dialog("option", "buttons", $buttonsp); - else - $dialog.dialog("option", "buttons", $buttons); + $('#playDL').attr('download', file.replace(/.*\//, '')); + + if (type == 'ts') { + if (opt.attr('odencd') != 0) { + /* encrypted: link to be enabled once populated */ + $('#playDL').disable(); + /* ... but if no DLNA never Play */ + if (opt.attr('dlna') != 1) $('#play').disable(); + } else { + /* link unencrypted file directly */ + $('#playDL').attr('href', file); + } + } else { + /* generic: enable Play once media file is parsed */ + $('#play').disable(); + $('#playDL').attr('href', file); + } + $dialog.dialog('open'); }); diff --git a/webif/html/browse/style.css b/webif/html/browse/style.css index d2e2fa3..93e63e4 100644 --- a/webif/html/browse/style.css +++ b/webif/html/browse/style.css @@ -35,3 +35,13 @@ input.uint8_t width: 6ch; } +/* a link that looks like a button */ +button + a.ui-button +{ + margin-right: 0.4em; +} +.ui-button:link +{ + background-color: rgb(254, 206, 47) !important; +} + diff --git a/webif/lib/setup b/webif/lib/setup index 82f17a0..4ae354b 100644 --- a/webif/lib/setup +++ b/webif/lib/setup @@ -33,16 +33,24 @@ if {![exists -proc require]} { exit } - proc httpheader {{type "text/html"} {cache 0} {extra ""}} {{done 0}} { + proc httpheader {{type "text/html"} {cache 0} {extra {}}} {{done 0}} { if {$done} return if {!$cache} { - puts -nonewline "Content-Type: $type; charset=\"UTF-8\"; no-cache\r\n" - puts -nonewline "Expires: -1\r\n" - puts -nonewline "Connection: close\r\n" - puts -nonewline "Pragma: no-cache\r\n" - puts -nonewline "Cache-Control: no-cache\r\n" + set hdr [dict create \ + "Content-Type" "$type; charset=\"UTF-8\"; no-cache" \ + "Expires" "-1" \ + "Connection" "close" \ + "Pragma" "no-cache" \ + "Cache-Control" "no-cache"] } else { - puts -nonewline "Content-Type: $type; charset=\"UTF-8\"\r\n" + set hdr [dict create "Content-Type" "$type; charset=\"UTF-8\""] + } + if {![catch {dict size $extra}]} { + set hdr [dict merge $hdr $extra] + set extra "" + } + dict for {k v} $hdr { + puts -nonewline "$k: $v\r\n" } if {$extra ne ""} { puts -nonewline "$extra" } puts -nonewline "\r\n" diff --git a/webif/lib/system.class b/webif/lib/system.class index e0ffb9a..db3ce76 100644 --- a/webif/lib/system.class +++ b/webif/lib/system.class @@ -269,7 +269,11 @@ proc {system dlnadb} {} { } proc {system _dlnaurl} {file urlbase} { - set mime "video/ts" + 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 {} } @@ -278,7 +282,10 @@ proc {system _dlnaurl} {file urlbase} { from tblresource join tblmedia using (mediaid) where localurl = '%s'} $file] if {[llength $muri]} { - lassign [lindex $muri 0] x mime x xuri + 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 { @@ -297,7 +304,7 @@ proc {system _dlnaurl} {file urlbase} { return [list $url $mime] } -proc {system dlnaurl} {file {urlbase ""}} { +proc {system dlnaurl} {file {urlbase "127.0.0.1"}} { if {$urlbase eq ""} { set urlbase [system ip] } set retries 5 set ret {} diff --git a/webif/lib/ts.class b/webif/lib/ts.class index 6eb0b51..d4de7ff 100644 --- a/webif/lib/ts.class +++ b/webif/lib/ts.class @@ -331,7 +331,7 @@ ts method setgenre {newgenre} { } ts method dlnaloc {{urlbase ""}} { - return [system dlnaurl [file normalize $file] $urlbase] + return [system dlnaurl $file $urlbase] } ts method cleanbmp {} {