286 lines
9.3 KiB
PHP
286 lines
9.3 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use Illuminate\Http\Request;
|
|
use Symfony\Component\HttpFoundation\StreamedResponse;
|
|
|
|
error_reporting(E_ALL);
|
|
ini_set('display_errors', true);
|
|
|
|
class PodcastController extends Controller
|
|
{
|
|
// TODO: Include program
|
|
private static $BASE_SQL = <<<QUERY
|
|
SELECT `podcast`.`id`, `podcast`.`soundfilename`,
|
|
`podcast_meta`.`creationdt` AS `created`, `podcast_meta`.`title`, `podcast_meta`.`content`, `podcast_meta`.`program`,
|
|
`programs`.`longname` AS `program_name`, `programs`.`description` AS `program_description`
|
|
FROM `podcast`
|
|
INNER JOIN `podcast_meta` ON `podcast`.`id` = `podcast_meta`.`podcast`
|
|
LEFT JOIN `programs` ON `podcast_meta`.`program` = `programs`.`id`
|
|
WHERE `podcast_meta`.`active` = 1 AND `podcast_meta`.`title` <> '' AND `podcast_meta`.`content` <> ''
|
|
QUERY;
|
|
|
|
/**
|
|
* Create a new controller instance.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function __construct()
|
|
{
|
|
//
|
|
}
|
|
|
|
/**
|
|
* Lijst van alle podcasts
|
|
*/
|
|
public function overview(Request $request) {
|
|
return $this->getPodcastList($request, null, []);
|
|
}
|
|
|
|
/**
|
|
* Lijst van alle podcasts voor een specifiek programma
|
|
*/
|
|
public function program(Request $request, $program) {
|
|
if((int)$program <= 0) {
|
|
return abort(404);
|
|
}
|
|
|
|
return $this->getPodcastList($request, '`podcast_meta`.`program` = :program', ['program' => (int)$program]);
|
|
}
|
|
|
|
/**
|
|
* Details over een specifieke podcast
|
|
*/
|
|
public function details(Request $request, $id) {
|
|
$podcasts = app('db')->select(self::$BASE_SQL . ' AND `podcast`.`id` = :podcast', ['podcast' => (int)$id]);
|
|
if(count($podcasts) != 1) {
|
|
return abort(404);
|
|
}
|
|
|
|
return response()->json(new \Model\Podcast($podcasts[0]));
|
|
}
|
|
|
|
private function getPodcastList(Request $request, $filter, $params)
|
|
{
|
|
$count = (int)$request->get('aantal', 15);
|
|
$page = (int)$request->get('pagina', 1);
|
|
if($count <= 0 || $page <= 0) {
|
|
return abort(400);
|
|
}
|
|
|
|
$start = ($page - 1) * $count;
|
|
$podcasts = app('db')->select(self::$BASE_SQL
|
|
. ($filter ? ' AND (' . $filter . ')' : '')
|
|
. ' ORDER BY `podcast_meta`.`creationdt` DESC, `podcast_meta`.`id` DESC'
|
|
. ' LIMIT ' . (int)$start . ', ' . (int)$count,
|
|
$params);
|
|
|
|
$result = array();
|
|
foreach($podcasts as $podcast) {
|
|
$result[] = new \Model\Podcast($podcast);
|
|
}
|
|
|
|
return response()->json(['page' => $page, 'count' => $count, 'podcasts' => $result]);
|
|
}
|
|
|
|
/**
|
|
* Download specifieke podcast
|
|
*/
|
|
public function download(Request $request, $id) {
|
|
$queryResult = app('db')->select(self::$BASE_SQL
|
|
. ' AND `podcast`.`id` = :id '
|
|
. ' LIMIT 0, 1',
|
|
['id' => (int)$id]);
|
|
|
|
if(count($queryResult) == 0) { return abort(404); }
|
|
|
|
$podcast = new \Model\Podcast($queryResult[0]);
|
|
if(!$podcast->isValidAuth($request->get('auth'))) {
|
|
return abort(404);
|
|
}
|
|
|
|
return response()->download($podcast->getSoundfile(),
|
|
'6FM Gemist - ' . $podcast->title . '.mp3');
|
|
}
|
|
|
|
/***
|
|
* Stream een specifieke podcast (niet voor uitzending)
|
|
*/
|
|
public function stream(Request $request, $id) {
|
|
$queryResult = app('db')->select(self::$BASE_SQL
|
|
. ' AND `podcast`.`id` = :id '
|
|
. ' LIMIT 0, 1',
|
|
['id' => $id]);
|
|
|
|
if(count($queryResult) == 0) { return abort(404); }
|
|
|
|
$podcast = new \Model\Podcast($queryResult[0]);
|
|
if(!$podcast->isValidAuth($request->get('auth'))) {
|
|
return abort(404);
|
|
}
|
|
|
|
$filename = $podcast->getSoundfile();
|
|
$file = fopen($filename, "rb");
|
|
$size = filesize($filename);
|
|
$content = fread($file, $size);
|
|
fclose($file);
|
|
|
|
self::streamFile($request, 'audio/mpeg', $content, $size);
|
|
}
|
|
|
|
/***
|
|
* Regionieuws
|
|
*/
|
|
public function latestNews(Request $request) {
|
|
$filename = '/var/audio/regionieuws.mp3';
|
|
return response()->download($filename,
|
|
'6FM Regionieuws ' . date('d-m-Y') . '.mp3');
|
|
|
|
$size = filesize($filename);
|
|
$content = fread($file, $size);
|
|
fclose($file);
|
|
|
|
self::streamFile($request, 'audio/mpeg', $content, $size);
|
|
}
|
|
|
|
/***
|
|
* Stream een specifieke uitzending
|
|
*/
|
|
public function complete(Request $r, $year, $month, $day, $hour, $duration) {
|
|
if($duration <= 0) {
|
|
return response()->abort(404);
|
|
}
|
|
|
|
$currentHour = new \DateTime();
|
|
$currentHour->setDate($year, $month, $day)->setTime($hour, 0, 0);
|
|
$files = [];
|
|
$size = 0;
|
|
while($duration--) {
|
|
$file = '/var/audio/uitzending/' . $currentHour->format('Ymd_Hi') . '.MP3';
|
|
if(!file_exists($file)) {
|
|
return response()->abort(404);
|
|
}
|
|
|
|
$size += ($filesize = filesize($file));
|
|
$files[] = [$file, $filesize];
|
|
$currentHour->add(\DateInterval::createFromDateString('1 hour'));
|
|
}
|
|
|
|
$headers = [
|
|
'Pragma' => 'public',
|
|
'Expires' => '-1',
|
|
'Cache-Control' => 'public,must-revalidate,post-check=0,pre-check=0',
|
|
'Content-Type' => 'audio/mpeg',
|
|
'Content-Length' => $size,
|
|
'Accept-Ranges' => 'bytes'
|
|
];
|
|
|
|
$range = [];
|
|
if(isset($_SERVER['HTTP_RANGE'])) {
|
|
list($unit, $rangestr) = explode('=', $_SERVER['HTTP_RANGE'], 2);
|
|
if($unit == "bytes") {
|
|
$range = explode('-', $rangestr);
|
|
$range[1] = (empty($range[1])) ? ($size - 1) : min(abs(intval($range[1])), ($size - 1));
|
|
$range[0] = (empty($range[0]) || $range[1] < abs(intval($range[0]))) ? 0 : max(abs(intval($range[0])), 0);
|
|
$range[2] = ($range[1] - $range[0] + 1);
|
|
|
|
//if($range[0] > 0 || $range[1] < ($size - 1)) {
|
|
$headers['Content-Range'] = 'bytes ' . $range[0] . '-' . $range[1] . '/' . $size;
|
|
$headers['Content-Length'] = $range[2];
|
|
//}
|
|
}
|
|
}
|
|
|
|
if($range === null) {
|
|
// $log = fopen('/tmp/rq.log', 'a');
|
|
// fwrite($log, "Aborting (416)!\n\n");
|
|
// fclose($log);
|
|
response()->abort(416); // Requested Range Not Satisfiable
|
|
return;
|
|
}
|
|
|
|
return new StreamedResponse(function() use($files, $range) {
|
|
// $log = fopen('/tmp/rq.log', 'a');
|
|
// if(!count($range)) {
|
|
// fwrite($log, "Writing everything.\n");
|
|
// } else {
|
|
// fwrite($log, "Writing {$range[2]} bytes ({$range[0]} - {$range[1]})\n");
|
|
//}
|
|
$output = fopen('php://output', 'w');
|
|
foreach($files as $file) {
|
|
if(count($range) && $range[0] >= $file[1]) {
|
|
// We have a range and can skip the whole file
|
|
// fwrite($log, "Skipping all of {$file[0]}.\n");
|
|
continue;
|
|
}
|
|
// else fwrite($log, "File {$file[0]} is {$file[1]} bytes (starting from {$range[0]}).\n");
|
|
|
|
$fp = fopen($file[0], 'rb');
|
|
if(count($range) && $range[0]) {
|
|
fseek($fp, $range[0]);
|
|
$range[0] = 0;
|
|
}
|
|
|
|
$maxToRead = count($range) ? min(1024, $range[2]) : 1024;
|
|
while ($buf = fread($fp, $maxToRead)) {
|
|
fwrite($output, $buf);
|
|
if(count($range)) {
|
|
$maxToRead = min(1024, $range[2]);
|
|
$bufSize = mb_strlen($buf, '8bit');
|
|
// $bytes += $bufSize;
|
|
if(($range[2] -= $bufSize) == 0) {
|
|
// fwrite($log, "Stopping reading from {$file[0]} because end of range reached.\n");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// fwrite($log, "Wrote $bytes bytes.\n");
|
|
fclose($fp);
|
|
|
|
if(count($range) && $range[2] == 0) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
fclose($output);
|
|
// fclose($log);
|
|
}, count($range) ? 206 : 200, $headers);
|
|
}
|
|
|
|
// Provide a streaming file with support for scrubbing
|
|
// Source: https://gist.github.com/widnyana/cd2bdda07dc02e9fce71
|
|
private static function streamFile(Request $r, $contentType, $stream, $fullsize ) {
|
|
$size = $fullsize;
|
|
$response_code = 200;
|
|
$headers = array("Content-type" => $contentType);
|
|
|
|
// Check for request for part of the stream
|
|
$range = $r->header('Range');
|
|
if($range != null) {
|
|
$eqPos = strpos($range, "=");
|
|
$toPos = strpos($range, "-");
|
|
$unit = substr($range, 0, $eqPos);
|
|
$start = intval(substr($range, $eqPos+1, $toPos));
|
|
$success = fseek($stream, $start);
|
|
if($success == 0) {
|
|
$size = $fullsize - $start;
|
|
$response_code = 206;
|
|
$headers["Accept-Ranges"] = $unit;
|
|
$headers["Content-Range"] = $unit . " " . $start . "-" . ($fullsize-1) . "/" . $fullsize;
|
|
}
|
|
}
|
|
|
|
$headers["Content-Length"] = $size;
|
|
$response = new StreamedResponse(
|
|
function () use ($stream) {
|
|
$out = fopen('php://output', 'w');
|
|
fputs($out, $stream);
|
|
fclose($out);
|
|
}, 200, $headers);
|
|
$response->send();
|
|
}
|
|
|
|
}
|