Refactor streamers

This commit is contained in:
Phan An 2018-08-22 19:59:14 +02:00
parent 040afa393d
commit 7c7693179d
17 changed files with 181 additions and 68 deletions

View file

@ -0,0 +1,62 @@
<?php
namespace App\Factories;
use App\Models\Song;
use App\Services\Streamers\DirectStreamerInterface;
use App\Services\Streamers\ObjectStorageStreamerInterface;
use App\Services\Streamers\StreamerInterface;
use App\Services\Streamers\TranscodingStreamerInterface;
class StreamerFactory
{
private $directStreamer;
private $transcodingStreamer;
private $objectStorageStreamer;
public function __construct(
DirectStreamerInterface $directStreamer,
TranscodingStreamerInterface $transcodingStreamer,
ObjectStorageStreamerInterface $objectStorageStreamer
)
{
$this->directStreamer = $directStreamer;
$this->transcodingStreamer = $transcodingStreamer;
$this->objectStorageStreamer = $objectStorageStreamer;
}
/**
* @param Song $song
*
* @param boolean|null $transcode
* @param int|null $bitRate
* @param int $startTime
*
* @return StreamerInterface
*/
public function createStreamer(Song $song, $transcode = null, $bitRate = null, $startTime = 0)
{
if ($song->s3_params) {
$this->objectStorageStreamer->setSong($song);
return $this->objectStorageStreamer;
}
// If `transcode` parameter isn't passed, the default is to only transcode FLAC.
if ($transcode === null && ends_with(mime_content_type($song->path), 'flac')) {
$transcode = true;
}
if ($transcode) {
$this->transcodingStreamer->setSong($song);
$this->transcodingStreamer->setBitRate($bitRate ?: config('koel.streaming.bitrate'));
$this->transcodingStreamer->setStartTime($startTime);
return $this->transcodingStreamer;
}
$this->directStreamer->setSong($song);
return $this->directStreamer;
}
}

View file

@ -2,15 +2,11 @@
namespace App\Http\Controllers\API;
use App\Factories\StreamerFactory;
use App\Http\Requests\API\SongPlayRequest;
use App\Http\Requests\API\SongUpdateRequest;
use App\Models\Song;
use App\Services\MediaInformationService;
use App\Services\Streamers\PHPStreamer;
use App\Services\Streamers\S3Streamer;
use App\Services\Streamers\TranscodingStreamer;
use App\Services\Streamers\XAccelRedirectStreamer;
use App\Services\Streamers\XSendFileStreamer;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Routing\Redirector;
@ -18,10 +14,12 @@ use Illuminate\Routing\Redirector;
class SongController extends Controller
{
private $mediaInformationService;
private $streamerFactory;
public function __construct(MediaInformationService $mediaInformationService)
public function __construct(MediaInformationService $mediaInformationService, StreamerFactory $streamerFactory)
{
$this->mediaInformationService = $mediaInformationService;
$this->streamerFactory = $streamerFactory;
}
/**
@ -40,38 +38,9 @@ class SongController extends Controller
*/
public function play(SongPlayRequest $request, Song $song, $transcode = null, $bitRate = null)
{
if ($song->s3_params) {
return (new S3Streamer($song))->stream();
}
// If `transcode` parameter isn't passed, the default is to only transcode FLAC.
if ($transcode === null && ends_with(mime_content_type($song->path), 'flac')) {
$transcode = true;
}
$streamer = null;
if ($transcode) {
$streamer = new TranscodingStreamer(
$song,
$bitRate ?: config('koel.streaming.bitrate'),
floatval($request->time)
);
} else {
switch (config('koel.streaming.method')) {
case 'x-sendfile':
$streamer = new XSendFileStreamer($song);
break;
case 'x-accel-redirect':
$streamer = new XAccelRedirectStreamer($song);
break;
default:
$streamer = new PHPStreamer($song);
break;
}
}
$streamer->stream();
return $this->streamerFactory
->createStreamer($song, $transcode, $bitRate, floatval($request->time))
->stream();
}
/**

View file

@ -2,11 +2,13 @@
namespace App\Providers;
use Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider;
use DB;
use Illuminate\Database\SQLiteConnection;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\ServiceProvider;
use Laravel\Tinker\TinkerServiceProvider;
class AppServiceProvider extends ServiceProvider
{
@ -39,8 +41,8 @@ class AppServiceProvider extends ServiceProvider
public function register()
{
if (!$this->app->environment('production')) {
$this->app->register('Laravel\Tinker\TinkerServiceProvider');
$this->app->register('Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider');
$this->app->register(TinkerServiceProvider::class);
$this->app->register(IdeHelperServiceProvider::class);
}
}
}

View file

@ -0,0 +1,41 @@
<?php
namespace App\Providers;
use App\Factories\StreamerFactory;
use App\Services\Streamers\DirectStreamerInterface;
use App\Services\Streamers\ObjectStorageStreamerInterface;
use App\Services\Streamers\PHPStreamer;
use App\Services\Streamers\S3Streamer;
use App\Services\Streamers\TranscodingStreamer;
use App\Services\Streamers\TranscodingStreamerInterface;
use App\Services\Streamers\XAccelRedirectStreamer;
use App\Services\Streamers\XSendFileStreamer;
use Illuminate\Support\ServiceProvider;
class StreamerServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->when(StreamerFactory::class)
->needs(DirectStreamerInterface::class)
->give(static function () {
switch (config('koel.streaming.method')) {
case 'x-sendfile':
return new XSendFileStreamer();
case 'x-accel-redirect':
return new XAccelRedirectStreamer();
default:
return new PHPStreamer();
}
});
$this->app->when(StreamerFactory::class)
->needs(TranscodingStreamerInterface::class)
->give(TranscodingStreamer::class);
$this->app->when(StreamerFactory::class)
->needs(ObjectStorageStreamerInterface::class)
->give(S3Streamer::class);
}
}

View file

@ -0,0 +1,7 @@
<?php
namespace App\Services\Streamers;
interface DirectStreamerInterface extends StreamerInterface
{
}

View file

@ -0,0 +1,7 @@
<?php
namespace App\Services\Streamers;
interface ObjectStorageStreamerInterface extends StreamerInterface
{
}

View file

@ -2,7 +2,7 @@
namespace App\Services\Streamers;
class PHPStreamer extends Streamer implements StreamerInterface
class PHPStreamer extends Streamer implements DirectStreamerInterface
{
/**
* Stream the current song using the most basic PHP method: readfile()

View file

@ -2,7 +2,7 @@
namespace App\Services\Streamers;
class S3Streamer extends Streamer implements StreamerInterface
class S3Streamer extends Streamer implements ObjectStorageStreamerInterface
{
/**
* Stream the current song through S3.

View file

@ -16,12 +16,13 @@ class Streamer
*/
protected $contentType;
/**
* BaseStreamer constructor.
*
* @param $song Song
*/
public function __construct(Song $song)
public function __construct()
{
// Turn off error reporting to make sure our stream isn't interfered.
@error_reporting(0);
}
public function setSong(Song $song)
{
$this->song = $song;
@ -29,9 +30,8 @@ class Streamer
// Hard code the content type instead of relying on PHP's fileinfo()
// or even Symfony's MIMETypeGuesser, since they appear to be wrong sometimes.
$this->contentType = 'audio/'.pathinfo($this->song->path, PATHINFO_EXTENSION);
// Turn off error reporting to make sure our stream isn't interfered.
@error_reporting(0);
if (!$this->song->s3_params) {
$this->contentType = 'audio/'.pathinfo($this->song->path, PATHINFO_EXTENSION);
}
}
}

