view release on metacpan or search on metacpan
lib/MHFS/HTTP/Server/Client/Request.pm view on Meta::CPAN
use Symbol 'gensym';
use Devel::Peek;
use Encode qw(decode encode);
use constant {
MAX_REQUEST_SIZE => 8192,
};
use FindBin;
use File::Spec;
use MHFS::EventLoop::Poll;
use MHFS::Process;
use MHFS::Util qw(get_printable_utf8 LOCK_GET_LOCKDATA getMIME shell_escape escape_html_noquote parse_ipv4);
BEGIN {
if( ! (eval "use JSON; 1")) {
eval "use JSON::PP; 1" or die "No implementation of JSON available";
warn __PACKAGE__.": Using PurePerl version of JSON (JSON::PP)";
}
}
# Optional dependency, Alien::Tar::Size
BEGIN {
use constant HAS_Alien_Tar_Size => (eval "use Alien::Tar::Size; 1");
lib/MHFS/HTTP/Server/Client/Request.pm view on Meta::CPAN
say "RECV: $rl";
if(($self->{'method'} ne 'GET') && ($self->{'method'} ne 'HEAD') && ($self->{'method'} ne 'PUT')) {
say "X-MHFS-CONN-ID: " . $self->{'outheaders'}{'X-MHFS-CONN-ID'} . 'Invalid method: ' . $self->{'method'}. ', closing conn';
return undef;
}
my ($path, $querystring) = ($self->{'uri'} =~ /^([^\?]+)(?:\?)?(.*)$/g);
say("raw path: $path\nraw querystring: $querystring");
# transformations
## Path
$path = uri_unescape($path);
my %pathStruct = ( 'unescapepath' => $path );
# collapse slashes
$path =~ s/\/{2,}/\//g;
say "collapsed: $path";
$pathStruct{'unsafecollapse'} = $path;
# without trailing slash
if(index($pathStruct{'unsafecollapse'}, '/', length($pathStruct{'unsafecollapse'})-1) != -1) {
chop($path);
say "no slash path: $path ";
lib/MHFS/HTTP/Server/Client/Request.pm view on Meta::CPAN
## Querystring
my %qsStruct;
# In the querystring spaces are sometimes encoded as + for legacy reasons unfortunately
$querystring =~ s/\+/%20/g;
my @qsPairs = split('&', $querystring);
foreach my $pair (@qsPairs) {
my($key, $value) = split('=', $pair);
if(defined $value) {
if(!defined $qsStruct{$key}) {
$qsStruct{$key} = uri_unescape($value);
}
else {
if(ref($qsStruct{$key}) ne 'ARRAY') {
$qsStruct{$key} = [$qsStruct{$key}];
};
push @{$qsStruct{$key}}, uri_unescape($value);
}
}
}
$self->{'path'} = \%pathStruct;
$self->{'qs'} = \%qsStruct;
$self->{'on_read_ready'} = \&want_headers;
#return want_headers($self);
goto &want_headers;
}
lib/MHFS/HTTP/Server/Client/Request.pm view on Meta::CPAN
my $disposition = 'inline';
if($opt->{'attachment'}) {
$disposition = 'attachment';
$filename = $opt->{'attachment'};
}
elsif($opt->{'inline'}) {
$filename = $opt->{'inline'};
}
if($filename) {
my $sendablebytes = encode('UTF-8', get_printable_utf8($filename));
$headtext .= "Content-Disposition: $disposition; filename*=UTF-8''".uri_escape($sendablebytes)."; filename=\"$sendablebytes\"\r\n";
}
$self->{'outheaders'}{'Accept-Ranges'} //= 'bytes';
$self->{'outheaders'}{'Connection'} //= $self->{'header'}{'Connection'};
$self->{'outheaders'}{'Connection'} //= 'keep-alive';
# SharedArrayBuffer
if($opt->{'allowSAB'}) {
say "sending SAB headers";
$self->{'outheaders'}{'Cross-Origin-Opener-Policy'} = 'same-origin';
lib/MHFS/HTTP/Server/Client/Request.pm view on Meta::CPAN
# encodes path and querystring
# path and query string keys and values must be bytes not unicode string
sub SendRedirect {
my ($self, $code, $path, $qs) = @_;
my $url;
# encode the path component
while(length($path)) {
my $slash = index($path, '/');
my $len = ($slash != -1) ? $slash : length($path);
my $pathcomponent = substr($path, 0, $len, '');
$url .= uri_escape($pathcomponent);
if($slash != -1) {
substr($path, 0, 1, '');
$url .= '/';
}
}
# encode the querystring
if($qs) {
$url .= '?';
foreach my $key (keys %{$qs}) {
my @values;
if(ref($qs->{$key}) ne 'ARRAY') {
push @values, $qs->{$key};
}
else {
@values = @{$qs->{$key}};
}
foreach my $value (@values) {
$url .= uri_escape($key).'='.uri_escape($value) . '&';
}
}
chop $url;
}
@_ = ($self, $code, $url);
goto &SendRedirectRawURL;
}
sub SendLocalFile {
lib/MHFS/HTTP/Server/Client/Request.pm view on Meta::CPAN
return min($filelength, $tocheck);
};
$self->_SendDataItem(\%fileitem, {
'size' => $filelength,
'mime' => $mime,
'filename' => $filename
});
}
# to do get rid of shell escape, launch ssh without blocking
sub SendFromSSH {
my ($self, $sshsource, $filename, $node) = @_;
my @sshcmd = ('ssh', $sshsource->{'userhost'}, '-p', $sshsource->{'port'});
my $fullescapedname = "'" . shell_escape($filename) . "'";
my $folder = $sshsource->{'folder'};
my $size = $node->[1];
my @cmd;
if(defined $self->{'header'}{'_RangeStart'}) {
my $start = $self->{'header'}{'_RangeStart'};
my $end = $self->{'header'}{'_RangeEnd'} // ($size - 1);
my $bytestoskip = $start;
my $count = $end - $start + 1;
@cmd = (@sshcmd, 'dd', 'skip='.$bytestoskip, 'count='.$count, 'bs=1', 'if='.$fullescapedname);
}
else{
@cmd = (@sshcmd, 'cat', $fullescapedname);
}
say "SendFromSSH (BLOCKING)";
open(my $cmdh, '-|', @cmd) or die("SendFromSSH $!");
$self->SendPipe($cmdh, basename($filename), $size);
return 1;
}
# ENOTIMPLEMENTED
sub Proxy {
lib/MHFS/HTTP/Server/Client/Request.pm view on Meta::CPAN
if(index($request->{'path'}{'unsafecollapse'}, '/', length($request->{'path'}{'unsafecollapse'})-1) == -1) {
$request->SendFile($requestfile);
}
else {
$request->Send404;
}
}
# is directory
elsif (-d _) {
# ends with slash
if(index($request->{'path'}{'unescapepath'}, '/', length($request->{'path'}{'unescapepath'})-1) != -1) {
my $index = $requestfile.'/index.html';
if(-f $index) {
$request->SendFile($index);
return;
}
$request->Send404;
}
else {
# redirect to slash path
my $bn = basename($requestfile);
lib/MHFS/HTTP/Server/Client/Request.pm view on Meta::CPAN
if(index($self->{'path'}{'unsafecollapse'}, '/', length($self->{'path'}{'unsafecollapse'})-1) == -1) {
$self->SendFile($requestfile);
}
else {
$self->Send404;
}
return;
}
elsif(-d _) {
# ends with slash
if((substr $self->{'path'}{'unescapepath'}, -1) eq '/') {
opendir ( my $dh, $requestfile ) or die "Error in opening dir $requestfile\n";
my $buf;
my $filename;
while( ($filename = readdir($dh))) {
next if(($filename eq '.') || ($filename eq '..'));
next if(!(-s "$requestfile/$filename"));
my $url = uri_escape($filename);
$url .= '/' if(-d _);
$buf .= '<a href="' . $url .'">'.${escape_html_noquote(decode('UTF-8', $filename, Encode::LEAVE_SRC))} .'</a><br><br>';
}
closedir($dh);
$self->SendHTML($buf);
return;
}
# redirect to slash path
else {
$self->SendRedirect(301, basename($requestfile).'/');
return;
}
lib/MHFS/Kodi/Util.pm view on Meta::CPAN
package MHFS::Kodi::Util v0.7.0;
use 5.014;
use strict; use warnings;
use Exporter 'import';
use MHFS::Util qw(uri_escape_path_utf8 escape_html_noquote);
our @EXPORT_OK = qw(html_list_item);
sub html_list_item {
my ($item, $isdir, $label) = @_;
$label //= $item;
my $url = uri_escape_path_utf8($item);
$url .= '/?fmt=html' if($isdir);
'<li><a href="' . $url .'">'. ${escape_html_noquote($label)} .'</a></li>'
}
1;
lib/MHFS/Plugin/BitTorrent/Client/Interface.pm view on Meta::CPAN
package MHFS::Plugin::BitTorrent::Client::Interface v0.7.0;
use 5.014;
use strict; use warnings;
use feature 'say';
use MHFS::BitTorrent::Client;
use MHFS::Util qw(escape_html do_multiples get_SI_size);
use URI::Escape qw(uri_escape);
sub is_video {
my ($name) = @_;
my ($ext) = $name =~ /\.(mkv|avi|mp4|webm|flv|ts|mpeg|mpg|m2t|m2ts|wmv)$/i;
return $ext;
}
sub is_mhfs_music_playable {
my ($name) = @_;
return $name =~ /\.(?:flac|mp3|wav)$/i;
lib/MHFS/Plugin/BitTorrent/Client/Interface.pm view on Meta::CPAN
'bytes_done' => sub { MHFS::BitTorrent::Client::torrent_d_bytes_done($server, $hash, @_); },
'size_bytes' => sub { MHFS::BitTorrent::Client::torrent_d_size_bytes($server, $hash, @_); },
'name' => sub { MHFS::BitTorrent::Client::torrent_d_name($server, $hash, @_); },
}, sub {
if( ! defined $_[0]) { $request->Send404; return;}
my ($data) = @_;
my $torrent_raw = $data->{'name'};
my $bytes_done = $data->{'bytes_done'};
my $size_bytes = $data->{'size_bytes'};
# print out the current torrent status
my $torrent_name = ${escape_html($torrent_raw)};
my $size_print = get_SI_size($size_bytes);
my $done_print = get_SI_size($bytes_done);
my $percent_print = (sprintf "%u%%", ($bytes_done/$size_bytes)*100);
my $buf = '<h1>Torrent</h1>';
$buf .= '<h3><a href="../video">Video</a> | <a href="../music">Music</a></h3>';
$buf .= '<table border="1" >';
$buf .= '<thead><tr><th>Name</th><th>Size</th><th>Done</th><th>Downloaded</th></tr></thead>';
$buf .= "<tbody><tr><td>$torrent_name</td><td>$size_print</td><td>$percent_print</td><td>$done_print</td></tr></tbody>";
$buf .= '</table>';
lib/MHFS/Plugin/BitTorrent/Client/Interface.pm view on Meta::CPAN
# print out the files with usage options
MHFS::BitTorrent::Client::torrent_file_information($server, $qs->{'infohash'}, $torrent_raw, sub {
if(! defined $_[0]){ $request->Send404; return; };
my ($tfi) = @_;
my @files = sort (keys %$tfi);
$buf .= '<br>';
$buf .= '<table border="1" >';
$buf .= '<thead><tr><th>File</th><th>Size</th><th>DL</th><th>Play in browser</th></tr></thead>';
$buf .= '<tbody';
foreach my $file (@files) {
my $htmlfile = ${escape_html($file)};
my $urlfile = uri_escape($file);
my $link = '<a href="get_video?name=' . $urlfile . '&fmt=noconv">DL</a>';
my $playlink = play_in_browser_link($file, $urlfile);
$buf .= "<tr><td>$htmlfile</td><td>" . get_SI_size($tfi->{$file}{'size'}) . "</td><td>$link</td>";
$buf .= "<td>$playlink</td>" if(!defined($qs->{'playinbrowser'}) || ($qs->{'playinbrowser'} == 1));
$buf .= "</tr>";
}
$buf .= '</tbody';
$buf .= '</table>';
$request->SendHTML($buf);
lib/MHFS/Plugin/BitTorrent/Client/Interface.pm view on Meta::CPAN
while(1) {
if($curtor =~ /^\[(u?)['"](.+)['"],\s'(.+)',\s([0-9]+),\s([0-9]+),\s([0-9]+)\]$/) {
my %torrent;
my $is_unicode = $1;
$torrent{'name'} = $2;
$torrent{'hash'} = $3;
$torrent{'size_bytes'} = $4;
$torrent{'bytes_done'} = $5;
$torrent{'private'} = $6;
if($is_unicode) {
my $escaped_unicode = $torrent{'name'};
$torrent{'name'} =~ s/\\u(.{4})/chr(hex($1))/eg;
$torrent{'name'} =~ s/\\x(.{2})/chr(hex($1))/eg;
my $decoded_as = $torrent{'name'};
$torrent{'name'} = ${escape_html($torrent{'name'})};
if($qs->{'logunicode'}) {
say 'unicode escaped: ' . $escaped_unicode;
say 'decoded as: ' . $decoded_as;
say 'html escaped ' . $torrent{'name'};
}
}
$buf .= '<tr><td>' . $torrent{'name'} . '</td><td>' . $torrent{'hash'} . '</td><td>' . $torrent{'size_bytes'} . '</td><td>' . $torrent{'bytes_done'} . '</td><td>' . $torrent{'private'} . '</td></tr>';
$curtor = '';
}
else {
my $line = shift @lines;
if(! $line) {
last;
}
lib/MHFS/Plugin/GetVideo.pm view on Meta::CPAN
package MHFS::Plugin::GetVideo v0.7.0;
use 5.014;
use strict; use warnings;
use feature 'say';
use Data::Dumper qw (Dumper);
use Fcntl qw(:seek);
use Feature::Compat::Try;
use Scalar::Util qw(weaken);
use URI::Escape qw (uri_escape);
use Devel::Peek qw(Dump);
no warnings "portable";
use Config;
use MHFS::Process;
use MHFS::Util qw(space2us LOCK_WRITE round shellcmd_unlock ASYNC pid_running read_text_file write_text_file ceil_div);
sub new {
my ($class, $settings) = @_;
if($Config{ivsize} < 8) {
lib/MHFS/Plugin/GetVideo.pm view on Meta::CPAN
catch ($e) {
say "$requestfile does not exist or is not UTF-8";
''
}
};
my $subm3u;
my $newm3ucontent = '';
foreach my $line (split("\n", $m3ucontent)) {
# master playlist doesn't get written with base url ...
if($line =~ /^(.+)\.m3u8_v$/) {
$subm3u = "get_video?".$settings->{VIDEO_TMPDIR_QS}."&fmt=noconv&name=" . uri_escape("$1/$1");
$line = $subm3u . '.m3u8_v';
}
$newm3ucontent .= $line . "\n";
}
# Always start at 0, even if we encoded half of the movie
#$newm3ucontent .= '#EXT-X-START:TIME-OFFSET=0,PRECISE=YES' . "\n";
# if ffmpeg created a sub include it in the playlist
($requestfile =~ /^(.+)\.m3u8$/);
lib/MHFS/Plugin/Kodi.pm view on Meta::CPAN
package MHFS::Plugin::Kodi v0.7.0;
use 5.014;
use strict; use warnings;
use feature 'say';
use File::Basename qw(basename);
use Cwd qw(abs_path getcwd);
use URI::Escape qw(uri_escape);
use Encode qw(decode encode_utf8);
use File::Path qw(make_path);
use Data::Dumper qw(Dumper);
use Scalar::Util qw(weaken);
use MIME::Base64 qw(encode_base64url decode_base64url);
use Devel::Peek qw(Dump);
use MHFS::Kodi::TVShows;
use MHFS::Kodi::Movie;
use MHFS::Kodi::MovieEdition;
use MHFS::Kodi::MovieEditions;
use MHFS::Kodi::MoviePart;
use MHFS::Kodi::Movies;
use MHFS::Kodi::MovieSubtitle;
use MHFS::Kodi::Season;
use MHFS::Process;
use MHFS::Promise;
use MHFS::Util qw(base64url_to_str str_to_base64url uri_escape_path_utf8 read_text_file_lossy write_text_file_lossy decode_utf_8 escape_html_noquote fold_case write_file read_file);
use Feature::Compat::Try;
BEGIN {
if( ! (eval "use JSON; 1")) {
eval "use JSON::PP; 1" or die "No implementation of JSON available";
warn __PACKAGE__.": Using PurePerl version of JSON (JSON::PP)";
}
}
sub readtvdir {
my ($self, $tvshows, $source, $b_tvdir) = @_;
lib/MHFS/Plugin/Kodi.pm view on Meta::CPAN
$request->Send400;
return;
};
try {
$tvitem = $self->_get_tv_item($tvshows, $showid, $season, $source, $b64_item);
} catch($e) {
say "exception $e";
$request->Send404;
return;
}
if (substr($request->{'path'}{'unescapepath'}, -1) ne '/') {
# redirect if we aren't accessing a file
if (!exists $tvitem->{b_path}) {
$request->SendRedirect(301, substr($request->{'path'}{'unescapepath'}, rindex($request->{'path'}{'unescapepath'}, '/')+1).'/');
} else {
$request->SendFile($tvitem->{b_path});
}
return;
}
} else {
$tvitem = bless {tvshows => $tvshows}, 'MHFS::Kodi::TVShows';
}
if(exists $request->{qs}{fmt} && $request->{qs}{fmt} eq 'html') {
my $buf = $tvitem->TO_HTML;
lib/MHFS/Plugin/Kodi.pm view on Meta::CPAN
say "subfile $subfile";
}
}
}
}
$movieitem = $self->_search_movie_library($movies, $movieid, $source, $editionname, $partname, $subfile);
} catch ($e) {
$request->Send404;
return;
}
if (substr($request->{'path'}{'unescapepath'}, -1) ne '/') {
# redirect if we aren't accessing a file
if (!exists $movieitem->{b_path}) {
$request->SendRedirect(301, substr($request->{'path'}{'unescapepath'}, rindex($request->{'path'}{'unescapepath'}, '/')+1).'/');
} else {
$request->SendFile($movieitem->{b_path});
}
return;
}
} else {
$movieitem = bless {movies => $movies}, 'MHFS::Kodi::Movies';
}
# render
if(exists $request->{qs}{fmt} && $request->{qs}{fmt} eq 'html') {
lib/MHFS/Plugin/Kodi.pm view on Meta::CPAN
<li>Click <b>Configure</b> (or open the MHFS Video settings) and fill in <b>$baseurl</b> (the URL of the MHFS server you want to connect to).</li>
<li>MHFS Video should now be installed, you should be able to access it from <b>Add-ons->Video add-ons->MHFS Video</b> on the main menu</li>
</ol>
<ul>
<a href="$repo_addon_name">$repo_addon_name</a>
</ul>
END_HTML
$request->SendHTML($html);
return;
} elsif (substr($request_path, length($kodidir)+1) ne $repo_addon_name ||
substr($request->{'path'}{'unescapepath'}, -1) eq '/') {
$request->Send404;
return;
}
my $xml = <<"END_XML";
<?xml version="1.0" encoding="UTF-8"?>
<addon id="repository.mhfs"
name="MHFS Repository"
version="$repo_addon_name"
provider-name="G4Vi">
<extension point="xbmc.addon.repository" name="MHFS Repository">
lib/MHFS/Plugin/Kodi.pm view on Meta::CPAN
if($qs){
foreach my $key (keys %{$qs}) {
my @values;
if(ref($qs->{$key}) ne 'ARRAY') {
push @values, $qs->{$key};
}
else {
@values = @{$qs->{$key}};
}
foreach my $value (@values) {
$url .= uri_escape($key).'='.uri_escape($value) . '&';
}
}
}
chop $url;
return _curl($server, [encode_utf8($url)], sub {
$cb->(decode_json($_[0]));
});
}
sub _TMDB_api_promise {
lib/MHFS/Plugin/MusicLibrary.pm view on Meta::CPAN
use feature 'say';
use Cwd qw(abs_path getcwd);
use File::Find;
use Data::Dumper;
use Devel::Peek;
use Fcntl ':mode';
use File::stat;
use File::Basename;
use File::Path qw(make_path);
use Scalar::Util qw(looks_like_number);
use MHFS::Util qw(get_printable_utf8 escape_html_noquote LOCK_GET_LOCKDATA LOCK_WRITE UNLOCK_WRITE);
BEGIN {
if( ! (eval "use JSON; 1")) {
eval "use JSON::PP; 1" or die "No implementation of JSON available";
warn __PACKAGE__.": Using PurePerl version of JSON (JSON::PP)";
}
}
use Encode qw(decode encode);
use URI::Escape;
use Storable qw(dclone);
use Fcntl ':mode';
lib/MHFS/Plugin/MusicLibrary.pm view on Meta::CPAN
return undef if( $size eq 0);
return [$basepath, $size, \@tree, $utf8name];
}
}
sub ToHTML {
my ($files, $where) = @_;
$where //= '';
my $buf = '';
my $name_unencoded = $files->[3];
my $name = ${escape_html_noquote($name_unencoded)};
if($files->[2]) {
my $dir = $files->[0];
$buf .= '<tr>';
$buf .= '<td>';
$buf .= '<table border="1" class="tbl_track">';
$buf .= '<tbody>';
$buf .= '<tr class="track">';
$buf .= '<th>' . $name . '</th>';
$buf .= '<th><a href="#">Play</a></th><th><a href="#">Queue</a></th><th><a href="music_dl?action=dl&name=' . uri_escape_utf8($where.$name_unencoded) . '">DL</a></th>';
$buf .= '</tr>';
$where .= $name_unencoded . '/';
foreach my $file (@{$files->[2]}) {
$buf .= ToHTML($file, $where) ;
}
$buf .= '</tbody></table>';
$buf .= '</td>';
}
else {
if($where eq '') {
$buf .= '<table border="1" class="tbl_track">';
$buf .= '<tbody>';
}
$buf .= '<tr class="track">';
$buf .= '<td>' . $name . '</td>';
$buf .= '<td><a href="#">Play</a></td><td><a href="#">Queue</a></td><td><a href="music_dl?action=dl&name=' . uri_escape_utf8($where.$name_unencoded).'">DL</a></td>';
if($where eq '') {
$buf .= '</tr>';
$buf .= '</tbody></table>';
return $buf;
}
}
$buf .= '</tr>';
return $buf;
}
lib/MHFS/Plugin/Playlist.pm view on Meta::CPAN
package MHFS::Plugin::Playlist v0.7.0;
use 5.014;
use strict; use warnings;
use feature 'say';
use Data::Dumper;
use URI::Escape qw(uri_escape);
use Encode qw(decode);
sub video_get_m3u8 {
my ($video, $urlstart) = @_;
my $buf;
my $m3u8 = <<'M3U8END';
#EXTM3U
#EXTVLCOPT:network-caching=40000'
M3U8END
lib/MHFS/Plugin/Playlist.pm view on Meta::CPAN
'root' => $video->{'src_file'}{'root'},
'on_file' => sub {
my ($path, $shortpath) = @_;
push @files, $shortpath;
}
});
}
foreach my $file (@files) {
$m3u8 .= '#EXTINF:0, ' . decode('UTF-8', $file, Encode::LEAVE_SRC) . "\n";
$m3u8 .= $urlstart . uri_escape($file) . "\n";
#$m3u8 .= $urlstart . small_url_encode($file) . "\n";
}
return \$m3u8;
}
sub new {
my ($class, $settings, $server) = @_;
my $self = {};
bless $self, $class;
lib/MHFS/Plugin/VideoLibrary.pm view on Meta::CPAN
package MHFS::Plugin::VideoLibrary v0.7.0;
use 5.014;
use strict; use warnings;
use feature 'say';
use Feature::Compat::Try;
use Encode qw(decode);
use URI::Escape qw (uri_escape);
use MHFS::Util qw(output_dir_versatile escape_html uri_escape_path);
sub player_video {
my ($request) = @_;
my $qs = $request->{'qs'};
my $server = $request->{'client'}{'server'};
my $packagename = __PACKAGE__;
my $settings = $server->{'settings'};
my $self = $request->{'client'}{'server'}{'loaded_plugins'}{$packagename};
my $buf = "<html>";
lib/MHFS/Plugin/VideoLibrary.pm view on Meta::CPAN
my $urlconstant = 'lib='.$lib.'&sid='.$sid;
my $playlisturl = "playlist/video/$sid/";
my $buf;
output_dir_versatile($dir, {
'root' => $dir,
'min_file_size' => 100000,
'on_dir_start' => sub {
my ($realpath, $unsafe_relpath) = @_;
my $relpath = uri_escape($unsafe_relpath);
my $disppath = escape_html(decode('UTF-8', $unsafe_relpath));
$buf .= '<li><div class="row">';
$buf .= '<a href="#' . $relpath . '_hide" class="hide" id="' . $$disppath . '_hide">' . "$$disppath</a>";
$buf .= '<a href="#' . $relpath . '_show" class="show" id="' . $$disppath . '_show">' . "$$disppath</a>";
$buf .= ' <a href="'.$playlisturl . uri_escape_path($unsafe_relpath) . '?fmt=m3u8">M3U</a>';
$buf .= '<div class="list"><ul>';
},
'on_dir_end' => sub {
$buf .= '</ul></div></div></li>';
},
'on_file' => sub {
my ($realpath, $unsafe_relpath, $unsafe_name) = @_;
my $relpath = uri_escape($unsafe_relpath);
my $filename = escape_html(decode('UTF-8', $unsafe_name));
$buf .= '<li><a href="video?'.$urlconstant.'&name='.$relpath.'&fmt=' . $fmt . '" class="mediafile">' . $$filename . '</a> <a href="get_video?'.$urlconstant.'&name=' . $relpath . '&fmt=' . $fmt . '">DL</a> <a href="'.$playlisturl . u...
}
});
return \$buf;
}
sub new {
my ($class, $settings) = @_;
my $self = {};
bless $self, $class;
lib/MHFS/Plugin/Youtube.pm view on Meta::CPAN
use 5.014;
use strict; use warnings;
use feature 'say';
use Data::Dumper;
use feature 'state';
use Encode;
use URI::Escape;
use Scalar::Util qw(looks_like_number weaken);
use File::stat;
use MHFS::Process;
use MHFS::Util qw(escape_html LOCK_WRITE UNLOCK_WRITE);
BEGIN {
if( ! (eval "use JSON; 1")) {
eval "use JSON::PP; 1" or die "No implementation of JSON available";
warn __PACKAGE__.": Using PurePerl version of JSON (JSON::PP)";
}
}
sub searchbox {
my ($self, $request) = @_;
#my $html = '<form name="searchbox" action="' . $request->{'path'}{'basename'} . '">';
my $html = '<form name="searchbox" action="yt">';
$html .= '<input type="text" width="50%" name="q" ';
my $query = $request->{'qs'}{'q'};
if($query) {
$query =~ s/\+/ /g;
my $escaped = escape_html($query);
$html .= 'value="' . $$escaped . '"';
}
$html .= '>';
if($request->{'qs'}{'media'}) {
$html .= '<input type="hidden" name="media" value="' . $request->{'qs'}{'media'} . '">';
}
$html .= '<input type="submit" value="Search">';
$html .= '</form>';
return $html;
}
sub ytplayer {
my ($self, $request) = @_;
my $html = '<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0" /><iframe src="static/250ms_silence.mp3" allow="autoplay" id="audio" style="display:none"></iframe>';
my $url = 'get_video?fmt=yt&id=' . uri_escape($request->{'qs'}{'id'});
$url .= '&media=' . uri_escape($request->{'qs'}{'media'}) if($request->{'qs'}{'media'});
if($request->{'qs'}{'media'} && ($request->{'qs'}{'media'} eq 'music')) {
$request->{'path'}{'basename'} = 'ytaudio';
$html .= '<audio controls autoplay src="' . $url . '">Great Browser</audio>';
}
else {
$request->{'path'}{'basename'} = 'yt';
$html .= '<video controls autoplay src="' . $url . '">Great Browser</video>';
}
return $html;
}
lib/MHFS/Plugin/Youtube.pm view on Meta::CPAN
return;
}
my $html = $self->searchbox($request);
$html .= '<div id="vidlist">';
foreach my $item (@{$json->{'items'}}) {
my $id = $item->{'id'}{'videoId'};
next if (! defined $id);
$html .= '<div>';
my $mediaurl = 'ytplayer?fmt=yt&id=' . $id;
my $media = $request->{'qs'}{'media'};
$mediaurl .= '&media=' . uri_escape($media) if(defined $media);
$html .= '<a href="' . $mediaurl . '">' . $item->{'snippet'}{'title'} . '</a>';
$html .= '<br>';
$html .= '<a href="' . $mediaurl . '"><img src="' . $item->{'snippet'}{'thumbnails'}{'default'}{'url'} . '" alt="Excellent image loading"></a>';
$html .= ' <a href="https://youtube.com/channel/' . $item->{'snippet'}{'channelId'} . '">' . $item->{'snippet'}{'channelTitle'} . '</a>';
$html .= '<p>' . $item->{'snippet'}{'description'} . '</p>';
$html .= '<br>-----------------------------------------------';
$html .= '</div>'
}
$html .= '</div>';
$html .= '<script>
lib/MHFS/Plugin/Youtube.pm view on Meta::CPAN
window.onpopstate = function(event) {
console.log(event.state);
vidlist.innerHTML = event.state;
}
</script>';
$request->SendHTML($html);
}
sub onYoutube {
my ($self, $request) = @_;
my $evp = $request->{'client'}{'server'}{'evp'};
my $youtubequery = 'q=' . (uri_escape($request->{'qs'}{'q'}) // '') . '&maxResults=' . ($request->{'qs'}{'maxResults'} // '25') . '&part=snippet&key=' . $self->{'settings'}{'Youtube'}{'key'};
$youtubequery .= '&type=video'; # playlists not supported yet
my $tosend = '';
my @curlcmd = ('curl', '-G', '-d', $youtubequery, 'https://www.googleapis.com/youtube/v3/search');
print "$_ " foreach @curlcmd;
print "\n";
state $tprocess;
$tprocess = MHFS::Process->new(\@curlcmd, $evp, {
'SIGCHLD' => sub {
my $stdout = $tprocess->{'fd'}{'stdout'}{'fd'};
my $buf;
lib/MHFS/Util.pm view on Meta::CPAN
use strict; use warnings;
use feature 'say';
use Carp qw(croak);
use Exporter 'import';
use Feature::Compat::Try;
use File::Find;
use File::Basename;
use POSIX ();
use Cwd qw(abs_path getcwd);
use Encode qw(decode encode);
use URI::Escape qw(uri_escape uri_escape_utf8);
use MIME::Base64 qw(encode_base64url decode_base64url);
use PerlIO::encoding;
use warnings::register;
our @EXPORT_OK = ('LOCK_GET_LOCKDATA', 'LOCK_WRITE', 'UNLOCK_WRITE', 'write_file', 'write_text_file', 'write_text_file_lossy', 'read_file', 'read_text_file', 'read_text_file_lossy', 'shellcmd_unlock', 'ASYNC', 'FindFile', 'space2us', 'escape_html', '...
BEGIN {
if (eval "use feature 'fc'; 1;") {
*fold_case = \&CORE::fc;
} else {
*fold_case = \&lc;
}
}
# single threaded locks
lib/MHFS/Util.pm view on Meta::CPAN
say "PID $pid ASYNC";
return $pid;
}
}
sub space2us {
my ($string) = @_;
$string =~ s/\s/_/g;
return $string;
}
sub escape_html {
my ($string) = @_;
my %dangerchars = ( '"' => '"', "'" => ''', '<' => '<', '>' => '>', '/' => '/');
$string =~ s/&/&/g;
foreach my $key(keys %dangerchars) {
my $val = $dangerchars{$key};
$string =~ s/$key/$val/g;
}
return \$string;
}
sub escape_html_noquote {
my ($string) = @_;
my %dangerchars = ('<' => '<', '>' => '>');
$string =~ s/&/&/g;
foreach my $key(keys %dangerchars) {
my $val = $dangerchars{$key};
$string =~ s/$key/$val/g;
}
return \$string;
}
sub pid_running {
return kill 0, shift;
}
sub shell_escape {
my ($cmd) = @_;
($cmd) =~ s/'/'"'"'/g;
return $cmd;
}
sub output_dir_versatile {
my ($path, $options) = @_;
# hide the root path if desired
my $root = $options->{'root'};
$options->{'min_file_size'} //= 0;
lib/MHFS/Util.pm view on Meta::CPAN
}
return $res;
}
# save space by not precent encoding valid UTF-8 characters
sub small_url_encode {
my ($octets) = @_;
say "before $octets";
my $escapedoctets = ${escape_html($octets)};
my $res;
while(length($escapedoctets)) {
$res .= decode('UTF-8', $escapedoctets, Encode::FB_QUIET);
last if(!length($escapedoctets));
my $oct = ord(substr($escapedoctets, 0, 1, ''));
$res .= sprintf ("%%%02X", $oct);
}
say "now: $res";
return $res;
}
sub uri_escape_path {
my ($b_path) = @_;
uri_escape($b_path, qr/[^A-Za-z0-9\-\._~\/]/)
}
sub uri_escape_path_utf8 {
my ($path) = @_;
uri_escape_utf8($path, qr/[^A-Za-z0-9\-\._~\/]/)
}
sub round {
return int($_[0]+0.5);
}
sub ceil_div {
return int(($_[0] + $_[1] - 1) / $_[1]);
}
share/public_html/static/hls.js view on Meta::CPAN
}
}]);
return Fragment;
}();
/* harmony default export */ var loader_fragment = (fragment_Fragment);
// CONCATENATED MODULE: ./src/utils/attr-list.js
function attr_list__classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var DECIMAL_RESOLUTION_REGEX = /^(\d+)x(\d+)$/; // eslint-disable-line no-useless-escape
var ATTR_LIST_REGEX = /\s*(.+?)\s*=((?:\".*?\")|.*?)(?:,|$)/g; // eslint-disable-line no-useless-escape
// adapted from https://github.com/kanongil/node-m3u8parse/blob/master/attrlist.js
var AttrList = function () {
function AttrList(attrs) {
attr_list__classCallCheck(this, AttrList);
if (typeof attrs === 'string') {
attrs = AttrList.parseAttrList(attrs);
}
share/public_html/static/music_inc/src/miniaudio.h view on Meta::CPAN
return frameCount;
}
static MA_INLINE float ma_noise_f32_brownian(ma_noise* pNoise, ma_uint32 iChannel)
{
double result;
result = (ma_lcg_rand_f64(&pNoise->lcg) + pNoise->state.brownian.accumulation[iChannel]);
result /= 1.005; /* Don't escape the -1..1 range on average. */
pNoise->state.brownian.accumulation[iChannel] = result;
result /= 20;
return (float)(result * pNoise->config.amplitude);
}
static MA_INLINE ma_int16 ma_noise_s16_brownian(ma_noise* pNoise, ma_uint32 iChannel)
{
return ma_pcm_sample_f32_to_s16(ma_noise_f32_brownian(pNoise, iChannel));
share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h view on Meta::CPAN
return frameCount;
}
static MA_INLINE float ma_noise_f32_brownian(ma_noise* pNoise, ma_uint32 iChannel)
{
double result;
result = (ma_lcg_rand_f64(&pNoise->lcg) + pNoise->state.brownian.accumulation[iChannel]);
result /= 1.005; /* Don't escape the -1..1 range on average. */
pNoise->state.brownian.accumulation[iChannel] = result;
result /= 20;
return (float)(result * pNoise->config.amplitude);
}
static MA_INLINE ma_int16 ma_noise_s16_brownian(ma_noise* pNoise, ma_uint32 iChannel)
{
return ma_pcm_sample_f32_to_s16(ma_noise_f32_brownian(pNoise, iChannel));
share/public_html/static/music_worklet_inprogress/index.html view on Meta::CPAN
fetch(myRequest).then(function(response) {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.text();
}).then((html) => {
document.getElementById("musicdb").innerHTML = html;
});
*/
function escapeHTML(unsafe) {
return unsafe
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
function dlurl(apiroot, namestack, name) {
let str = apiroot + '/music_dl?action=dl&name=';
let fullname = '';
namestack.forEach(function(elm) {
fullname += elm + '/';
});
fullname += name;
return str+encodeURIComponent(fullname);
}
function trackHTML(apiroot, namestack, name) {
let text = '<tr class="track"><td>' + escapeHTML(name) + '</td>';
text += '<td><a href="#">Play</a></td><td><a href="#">Queue</a></td><td><a href="';
text += dlurl(apiroot, namestack, name) + '">DL</a></td></tr>';
return text;
}
const OldDB2HTML = function(json) {
let text = '';
let namestack = [];
let files = json.files;
while(files.length > 0) {
share/public_html/static/music_worklet_inprogress/index.html view on Meta::CPAN
text += '</tbody></table></td></tr>';
if((namestack.length === 0)) {
text += '<br>';
}
continue;
}
// is directory
else if(file.files) {
text += '<tr><td><table border="1" class="tbl_track"><tbody><tr class="track"><th>';
text += escapeHTML(file.name) + '</th><th><a href="#">Play</a></th><th><a href="#">Queue</a></th><th><a href="';
text += dlurl(apiroot, namestack, file.name) + '">DL</a></th></tr>';
namestack.push(file.name);
file.files.push(null);
file.files.push(...files);
files = file.files;
}
else {
// single track without dir
if(namestack.length === 0) {
text += '<table border="1" class="tbl_track"><tbody>';
share/public_html/static/music_worklet_inprogress/index.html view on Meta::CPAN
const DB2HTML = function(node, currentPath) {
let text = '';
let tiopen = '<td>';
let ticlose = '</td>';
if(!currentPath || node.files) {
text += '<table border="1" class="tbl_track"><tbody>';
tiopen = '<th>';
ticlose = '</th>';
}
text += '<tr class="track">';
text += tiopen +escapeHTML(node.name) + ticlose + tiopen + '<a href="#">Play</a>' + ticlose + tiopen + '<a href="#">Queue</a>' + ticlose + tiopen + '<a href="';
text += DB2HTMLURL(currentPath+node.name) + '">DL</a>' + ticlose + '</tr>';
if(node.files) {
for(const file of node.files) {
text += DB2HTML(file, currentPath+node.name);
}
}
if(!currentPath || node.files) {
text += '</tbody></table>';
}
return text;
t/01-util.t view on Meta::CPAN
#!perl
use 5.014;
use strict;
use warnings;
use Test2::V0;
use Feature::Compat::Try;
use Encode qw(decode encode);
use MHFS::Util qw(space2us escape_html escape_html_noquote shell_escape get_printable_utf8 read_text_file_lossy read_text_file write_text_file write_text_file_lossy decode_utf_8 parse_ipv4 write_file read_file fold_case);
plan 103;
is(space2us('hello world'), 'hello_world');
my $unsafe_chars = q|"'<>/|;
is(${escape_html($unsafe_chars)}, '"'<>/');
is(${escape_html_noquote($unsafe_chars)}, q|"'<>/|);
is(shell_escape(q|it's|), q|it'"'"'s|);
{
my $message = 'valid ip parses';
try {
is(parse_ipv4('8.8.8.8'), 8 | (8 << 8) | (8 << 16) | (8 << 24), $message);
is(parse_ipv4('255.255.255.255'), 0xFFFFFFFF, $message);
} catch ($e) {
fail($message);
}
}