App-MHFS
view release on metacpan or search on metacpan
lib/MHFS/Plugin/MusicLibrary.pm view on Meta::CPAN
return;
}
if(! $TRACKDURATION{$tosend}) {
say __PACKAGE__.": failed to get track duration";
$request->Send503();
return;
}
say "no proc, duration cached";
my $pv = MHFS::XS::new($tosend);
$request->{'outheaders'}{'X-MHFS-NUMSEGMENTS'} = ceil($TRACKDURATION{$tosend} / $SEGMENT_DURATION);
$request->{'outheaders'}{'X-MHFS-TRACKDURATION'} = $TRACKDURATION{$tosend};
$request->{'outheaders'}{'X-MHFS-MAXSEGDURATION'} = $SEGMENT_DURATION;
my $samples_per_seg = $TRACKINFO{$tosend}{'SAMPLERATE'} * $SEGMENT_DURATION;
my $spos = $samples_per_seg * ($request->{'qs'}{'part'} - 1);
my $samples_left = $TRACKINFO{$tosend}{'TOTALSAMPLES'} - $spos;
my $res = MHFS::XS::get_flac($pv, $spos, $samples_per_seg < $samples_left ? $samples_per_seg : $samples_left);
$request->SendBytes('audio/flac', $res);
}
elsif(defined $request->{'qs'}{'fmt'} && ($request->{'qs'}{'fmt'} eq 'wav')) {
if(! HAS_MHFS_XS) {
say __PACKAGE__.": route not available without XS";
$request->Send503();
return;
}
my $pv = MHFS::XS::new($tosend);
my $outbuf = '';
my $wavsize = (44+ $TRACKINFO{$tosend}{'TOTALSAMPLES'} * ($TRACKINFO{$tosend}{'BITSPERSAMPLE'}/8) * $TRACKINFO{$tosend}{'NUMCHANNELS'});
my $startbyte = $request->{'header'}{'_RangeStart'} || 0;
my $endbyte = $request->{'header'}{'_RangeEnd'} // $wavsize-1;
say "start byte" . $startbyte;
say "end byte " . $endbyte;
say "MHFS::XS::wavvfs_read_range " . $startbyte . ' ' . $endbyte;
my $maxsendsize;
$maxsendsize = 1048576/2;
say "maxsendsize $maxsendsize " . ' bytespersample ' . ($TRACKINFO{$tosend}{'BITSPERSAMPLE'}/8) . ' numchannels ' . $TRACKINFO{$tosend}{'NUMCHANNELS'};
$request->SendCallback(sub{
my ($fileitem) = @_;
my $actual_endbyte = $startbyte + $maxsendsize - 1;
if($actual_endbyte >= $endbyte) {
$actual_endbyte = $endbyte;
$fileitem->{'cb'} = undef;
say "SendCallback last send";
}
my $actual_startbyte = $startbyte;
$startbyte = $actual_endbyte+1;
say "SendCallback wavvfs_read_range " . $actual_startbyte . ' ' . $actual_endbyte;
return MHFS::XS::wavvfs_read_range($pv, $actual_startbyte, $actual_endbyte);
}, {
'mime' => 'audio/wav',
'size' => $wavsize,
});
}
else {
if($request->{'qs'}{'action'} && ($request->{'qs'}{'action'} eq 'dl')) {
$request->{'responseopt'}{'cd_file'} = 'attachment';
}
# Send the total pcm frame count for mp3
elsif(lc(substr($tosend, -4)) eq '.mp3') {
if(HAS_MHFS_XS) {
if(! $TRACKINFO{$tosend}) {
$TRACKINFO{$tosend} = { 'TOTALSAMPLES' => MHFS::XS::get_totalPCMFrameCount($tosend) };
say "mp3 totalPCMFrames: " . $TRACKINFO{$tosend}{'TOTALSAMPLES'};
}
$request->{'outheaders'}{'X-MHFS-totalPCMFrameCount'} = $TRACKINFO{$tosend}{'TOTALSAMPLES'};
}
}
$request->SendLocalFile($tosend);
}
}
sub parseStreamInfo {
# https://metacpan.org/source/DANIEL/Audio-FLAC-Header-2.4/Header.pm
my ($buf) = @_;
my $metaBinString = unpack('B144', $buf);
my $x32 = 0 x 32;
my $info = {};
$info->{'MINIMUMBLOCKSIZE'} = unpack('N', pack('B32', substr($x32 . substr($metaBinString, 0, 16), -32)));
$info->{'MAXIMUMBLOCKSIZE'} = unpack('N', pack('B32', substr($x32 . substr($metaBinString, 16, 16), -32)));
$info->{'MINIMUMFRAMESIZE'} = unpack('N', pack('B32', substr($x32 . substr($metaBinString, 32, 24), -32)));
$info->{'MAXIMUMFRAMESIZE'} = unpack('N', pack('B32', substr($x32 . substr($metaBinString, 56, 24), -32)));
$info->{'SAMPLERATE'} = unpack('N', pack('B32', substr($x32 . substr($metaBinString, 80, 20), -32)));
$info->{'NUMCHANNELS'} = unpack('N', pack('B32', substr($x32 . substr($metaBinString, 100, 3), -32))) + 1;
$info->{'BITSPERSAMPLE'} = unpack('N', pack('B32', substr($x32 . substr($metaBinString, 103, 5), -32))) + 1;
# Calculate total samples in two parts
my $highBits = unpack('N', pack('B32', substr($x32 . substr($metaBinString, 108, 4), -32)));
$info->{'TOTALSAMPLES'} = $highBits * 2 ** 32 +
unpack('N', pack('B32', substr($x32 . substr($metaBinString, 112, 32), -32)));
# Return the MD5 as a 32-character hexadecimal string
$info->{'MD5CHECKSUM'} = unpack('H32',substr($buf, 18, 16));
return $info;
}
sub GetTrackInfo {
my ($file) = @_;
open(my $fh, '<', $file) or die "open failed";
my $buf = '';
seek($fh, 8, 0) or die "seek failed";
(read($fh, $buf, 34) == 34) or die "short read";
my $info = parseStreamInfo($buf);
$info->{'duration'} = $info->{'TOTALSAMPLES'}/$info->{'SAMPLERATE'};
print Dumper($info);
return $info;
}
sub SendLocalTrack {
my ($request, $file) = @_;
# fast path, just send the file
my $justsendfile = (!defined($request->{'qs'}{'fmt'})) && (!defined($request->{'qs'}{'max_sample_rate'})) && (!defined($request->{'qs'}{'bitdepth'})) && (!defined($request->{'qs'}{'part'}));
if($justsendfile) {
SendTrack($request, $file);
return;
( run in 0.811 second using v1.01-cache-2.11-cpan-e1769b4cff6 )