View file

@ -2,10 +2,10 @@
namespace App\Services\Streamers;
use App\Models\Song;
interface StreamerInterface
{
/**
* Stream the current song.
*/
public function setSong(Song $song);
public function stream();
}

View file

@ -2,9 +2,7 @@
namespace App\Services\Streamers;
use App\Models\Song;
class TranscodingStreamer extends Streamer implements StreamerInterface
class TranscodingStreamer extends Streamer implements TranscodingStreamerInterface
{
/**
* Bit rate the stream should be transcoded at.
@ -20,13 +18,6 @@ class TranscodingStreamer extends Streamer implements StreamerInterface
*/
private $startTime;
public function __construct(Song $song, $bitRate, $startTime = 0)
{
parent::__construct($song);
$this->bitRate = $bitRate;
$this->startTime = $startTime;
}
/**
* On-the-fly stream the current song while transcoding.
*/
@ -55,4 +46,14 @@ class TranscodingStreamer extends Streamer implements StreamerInterface
passthru("$ffmpeg ".implode($args, ' '));
}
public function setBitRate($bitRate)
{
$this->bitRate = $bitRate;
}
public function setStartTime($startTime)
{
$this->startTime = $startTime;
}
}

View file

@ -0,0 +1,9 @@
<?php
namespace App\Services\Streamers;
interface TranscodingStreamerInterface extends StreamerInterface
{
public function setBitRate($bitRate);
public function setStartTime($startTime);
}

View file

@ -4,7 +4,7 @@ namespace App\Services\Streamers;
use App\Models\Setting;
class XAccelRedirectStreamer extends Streamer implements StreamerInterface
class XAccelRedirectStreamer extends Streamer implements DirectStreamerInterface
{
/**
* Stream the current song using nginx's X-Accel-Redirect.

View file

@ -2,7 +2,7 @@
namespace App\Services\Streamers;
class XSendFileStreamer extends Streamer implements StreamerInterface
class XSendFileStreamer extends Streamer implements DirectStreamerInterface
{
/**
* Stream the current song using Apache's x_sendfile module.

View file

@ -16,6 +16,7 @@
"doctrine/dbal": "^2.5",
"jackiedo/dotenv-editor": "^1.0",
"ext-exif": "*",
"ext-fileinfo": "*",
"ext-json": "*",
"ext-SimpleXML": "*"
},

View file

@ -140,6 +140,7 @@ return [
Illuminate\View\ViewServiceProvider::class,
Tymon\JWTAuth\Providers\JWTAuthServiceProvider::class,
Aws\Laravel\AwsServiceProvider::class,
Jackiedo\DotenvEditor\DotenvEditorServiceProvider::class,
/*
* Application Service Providers...
@ -154,7 +155,7 @@ return [
App\Providers\DownloadServiceProvider::class,
App\Providers\BroadcastServiceProvider::class,
App\Providers\iTunesServiceProvider::class,
Jackiedo\DotenvEditor\DotenvEditorServiceProvider::class,
App\Providers\StreamerServiceProvider::class,
],
/*

View file

@ -0,0 +1,13 @@
<?php
namespace Tests\Integration\Factories;
use Tests\TestCase;
class StreamerFactoryTest extends TestCase
{
public function testCreate()
{
self::fail('Write the test you lazy ass.');
}
}