koel/app/Services/Streamers/PHPStreamer.php

114 lines
3.8 KiB
PHP
Raw Normal View History

2015-12-13 04:42:28 +00:00
<?php
2017-04-20 11:20:32 +00:00
namespace App\Services\Streamers;
2015-12-13 04:42:28 +00:00
2018-08-22 17:59:14 +00:00
class PHPStreamer extends Streamer implements DirectStreamerInterface
2015-12-13 04:42:28 +00:00
{
/**
* Stream the current song using the most basic PHP method: readfile()
* Credits: DaveRandom @ http://stackoverflow.com/a/4451376/794641.
*/
public function stream()
{
// Get the 'Range' header if one was sent
2016-08-03 10:42:11 +00:00
if (array_key_exists('HTTP_RANGE', $_SERVER)) {
2015-12-13 04:42:28 +00:00
// IIS/Some Apache versions
$range = $_SERVER['HTTP_RANGE'];
} elseif (function_exists('apache_request_headers') && $apache = apache_request_headers()) {
// Try Apache again
$headers = [];
foreach ($apache as $header => $val) {
$headers[strtolower($header)] = $val;
}
2016-08-03 10:42:11 +00:00
$range = array_key_exists('range', $headers) ? $headers['range'] : false;
2015-12-13 04:42:28 +00:00
} else {
// We can't get the header/there isn't one set
$range = false;
}
// Get the data range requested (if any)
$fileSize = filesize($this->song->path);
if ($range) {
$partial = true;
list($param, $range) = explode('=', $range);
// Bad request - range unit is not 'bytes'
abort_unless(strtolower(trim($param)) === 'bytes', 400);
2015-12-13 04:42:28 +00:00
$range = explode(',', $range);
$range = explode('-', $range[0]); // We only deal with the first requested range
// Bad request - 'bytes' parameter is not valid
abort_unless(count($range) === 2, 400);
2015-12-13 04:42:28 +00:00
if ($range[0] === '') {
// First number missing, return last $range[1] bytes
$start = 0;
$end = (int) $range[1];
2015-12-13 04:42:28 +00:00
} elseif ($range[1] === '') {
2018-04-14 20:46:08 +00:00
if ($range[0] === 0) {
$start = 0;
$end = $fileSize - 1;
$partial = false;
} else {
// Second number missing, return from byte $range[0] to end
$start = (int) $range[0];
$end = $fileSize - 1;
}
2015-12-13 04:42:28 +00:00
} else {
// Both numbers present, return specific range
2016-08-03 10:42:11 +00:00
$start = (int) $range[0];
$end = (int) $range[1];
2015-12-13 04:42:28 +00:00
2016-08-03 10:42:11 +00:00
if ($end >= $fileSize || (!$start && (!$end || $end === ($fileSize - 1)))) {
2015-12-13 04:42:28 +00:00
// Invalid range/whole file specified, return whole file
$partial = false;
}
}
$length = $end - $start + 1;
} else {
// No range requested
2016-02-01 07:04:16 +00:00
$length = filesize($this->song->path);
2015-12-13 04:42:28 +00:00
$partial = false;
}
// Send standard headers
header("Content-Type: {$this->contentType}");
2015-12-14 18:28:33 +00:00
header("Content-Length: $length");
header('Last-Modified: '.gmdate('D, d M Y H:i:s T', filemtime($this->song->path)));
2015-12-13 04:42:28 +00:00
header('Content-Disposition: attachment; filename="'.basename($this->song->path).'"');
header('Accept-Ranges: bytes');
// if requested, send extra headers and part of file...
if ($partial) {
header('HTTP/1.1 206 Partial Content');
header("Content-Range: bytes $start-$end/$fileSize");
// Error out if we can't read the file
abort_unless($fp = fopen($this->song->path, 'r'), 500);
2015-12-13 04:42:28 +00:00
if ($start) {
fseek($fp, $start);
}
while ($length) {
// Read in blocks of 8KB so we don't chew up memory on the server
$read = ($length > 8192) ? 8192 : $length;
$length -= $read;
2015-12-14 13:22:39 +00:00
echo fread($fp, $read);
2015-12-13 04:42:28 +00:00
}
fclose($fp);
} else {
// ...otherwise just send the whole file
readfile($this->song->path);
}
exit;
}
}