mirror of
https://github.com/koel/koel
synced 2024-11-10 06:34:14 +00:00
Big Bang
This commit is contained in:
parent
32f56ce24b
commit
0ee372882c
206 changed files with 15082 additions and 0 deletions
3
.babelrc
Normal file
3
.babelrc
Normal file
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"presets": ["es2015"]
|
||||
}
|
3
.editorconfig
Normal file
3
.editorconfig
Normal file
|
@ -0,0 +1,3 @@
|
|||
[*.{js,sass,scss,json,coffee,vue}]
|
||||
indent_style = space
|
||||
indent_size = 4
|
29
.env.example
Normal file
29
.env.example
Normal file
|
@ -0,0 +1,29 @@
|
|||
APP_ENV=local
|
||||
APP_DEBUG=true
|
||||
APP_KEY=SomeRandomString
|
||||
|
||||
# Username and passsword for the initial admin account
|
||||
# This info will be populated into the database during `php artisan db:seed`
|
||||
# After that, it can (and should) be removed from this .env file
|
||||
ADMIN_EMAIL=
|
||||
ADMIN_NAME=
|
||||
ADMIN_PASSWORD=
|
||||
|
||||
# The maximum scan time, in seconds. Increase this if you have a huge library.
|
||||
APP_MAX_SCAN_TIME=600
|
||||
|
||||
DB_HOST=localhost
|
||||
DB_DATABASE=homestead
|
||||
DB_USERNAME=homestead
|
||||
DB_PASSWORD=secret
|
||||
|
||||
CACHE_DRIVER=file
|
||||
SESSION_DRIVER=file
|
||||
QUEUE_DRIVER=sync
|
||||
|
||||
MAIL_DRIVER=smtp
|
||||
MAIL_HOST=mailtrap.io
|
||||
MAIL_PORT=2525
|
||||
MAIL_USERNAME=null
|
||||
MAIL_PASSWORD=null
|
||||
MAIL_ENCRYPTION=null
|
3
.gitattributes
vendored
Normal file
3
.gitattributes
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
* text=auto
|
||||
*.css linguist-vendored
|
||||
*.less linguist-vendored
|
75
.gitignore
vendored
Normal file
75
.gitignore
vendored
Normal file
|
@ -0,0 +1,75 @@
|
|||
/vendor
|
||||
/node_modules
|
||||
Homestead.yaml
|
||||
Homestead.json
|
||||
.env
|
||||
/public
|
||||
/.idea
|
||||
/_ide_helper.php
|
||||
/config.testing
|
||||
/config.local
|
||||
db.mwb.bak
|
||||
bower_components
|
||||
|
||||
### Node ###
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
|
||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (http://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directory
|
||||
# https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git
|
||||
node_modules
|
||||
|
||||
|
||||
### OSX ###
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear in the root of a volume
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
|
||||
### Sass ###
|
||||
.sass-cache/
|
||||
*.css.map
|
41
.htaccess
Normal file
41
.htaccess
Normal file
|
@ -0,0 +1,41 @@
|
|||
<IfModule mod_headers.c>
|
||||
<FilesMatch "\.(eot|font.css|otf|ttc|ttf|woff)$">
|
||||
Header set Access-Control-Allow-Origin "*"
|
||||
</FilesMatch>
|
||||
</IfModule>
|
||||
|
||||
<IfModule mod_rewrite.c>
|
||||
<IfModule mod_negotiation.c>
|
||||
Options -MultiViews
|
||||
</IfModule>
|
||||
|
||||
RewriteEngine On
|
||||
RewriteBase /
|
||||
|
||||
# Deny access to framework directories
|
||||
RewriteRule ^(app/|bootstrap/|config/|database/|resources/|storage/tests|vendor/) - [R=404,L,NC]
|
||||
|
||||
# And dot files/folders (for example .env)
|
||||
RedirectMatch 404 /\..*$
|
||||
|
||||
# Redirect Trailing Slashes...
|
||||
RewriteRule ^(.*)/$ /$1 [L,R=301]
|
||||
|
||||
# Handle Front Controller...
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteRule ^ index.php [L]
|
||||
</IfModule>
|
||||
|
||||
<IfModule mod_deflate.c>
|
||||
# Disable deflation for media files.
|
||||
SetEnvIfNoCase Request_URI "^/api/play/" no-gzip dont-vary
|
||||
</IfModule>
|
||||
|
||||
<IfModule mod_xsendfile.c>
|
||||
# Set a MOD_X_SENDFILE_ENABLED env variable for PHP to use later.
|
||||
<Files *.php>
|
||||
XSendFile On
|
||||
SetEnv MOD_X_SENDFILE_ENABLED 1
|
||||
</Files>
|
||||
</IfModule>
|
6
.jshintrc
Normal file
6
.jshintrc
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"globals": { "$": false },
|
||||
"globalstrict": false,
|
||||
"devel": true,
|
||||
"esnext": true
|
||||
}
|
15
.travis.yml
Normal file
15
.travis.yml
Normal file
|
@ -0,0 +1,15 @@
|
|||
language: php
|
||||
|
||||
php:
|
||||
- 5.6
|
||||
- 7.0
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
|
||||
before_script:
|
||||
- curl -s http://getcomposer.org/installer | php
|
||||
- php composer.phar install
|
||||
|
||||
script: phpunit
|
35
app/Application.php
Normal file
35
app/Application.php
Normal file
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
namespace App;
|
||||
|
||||
use Illuminate\Foundation\Application as IlluminateApplication;
|
||||
use InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* Extends \Illuminate\Foundation\Application to override some defaults.
|
||||
*/
|
||||
class Application extends IlluminateApplication
|
||||
{
|
||||
/**
|
||||
* Loads a revision'ed asset file, making use of gulp-rev
|
||||
* This is a copycat of L5's Elixir, but catered to our directory structure.
|
||||
*
|
||||
* @param string $file
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function rev($file)
|
||||
{
|
||||
static $manifest = null;
|
||||
|
||||
if (is_null($manifest)) {
|
||||
$manifest = json_decode(file_get_contents($this->publicPath().'/build/rev-manifest.json'), true);
|
||||
}
|
||||
|
||||
if (isset($manifest[$file])) {
|
||||
return "/public/build/{$manifest[$file]}";
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException("File {$file} not defined in asset manifest.");
|
||||
}
|
||||
}
|
86
app/Console/Commands/SyncMedia.php
Normal file
86
app/Console/Commands/SyncMedia.php
Normal file
|
@ -0,0 +1,86 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Setting;
|
||||
use Illuminate\Console\Command;
|
||||
use Media;
|
||||
|
||||
class SyncMedia extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'koel:sync';
|
||||
|
||||
protected $ignored = 0;
|
||||
protected $invalid = 0;
|
||||
protected $synced = 0;
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Sync songs found in configured directory against the database.';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
if (!Setting::get('media_path')) {
|
||||
$this->error("Media path hasn't been configured. Exiting.");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->info('Koel syncing started. All we need now is just a little patience…');
|
||||
|
||||
Media::sync(null, $this);
|
||||
|
||||
$this->output->writeln("<info>Completed! {$this->synced} new or updated songs(s)</info>, "
|
||||
."{$this->ignored} unchanged song(s), "
|
||||
."and <comment>{$this->invalid} invalid file(s)</comment>.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a song's sync status to console.
|
||||
*/
|
||||
public function logToConsole($path, $result)
|
||||
{
|
||||
$name = basename($path);
|
||||
|
||||
if ($result === true) {
|
||||
if ($this->option('verbose')) {
|
||||
$this->line("$name has no changes – ignoring");
|
||||
}
|
||||
|
||||
++$this->ignored;
|
||||
} elseif ($result === false) {
|
||||
if ($this->option('verbose')) {
|
||||
$this->error("$name is not a valid media file");
|
||||
}
|
||||
|
||||
++$this->invalid;
|
||||
} else {
|
||||
if ($this->option('verbose')) {
|
||||
$this->info("$name synced");
|
||||
}
|
||||
|
||||
++$this->synced;
|
||||
}
|
||||
}
|
||||
}
|
28
app/Console/Kernel.php
Normal file
28
app/Console/Kernel.php
Normal file
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console;
|
||||
|
||||
use Illuminate\Console\Scheduling\Schedule;
|
||||
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
|
||||
|
||||
class Kernel extends ConsoleKernel
|
||||
{
|
||||
/**
|
||||
* The Artisan commands provided by your application.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $commands = [
|
||||
\App\Console\Commands\SyncMedia::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* Define the application's command schedule.
|
||||
*
|
||||
* @param \Illuminate\Console\Scheduling\Schedule $schedule
|
||||
* @return void
|
||||
*/
|
||||
protected function schedule(Schedule $schedule)
|
||||
{
|
||||
}
|
||||
}
|
8
app/Events/Event.php
Normal file
8
app/Events/Event.php
Normal file
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
|
||||
namespace App\Events;
|
||||
|
||||
abstract class Event
|
||||
{
|
||||
//
|
||||
}
|
51
app/Exceptions/Handler.php
Normal file
51
app/Exceptions/Handler.php
Normal file
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
|
||||
|
||||
class Handler extends ExceptionHandler
|
||||
{
|
||||
/**
|
||||
* A list of the exception types that should not be reported.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $dontReport = [
|
||||
HttpException::class,
|
||||
ModelNotFoundException::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* Report or log an exception.
|
||||
*
|
||||
* This is a great spot to send exceptions to Sentry, Bugsnag, etc.
|
||||
*
|
||||
* @param \Exception $e
|
||||
* @return void
|
||||
*/
|
||||
public function report(Exception $e)
|
||||
{
|
||||
return parent::report($e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render an exception into an HTTP response.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Exception $e
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function render($request, Exception $e)
|
||||
{
|
||||
if ($e instanceof ModelNotFoundException) {
|
||||
$e = new NotFoundHttpException($e->getMessage(), $e);
|
||||
}
|
||||
|
||||
return parent::render($request, $e);
|
||||
}
|
||||
}
|
13
app/Facades/Media.php
Normal file
13
app/Facades/Media.php
Normal file
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
namespace App\Facades;
|
||||
|
||||
use Illuminate\Support\Facades\Facade;
|
||||
|
||||
class Media extends Facade
|
||||
{
|
||||
protected static function getFacadeAccessor()
|
||||
{
|
||||
return 'Media';
|
||||
}
|
||||
}
|
9
app/Http/Controllers/API/Controller.php
Normal file
9
app/Http/Controllers/API/Controller.php
Normal file
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\API;
|
||||
|
||||
use App\Http\Controllers\Controller as BaseController;
|
||||
|
||||
abstract class Controller extends BaseController
|
||||
{
|
||||
}
|
37
app/Http/Controllers/API/DataController.php
Normal file
37
app/Http/Controllers/API/DataController.php
Normal file
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\API;
|
||||
|
||||
use App\Models\Artist;
|
||||
use App\Models\Interaction;
|
||||
use App\Models\Playlist;
|
||||
use App\Models\Setting;
|
||||
use App\Models\Song;
|
||||
use App\Models\User;
|
||||
|
||||
class DataController extends Controller
|
||||
{
|
||||
/**
|
||||
* Get a set of application data.
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$playlists = Playlist::byCurrentUser()->orderBy('name')->with('songs')->get()->toArray();
|
||||
|
||||
// We don't need full song data, just ID's
|
||||
foreach ($playlists as &$playlist) {
|
||||
$playlist['songs'] = array_pluck($playlist['songs'], 'id');
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'artists' => Artist::orderBy('name')->with('albums', with('albums.songs'))->get(),
|
||||
'settings' => Setting::all()->lists('value', 'key'),
|
||||
'playlists' => $playlists,
|
||||
'interactions' => Interaction::byCurrentUser()->get(),
|
||||
'users' => auth()->user()->is_admin ? User::all() : [],
|
||||
'user' => auth()->user(),
|
||||
]);
|
||||
}
|
||||
}
|
58
app/Http/Controllers/API/InteractionController.php
Normal file
58
app/Http/Controllers/API/InteractionController.php
Normal file
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\API;
|
||||
|
||||
use App\Models\Interaction;
|
||||
use App\Http\Requests\API\BatchInteractionRequest;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class InteractionController extends Controller
|
||||
{
|
||||
/**
|
||||
* Increase a song's play count as the currently authenticated user.
|
||||
*
|
||||
* @param Request $request
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function play(Request $request)
|
||||
{
|
||||
return response()->json(Interaction::increasePlayCount($request->input('id')));
|
||||
}
|
||||
|
||||
/**
|
||||
* Like or unlike a song as the currently authenticated user.
|
||||
*
|
||||
* @param Request $request
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function like(Request $request)
|
||||
{
|
||||
return response()->json(Interaction::toggleLike($request->input('id')));
|
||||
}
|
||||
|
||||
/**
|
||||
* Like several songs at once as the currently authenticated user.
|
||||
*
|
||||
* @param BatchInteractionRequest $request
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function batchLike(BatchInteractionRequest $request)
|
||||
{
|
||||
return response()->json(Interaction::batchLike((array) $request->input('ids')));
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlike several songs at once as the currently authenticated user.
|
||||
*
|
||||
* @param BatchInteractionRequest $request
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function batchUnlike(BatchInteractionRequest $request)
|
||||
{
|
||||
return response()->json(Interaction::batchUnlike((array) $request->input('ids')));
|
||||
}
|
||||
}
|
90
app/Http/Controllers/API/PlaylistController.php
Normal file
90
app/Http/Controllers/API/PlaylistController.php
Normal file
|
@ -0,0 +1,90 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\API;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\Playlist;
|
||||
use App\Http\Requests\API\PlaylistStoreRequest;
|
||||
|
||||
class PlaylistController extends Controller
|
||||
{
|
||||
/**
|
||||
* Create a new playlist.
|
||||
*
|
||||
* @param PlaylistStoreRequest $request
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function store(PlaylistStoreRequest $request)
|
||||
{
|
||||
$playlist = auth()->user()->playlists()->create($request->only('name'));
|
||||
$playlist->songs()->sync($request->input('songs'));
|
||||
|
||||
$playlist->songs = $playlist->songs->fetch('id');
|
||||
|
||||
return response()->json($playlist);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rename a playlist.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param int $id
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
$playlist = Playlist::findOrFail($id);
|
||||
|
||||
if ($playlist->user_id !== auth()->user()->id) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
$playlist->name = $request->input('name');
|
||||
$playlist->save();
|
||||
|
||||
return response()->json($playlist);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync a playlist with songs.
|
||||
* Any songs that are not populated here will be removed from the playlist.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param int $id
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function sync(Request $request, $id)
|
||||
{
|
||||
$playlist = Playlist::findOrFail($id);
|
||||
|
||||
if ($playlist->user_id !== auth()->user()->id) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
$playlist->songs()->sync($request->input('songs'));
|
||||
|
||||
return response()->json();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a playlist.
|
||||
*
|
||||
* @param int $id
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function destroy($id)
|
||||
{
|
||||
// This can't be put into a Request authorize(), due to Laravel(?)'s limitation.
|
||||
if (Playlist::findOrFail($id)->user_id !== auth()->user()->id) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
Playlist::destroy($id);
|
||||
|
||||
return response()->json();
|
||||
}
|
||||
}
|
29
app/Http/Controllers/API/SettingController.php
Normal file
29
app/Http/Controllers/API/SettingController.php
Normal file
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\API;
|
||||
|
||||
use App\Facades\Media;
|
||||
use App\Http\Requests\API\SettingRequest;
|
||||
use App\Models\Setting;
|
||||
|
||||
class SettingController extends Controller
|
||||
{
|
||||
/**
|
||||
* Save the application settings.
|
||||
*
|
||||
* @param SettingRequest $request
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function save(SettingRequest $request)
|
||||
{
|
||||
// For right now there's only one setting to be saved
|
||||
Setting::set('media_path', rtrim(trim($request->input('media_path')), '/'));
|
||||
|
||||
// In a next version we should opt for a "MediaPathChanged" event,
|
||||
// but let's just do this async now.
|
||||
Media::sync();
|
||||
|
||||
return response()->json();
|
||||
}
|
||||
}
|
43
app/Http/Controllers/API/SongController.php
Normal file
43
app/Http/Controllers/API/SongController.php
Normal file
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\API;
|
||||
|
||||
use App\Http\Streamers\XSendFileStreamer;
|
||||
use App\Http\Streamers\PHPStreamer;
|
||||
use App\Models\Song;
|
||||
|
||||
class SongController extends Controller
|
||||
{
|
||||
/**
|
||||
* Play a song.
|
||||
* As of current Koel supports two streamer: x_sendfile and native PHP readfile.
|
||||
*
|
||||
* @param $id
|
||||
*/
|
||||
public function play($id)
|
||||
{
|
||||
if (env('MOD_X_SENDFILE_ENABLED') ||
|
||||
(function_exists('apache_get_modules') && in_array('mod_xsendfile', apache_get_modules()))) {
|
||||
(new XSendFileStreamer($id))->stream();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
(new PHPStreamer($id))->stream();
|
||||
|
||||
// Exit here to avoid accidentally sending extra content at the end of the file.
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the lyrics of a song.
|
||||
*
|
||||
* @param $id
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function getLyrics($id)
|
||||
{
|
||||
return response()->json(Song::findOrFail($id)->lyrics);
|
||||
}
|
||||
}
|
81
app/Http/Controllers/API/UserController.php
Normal file
81
app/Http/Controllers/API/UserController.php
Normal file
|
@ -0,0 +1,81 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\API;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Http\Requests\API\UserStoreRequest;
|
||||
use App\Http\Requests\API\UserUpdateRequest;
|
||||
use App\Http\Requests\API\ProfileUpdateRequest;
|
||||
use Hash;
|
||||
|
||||
class UserController extends Controller
|
||||
{
|
||||
/**
|
||||
* Create a new user.
|
||||
*
|
||||
* @param UserStoreRequest $request
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function store(UserStoreRequest $request)
|
||||
{
|
||||
return response()->json(User::create([
|
||||
'name' => $request->input('name'),
|
||||
'email' => $request->input('email'),
|
||||
'password' => Hash::make($request->input('password')),
|
||||
]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a user.
|
||||
*
|
||||
* @param UserUpdateRequest $request
|
||||
* @param int $id
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function update(UserUpdateRequest $request, $id)
|
||||
{
|
||||
$data = $request->only('name', 'email');
|
||||
|
||||
if ($password = $request->input('password')) {
|
||||
$data['password'] = Hash::make($password);
|
||||
}
|
||||
|
||||
return response()->json(User::findOrFail($id)->update($data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a user.
|
||||
*
|
||||
* @param int $id
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function destroy($id)
|
||||
{
|
||||
if (!auth()->user()->is_admin || auth()->user()->id === $id) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
return response()->json(User::destroy($id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the current user's profile.
|
||||
*
|
||||
* @param ProfileUpdateRequest $request
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function updateProfile(ProfileUpdateRequest $request)
|
||||
{
|
||||
$data = $request->only('name', 'email');
|
||||
|
||||
if ($password = $request->input('password')) {
|
||||
$data['password'] = Hash::make($password);
|
||||
}
|
||||
|
||||
return response()->json(auth()->user()->update($data));
|
||||
}
|
||||
}
|
68
app/Http/Controllers/Auth/AuthController.php
Normal file
68
app/Http/Controllers/Auth/AuthController.php
Normal file
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\User;
|
||||
use Validator;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Foundation\Auth\ThrottlesLogins;
|
||||
use Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers;
|
||||
|
||||
class AuthController extends Controller
|
||||
{
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Registration & Login Controller
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This controller handles the registration of new users, as well as the
|
||||
| authentication of existing users. By default, this controller uses
|
||||
| a simple trait to add these behaviors. Why don't you explore it?
|
||||
|
|
||||
*/
|
||||
|
||||
protected $redirectPath = '/♫';
|
||||
protected $loginPath = '/login';
|
||||
|
||||
use AuthenticatesAndRegistersUsers, ThrottlesLogins;
|
||||
|
||||
/**
|
||||
* Create a new authentication controller instance.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('guest', ['except' => 'getLogout']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a validator for an incoming registration request.
|
||||
*
|
||||
* @param array $data
|
||||
*
|
||||
* @return \Illuminate\Contracts\Validation\Validator
|
||||
*/
|
||||
protected function validator(array $data)
|
||||
{
|
||||
return Validator::make($data, [
|
||||
'name' => 'required|max:255',
|
||||
'email' => 'required|email|max:255|unique:users',
|
||||
'password' => 'required|confirmed',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new user instance after a valid registration.
|
||||
*
|
||||
* @param array $data
|
||||
*
|
||||
* @return User
|
||||
*/
|
||||
protected function create(array $data)
|
||||
{
|
||||
return User::create([
|
||||
'name' => $data['name'],
|
||||
'email' => $data['email'],
|
||||
'password' => bcrypt($data['password']),
|
||||
]);
|
||||
}
|
||||
}
|
30
app/Http/Controllers/Auth/PasswordController.php
Normal file
30
app/Http/Controllers/Auth/PasswordController.php
Normal file
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Foundation\Auth\ResetsPasswords;
|
||||
|
||||
class PasswordController extends Controller
|
||||
{
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Password Reset Controller
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This controller is responsible for handling password reset requests
|
||||
| and uses a simple trait to include this behavior. You're free to
|
||||
| explore this trait and override any methods you wish to tweak.
|
||||
|
|
||||
*/
|
||||
|
||||
use ResetsPasswords;
|
||||
|
||||
/**
|
||||
* Create a new password controller instance.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('guest');
|
||||
}
|
||||
}
|
13
app/Http/Controllers/Controller.php
Normal file
13
app/Http/Controllers/Controller.php
Normal file
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Foundation\Bus\DispatchesJobs;
|
||||
use Illuminate\Routing\Controller as BaseController;
|
||||
use Illuminate\Foundation\Validation\ValidatesRequests;
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
|
||||
abstract class Controller extends BaseController
|
||||
{
|
||||
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
|
||||
}
|
42
app/Http/Kernel.php
Normal file
42
app/Http/Kernel.php
Normal file
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http;
|
||||
|
||||
use App\Http\Middleware\Authenticate;
|
||||
use App\Http\Middleware\EncryptCookies;
|
||||
use App\Http\Middleware\RedirectIfAuthenticated;
|
||||
use App\Http\Middleware\VerifyCsrfToken;
|
||||
use Illuminate\Auth\Middleware\AuthenticateWithBasicAuth;
|
||||
use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse;
|
||||
use Illuminate\Foundation\Http\Kernel as HttpKernel;
|
||||
use Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode;
|
||||
use Illuminate\Session\Middleware\StartSession;
|
||||
use Illuminate\View\Middleware\ShareErrorsFromSession;
|
||||
|
||||
class Kernel extends HttpKernel
|
||||
{
|
||||
/**
|
||||
* The application's global HTTP middleware stack.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $middleware = [
|
||||
CheckForMaintenanceMode::class,
|
||||
EncryptCookies::class,
|
||||
AddQueuedCookiesToResponse::class,
|
||||
StartSession::class,
|
||||
ShareErrorsFromSession::class,
|
||||
VerifyCsrfToken::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* The application's route middleware.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $routeMiddleware = [
|
||||
'auth' => Authenticate::class,
|
||||
'auth.basic' => AuthenticateWithBasicAuth::class,
|
||||
'guest' => RedirectIfAuthenticated::class,
|
||||
];
|
||||
}
|
47
app/Http/Middleware/Authenticate.php
Normal file
47
app/Http/Middleware/Authenticate.php
Normal file
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Contracts\Auth\Guard;
|
||||
|
||||
class Authenticate
|
||||
{
|
||||
/**
|
||||
* The Guard implementation.
|
||||
*
|
||||
* @var Guard
|
||||
*/
|
||||
protected $auth;
|
||||
|
||||
/**
|
||||
* Create a new filter instance.
|
||||
*
|
||||
* @param Guard $auth
|
||||
*/
|
||||
public function __construct(Guard $auth)
|
||||
{
|
||||
$this->auth = $auth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle($request, Closure $next)
|
||||
{
|
||||
if ($this->auth->guest()) {
|
||||
if ($request->ajax() || $request->route()->getName() == 'play') {
|
||||
return response('Unauthorized.', 401);
|
||||
} else {
|
||||
return redirect()->guest('login');
|
||||
}
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
17
app/Http/Middleware/EncryptCookies.php
Normal file
17
app/Http/Middleware/EncryptCookies.php
Normal file
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Cookie\Middleware\EncryptCookies as BaseEncrypter;
|
||||
|
||||
class EncryptCookies extends BaseEncrypter
|
||||
{
|
||||
/**
|
||||
* The names of the cookies that should not be encrypted.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $except = [
|
||||
//
|
||||
];
|
||||
}
|
43
app/Http/Middleware/RedirectIfAuthenticated.php
Normal file
43
app/Http/Middleware/RedirectIfAuthenticated.php
Normal file
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Contracts\Auth\Guard;
|
||||
|
||||
class RedirectIfAuthenticated
|
||||
{
|
||||
/**
|
||||
* The Guard implementation.
|
||||
*
|
||||
* @var Guard
|
||||
*/
|
||||
protected $auth;
|
||||
|
||||
/**
|
||||
* Create a new filter instance.
|
||||
*
|
||||
* @param Guard $auth
|
||||
*/
|
||||
public function __construct(Guard $auth)
|
||||
{
|
||||
$this->auth = $auth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle($request, Closure $next)
|
||||
{
|
||||
if ($this->auth->check()) {
|
||||
return redirect('/♫');
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
17
app/Http/Middleware/VerifyCsrfToken.php
Normal file
17
app/Http/Middleware/VerifyCsrfToken.php
Normal file
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as BaseVerifier;
|
||||
|
||||
class VerifyCsrfToken extends BaseVerifier
|
||||
{
|
||||
/**
|
||||
* The URIs that should be excluded from CSRF verification.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $except = [
|
||||
//
|
||||
];
|
||||
}
|
28
app/Http/Requests/API/BatchInteractionRequest.php
Normal file
28
app/Http/Requests/API/BatchInteractionRequest.php
Normal file
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\API;
|
||||
|
||||
class BatchInteractionRequest extends Request
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'ids' => 'required|array',
|
||||
];
|
||||
}
|
||||
}
|
29
app/Http/Requests/API/PlaylistStoreRequest.php
Normal file
29
app/Http/Requests/API/PlaylistStoreRequest.php
Normal file
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\API;
|
||||
|
||||
class PlaylistStoreRequest extends Request
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'name' => 'required',
|
||||
'songs' => 'array',
|
||||
];
|
||||
}
|
||||
}
|
29
app/Http/Requests/API/ProfileUpdateRequest.php
Normal file
29
app/Http/Requests/API/ProfileUpdateRequest.php
Normal file
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\API;
|
||||
|
||||
class ProfileUpdateRequest extends Request
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'name' => 'required',
|
||||
'email' => 'required|email|unique:users,email,'.auth()->user()->id,
|
||||
];
|
||||
}
|
||||
}
|
10
app/Http/Requests/API/Request.php
Normal file
10
app/Http/Requests/API/Request.php
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\API;
|
||||
|
||||
use App\Http\Requests\Request as BaseRequest;
|
||||
|
||||
class Request extends BaseRequest
|
||||
{
|
||||
//
|
||||
}
|
28
app/Http/Requests/API/SettingRequest.php
Normal file
28
app/Http/Requests/API/SettingRequest.php
Normal file
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\API;
|
||||
|
||||
class SettingRequest extends Request
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return auth()->user()->is_admin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'media_path' => 'string|required|valid_path',
|
||||
];
|
||||
}
|
||||
}
|
30
app/Http/Requests/API/UserStoreRequest.php
Normal file
30
app/Http/Requests/API/UserStoreRequest.php
Normal file
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\API;
|
||||
|
||||
class UserStoreRequest extends Request
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return auth()->user()->is_admin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'name' => 'required',
|
||||
'email' => 'required|email|unique:users',
|
||||
'password' => 'required'
|
||||
];
|
||||
}
|
||||
}
|
29
app/Http/Requests/API/UserUpdateRequest.php
Normal file
29
app/Http/Requests/API/UserUpdateRequest.php
Normal file
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\API;
|
||||
|
||||
class UserUpdateRequest extends Request
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return auth()->user()->is_admin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'name' => 'required',
|
||||
'email' => 'required|email|unique:users,email,'.$this->route('user'),
|
||||
];
|
||||
}
|
||||
}
|
10
app/Http/Requests/Request.php
Normal file
10
app/Http/Requests/Request.php
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
abstract class Request extends FormRequest
|
||||
{
|
||||
//
|
||||
}
|
39
app/Http/Streamers/BaseStreamer.php
Normal file
39
app/Http/Streamers/BaseStreamer.php
Normal file
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Streamers;
|
||||
|
||||
use App\Models\Song;
|
||||
|
||||
class BaseStreamer
|
||||
{
|
||||
/**
|
||||
* @var Song|string
|
||||
*/
|
||||
protected $song;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $contentType;
|
||||
|
||||
/**
|
||||
* BaseStreamer constructor.
|
||||
*
|
||||
* @param $song Song|string A Song object, or its ID.
|
||||
*/
|
||||
public function __construct($song)
|
||||
{
|
||||
$this->song = $song instanceof Song ? $song : Song::findOrFail($song);
|
||||
|
||||
if (!file_exists($this->song->path)) {
|
||||
abort(404);
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
116
app/Http/Streamers/PHPStreamer.php
Normal file
116
app/Http/Streamers/PHPStreamer.php
Normal file
|
@ -0,0 +1,116 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Streamers;
|
||||
|
||||
class PHPStreamer extends BaseStreamer implements StreamerInterface
|
||||
{
|
||||
public function __construct($id)
|
||||
{
|
||||
parent::__construct($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
if (isset($_SERVER['HTTP_RANGE'])) {
|
||||
// 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;
|
||||
}
|
||||
|
||||
$range = isset($headers['range']) ? $headers['range'] : false;
|
||||
} 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);
|
||||
|
||||
if (strtolower(trim($param)) != 'bytes') {
|
||||
// Bad request - range unit is not 'bytes'
|
||||
abort(400);
|
||||
}
|
||||
|
||||
$range = explode(',', $range);
|
||||
$range = explode('-', $range[0]); // We only deal with the first requested range
|
||||
|
||||
if (count($range) != 2) {
|
||||
// Bad request - 'bytes' parameter is not valid
|
||||
abort(400);
|
||||
}
|
||||
|
||||
if ($range[0] === '') {
|
||||
// First number missing, return last $range[1] bytes
|
||||
$end = $fileSize - 1;
|
||||
$start = $end - intval($range[0]);
|
||||
} elseif ($range[1] === '') {
|
||||
// Second number missing, return from byte $range[0] to end
|
||||
$start = intval($range[0]);
|
||||
$end = $fileSize - 1;
|
||||
} else {
|
||||
// Both numbers present, return specific range
|
||||
$start = intval($range[0]);
|
||||
$end = intval($range[1]);
|
||||
|
||||
if ($end >= $fileSize || (!$start && (!$end || $end == ($fileSize - 1)))) {
|
||||
// Invalid range/whole file specified, return whole file
|
||||
$partial = false;
|
||||
}
|
||||
}
|
||||
|
||||
$length = $end - $start + 1;
|
||||
} else {
|
||||
// No range requested
|
||||
$partial = false;
|
||||
}
|
||||
|
||||
// Send standard headers
|
||||
header("Content-Type: {$this->contentType}");
|
||||
header("Content-Length: $fileSize");
|
||||
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");
|
||||
|
||||
if (!$fp = fopen($this->song->path, 'r')) {
|
||||
// Error out if we can't read the file
|
||||
abort(500);
|
||||
}
|
||||
|
||||
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;
|
||||
print(fread($fp, $read));
|
||||
}
|
||||
|
||||
fclose($fp);
|
||||
} else {
|
||||
// ...otherwise just send the whole file
|
||||
readfile($this->song->path);
|
||||
}
|
||||
|
||||
exit;
|
||||
}
|
||||
}
|
11
app/Http/Streamers/StreamerInterface.php
Normal file
11
app/Http/Streamers/StreamerInterface.php
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Streamers;
|
||||
|
||||
interface StreamerInterface
|
||||
{
|
||||
/**
|
||||
* Stream the current song.
|
||||
*/
|
||||
public function stream();
|
||||
}
|
23
app/Http/Streamers/XSendFileStreamer.php
Normal file
23
app/Http/Streamers/XSendFileStreamer.php
Normal file
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Streamers;
|
||||
|
||||
class XSendFileStreamer extends BaseStreamer implements StreamerInterface
|
||||
{
|
||||
public function __construct($id)
|
||||
{
|
||||
parent::__construct($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stream the current song using Apache's x_sendfile module.
|
||||
*/
|
||||
public function stream()
|
||||
{
|
||||
header("X-Sendfile: {$this->song->path}");
|
||||
header("Content-Type: {$this->contentType}");
|
||||
header('Content-Disposition: inline; filename="'.basename($this->song->path).'"');
|
||||
|
||||
exit;
|
||||
}
|
||||
}
|
38
app/Http/routes.php
Normal file
38
app/Http/routes.php
Normal file
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
get('login', 'Auth\AuthController@getLogin');
|
||||
post('login', 'Auth\AuthController@postLogin');
|
||||
get('logout', 'Auth\AuthController@getLogout');
|
||||
|
||||
get('/', function () {
|
||||
return redirect('/♫');
|
||||
});
|
||||
|
||||
get('♫', ['middleware' => 'auth', function () {
|
||||
return view('index');
|
||||
}]);
|
||||
|
||||
Route::group(['prefix' => 'api', 'middleware' => 'auth', 'namespace' => 'API'], function () {
|
||||
get('/', function () {
|
||||
// Just acting as a ping service.
|
||||
});
|
||||
|
||||
get('data', 'DataController@index');
|
||||
|
||||
post('settings', 'SettingController@save');
|
||||
|
||||
get('{id}/play', 'SongController@play')->where('id', '[a-f0-9]{32}');
|
||||
|
||||
get('{id}/lyrics', 'SongController@getLyrics')->where('id', '[a-f0-9]{32}');
|
||||
|
||||
post('interaction/play', 'InteractionController@play');
|
||||
post('interaction/like', 'InteractionController@like');
|
||||
post('interaction/batch/like', 'InteractionController@batchLike');
|
||||
post('interaction/batch/unlike', 'InteractionController@batchUnlike');
|
||||
|
||||
resource('playlist', 'PlaylistController');
|
||||
put('playlist/{id}/sync', 'PlaylistController@sync')->where(['id' => '\d+']);
|
||||
|
||||
resource('user', 'UserController');
|
||||
put('me', 'UserController@updateProfile');
|
||||
});
|
21
app/Jobs/Job.php
Normal file
21
app/Jobs/Job.php
Normal file
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
|
||||
abstract class Job
|
||||
{
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Queueable Jobs
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This job base class provides a central location to place any logic that
|
||||
| is shared across all of your jobs. The trait included with the class
|
||||
| provides access to the "onQueue" and "delay" queue helper methods.
|
||||
|
|
||||
*/
|
||||
|
||||
use Queueable;
|
||||
}
|
0
app/Listeners/.gitkeep
Normal file
0
app/Listeners/.gitkeep
Normal file
109
app/Models/Album.php
Normal file
109
app/Models/Album.php
Normal file
|
@ -0,0 +1,109 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
/**
|
||||
* @property string cover The path to the album's cover
|
||||
* @property bool has_cover If the album has a cover image
|
||||
* @property int id
|
||||
*/
|
||||
class Album extends Model
|
||||
{
|
||||
const UNKNOWN_ID = 1;
|
||||
const UNKNOWN_NAME = 'Unknown Album';
|
||||
const UNKNOWN_COVER = 'unknown-album.png';
|
||||
|
||||
protected $guarded = ['id'];
|
||||
protected $hidden = ['created_at', 'updated_at'];
|
||||
|
||||
public function artist()
|
||||
{
|
||||
return $this->belongsTo(Artist::class);
|
||||
}
|
||||
|
||||
public function songs()
|
||||
{
|
||||
return $this->hasMany(Song::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an album using some provided information.
|
||||
*
|
||||
* @param Artist $artist
|
||||
* @param $name
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public static function get(Artist $artist, $name)
|
||||
{
|
||||
// If an empty name is provided, turn it into our "Unknown Album"
|
||||
$name = $name ?: self::UNKNOWN_NAME;
|
||||
|
||||
$album = self::firstOrCreate([
|
||||
'artist_id' => $artist->id,
|
||||
'name' => $name,
|
||||
]);
|
||||
|
||||
return $album;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a cover from provided data.
|
||||
*
|
||||
* @param array $cover The cover data in array format, extracted by getID3.
|
||||
* For example:
|
||||
* [
|
||||
* 'data' => '<binary data>',
|
||||
* 'image_mime' => 'image/png',
|
||||
* 'image_width' => 512,
|
||||
* 'image_height' => 512,
|
||||
* 'imagetype' => 'PNG', // not always present
|
||||
* 'picturetype' => 'Other',
|
||||
* 'description' => '',
|
||||
* 'datalength' => 7627,
|
||||
* ]
|
||||
*/
|
||||
public function generateCover(array $cover)
|
||||
{
|
||||
$extension = explode('/', $cover['image_mime']);
|
||||
$fileName = uniqid().'.'.strtolower($extension[1]);
|
||||
$coverPath = app()->publicPath().'/img/covers/'.$fileName;
|
||||
|
||||
file_put_contents($coverPath, $cover['data']);
|
||||
|
||||
$this->update(['cover' => $fileName]);
|
||||
}
|
||||
|
||||
public function setCoverAttribute($value)
|
||||
{
|
||||
$this->attributes['cover'] = $value ?: self::UNKNOWN_COVER;
|
||||
}
|
||||
|
||||
public function getCoverAttribute($value)
|
||||
{
|
||||
return '/public/img/covers/'.($value ?: self::UNKNOWN_COVER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the current album has a cover.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getHasCoverAttribute()
|
||||
{
|
||||
return $this->cover !== $this->getCoverAttribute(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sometimes the tags extracted from getID3 are HTML entity encoded.
|
||||
* This makes sure they are always sane.
|
||||
*
|
||||
* @param $value
|
||||
*/
|
||||
public function setNameAttribute($value)
|
||||
{
|
||||
$this->attributes['name'] = html_entity_decode($value);
|
||||
}
|
||||
}
|
54
app/Models/Artist.php
Normal file
54
app/Models/Artist.php
Normal file
|
@ -0,0 +1,54 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
/**
|
||||
* @property int id The model ID
|
||||
*/
|
||||
class Artist extends Model
|
||||
{
|
||||
const UNKNOWN_ID = 1;
|
||||
const UNKNOWN_NAME = 'Unknown Artist';
|
||||
|
||||
protected $guarded = ['id'];
|
||||
|
||||
protected $hidden = ['created_at', 'updated_at'];
|
||||
|
||||
public function albums()
|
||||
{
|
||||
return $this->hasMany(Album::class);
|
||||
}
|
||||
|
||||
public function getNameAttribute($value)
|
||||
{
|
||||
return $value ?: self::UNKNOWN_NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an Artist object from their name.
|
||||
* If such is not found, a new artist will be created.
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return Artist
|
||||
*/
|
||||
public static function get($name)
|
||||
{
|
||||
$name = trim($name) ?: self::UNKNOWN_NAME;
|
||||
|
||||
return self::firstOrCreate(compact('name'), compact('name'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sometimes the tags extracted from getID3 are HTML entity encoded.
|
||||
* This makes sure they are always sane.
|
||||
*
|
||||
* @param $value
|
||||
*/
|
||||
public function setNameAttribute($value)
|
||||
{
|
||||
$this->attributes['name'] = html_entity_decode($value);
|
||||
}
|
||||
}
|
132
app/Models/Interaction.php
Normal file
132
app/Models/Interaction.php
Normal file
|
@ -0,0 +1,132 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use App\Traits\CanFilterByUser;
|
||||
use DB;
|
||||
|
||||
/**
|
||||
* @property bool liked
|
||||
* @property int play_count
|
||||
*/
|
||||
class Interaction extends Model
|
||||
{
|
||||
use CanFilterByUser;
|
||||
|
||||
protected $casts = [
|
||||
'liked' => 'boolean',
|
||||
'play_count' => 'integer',
|
||||
];
|
||||
|
||||
protected $guarded = ['id'];
|
||||
|
||||
protected $hidden = ['user_id', 'created_at', 'updated_at'];
|
||||
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
|
||||
public function song()
|
||||
{
|
||||
return $this->belongsTo(Song::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Increase the number of times a song is played by a user.
|
||||
*
|
||||
* @param string $songId
|
||||
* @param int|null $userId
|
||||
*
|
||||
* @return Interaction
|
||||
*/
|
||||
public static function increasePlayCount($songId, $userId = null)
|
||||
{
|
||||
$interaction = self::firstOrCreate([
|
||||
'song_id' => $songId,
|
||||
'user_id' => $userId ?: auth()->user()->id,
|
||||
]);
|
||||
|
||||
if (!$interaction->exists) {
|
||||
$interaction->liked = false;
|
||||
}
|
||||
|
||||
++$interaction->play_count;
|
||||
$interaction->save();
|
||||
|
||||
return $interaction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Like or unlike a song on behalf of a user.
|
||||
*
|
||||
* @param string $songId
|
||||
* @param int|null $userId
|
||||
*
|
||||
* @return Interaction
|
||||
*/
|
||||
public static function toggleLike($songId, $userId = null)
|
||||
{
|
||||
$interaction = self::firstOrCreate([
|
||||
'song_id' => $songId,
|
||||
'user_id' => $userId ?: auth()->user()->id,
|
||||
]);
|
||||
|
||||
if (!$interaction->exists) {
|
||||
$interaction->play_count = 0;
|
||||
}
|
||||
|
||||
$interaction->liked = !$interaction->liked;
|
||||
$interaction->save();
|
||||
|
||||
return $interaction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Like several songs at once.
|
||||
*
|
||||
* @param array $songIds
|
||||
* @param int|null $userId
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function batchLike(array $songIds, $userId = null)
|
||||
{
|
||||
$result = [];
|
||||
|
||||
foreach ($songIds as $songId) {
|
||||
$interaction = self::firstOrCreate([
|
||||
'song_id' => $songId,
|
||||
'user_id' => $userId ?: auth()->user()->id,
|
||||
]);
|
||||
|
||||
if (!$interaction->exists) {
|
||||
$interaction->play_count = 0;
|
||||
}
|
||||
|
||||
$interaction->liked = true;
|
||||
$interaction->save();
|
||||
|
||||
$result[] = $interaction;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlike several songs at once.
|
||||
*
|
||||
* @param array $songIds
|
||||
* @param int|null $userId
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static function batchUnlike(array $songIds, $userId = null)
|
||||
{
|
||||
return DB::table('interactions')
|
||||
->whereIn('song_id', $songIds)
|
||||
->where('user_id', $userId ?: auth()->user()->id)
|
||||
->update(['liked' => false]);
|
||||
}
|
||||
}
|
29
app/Models/Playlist.php
Normal file
29
app/Models/Playlist.php
Normal file
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use App\Traits\CanFilterByUser;
|
||||
|
||||
class Playlist extends Model
|
||||
{
|
||||
use CanFilterByUser;
|
||||
|
||||
protected $hidden = ['user_id', 'created_at', 'updated_at'];
|
||||
|
||||
protected $guarded = ['id'];
|
||||
|
||||
protected $casts = [
|
||||
'user_id' => 'int',
|
||||
];
|
||||
|
||||
public function songs()
|
||||
{
|
||||
return $this->belongsToMany(Song::class);
|
||||
}
|
||||
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
}
|
69
app/Models/Setting.php
Normal file
69
app/Models/Setting.php
Normal file
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Facades\Media;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
|
||||
class Setting extends Model
|
||||
{
|
||||
protected $primaryKey = 'key';
|
||||
|
||||
public $timestamps = false;
|
||||
|
||||
protected $guarded = [];
|
||||
|
||||
/**
|
||||
* Get a setting value.
|
||||
*
|
||||
* @param string $key
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public static function get($key)
|
||||
{
|
||||
if ($record = self::find($key)) {
|
||||
return $record->value;
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a setting (no pun) value.
|
||||
*
|
||||
* @param string|array $key The key of the setting, or an associative array of settings,
|
||||
* in which case $value will be discarded.
|
||||
* @param mixed $value
|
||||
*
|
||||
*/
|
||||
public static function set($key, $value = null)
|
||||
{
|
||||
if (is_array($key)) {
|
||||
foreach ($key as $k => $v) {
|
||||
self::set($k, $v);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
self::updateOrCreate(compact('key'), compact('value'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize the setting value before saving into the database.
|
||||
* This makes settings more flexible.
|
||||
*
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function setValueAttribute($value)
|
||||
{
|
||||
$this->attributes['value'] = serialize($value);
|
||||
}
|
||||
|
||||
public function getValueAttribute($value)
|
||||
{
|
||||
return unserialize($value);
|
||||
}
|
||||
}
|
68
app/Models/Song.php
Normal file
68
app/Models/Song.php
Normal file
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
/**
|
||||
* @property string path
|
||||
*/
|
||||
class Song extends Model
|
||||
{
|
||||
protected $guarded = [];
|
||||
|
||||
/**
|
||||
* Attributes to be hidden from JSON outputs.
|
||||
* Here we specify to hide lyrics as well to save some bandwidth (actually, lots of it).
|
||||
* Lyrics can then be queried on demand.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $hidden = ['lyrics', 'created_at', 'updated_at', 'path', 'mtime'];
|
||||
|
||||
public function album()
|
||||
{
|
||||
return $this->belongsTo(Album::class);
|
||||
}
|
||||
|
||||
public function playlists()
|
||||
{
|
||||
return $this->belongsToMany(Playlist::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sometimes the tags extracted from getID3 are HTML entity encoded.
|
||||
* This makes sure they are always sane.
|
||||
*
|
||||
* @param $value
|
||||
*/
|
||||
public function setTitleAttribute($value)
|
||||
{
|
||||
$this->attributes['title'] = html_entity_decode($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Some songs don't have a title.
|
||||
* Fall back to the file name (without extension) for such.
|
||||
*
|
||||
* @param $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTitleAttribute($value)
|
||||
{
|
||||
return $value ?: pathinfo($this->path, PATHINFO_FILENAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the lyrics for displaying.
|
||||
*
|
||||
* @param $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLyricsAttribute($value)
|
||||
{
|
||||
return nl2br($value);
|
||||
}
|
||||
}
|
53
app/Models/User.php
Normal file
53
app/Models/User.php
Normal file
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Auth\Authenticatable;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Auth\Passwords\CanResetPassword;
|
||||
use Illuminate\Foundation\Auth\Access\Authorizable;
|
||||
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
|
||||
use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
|
||||
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
|
||||
|
||||
class User extends Model implements AuthenticatableContract,
|
||||
AuthorizableContract,
|
||||
CanResetPasswordContract
|
||||
{
|
||||
use Authenticatable, Authorizable, CanResetPassword;
|
||||
|
||||
/**
|
||||
* The database table used by the model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $table = 'users';
|
||||
|
||||
/**
|
||||
* The attributes that are protected from mass assign.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $guarded = ['id'];
|
||||
|
||||
protected $casts = [
|
||||
'is_admin' => 'bool',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes excluded from the model's JSON form.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $hidden = ['password', 'remember_token', 'created_at', 'updated_at' ];
|
||||
|
||||
public function playlists()
|
||||
{
|
||||
return $this->hasMany(Playlist::class);
|
||||
}
|
||||
|
||||
public function interactions()
|
||||
{
|
||||
return $this->hasMany(Interaction::class);
|
||||
}
|
||||
}
|
0
app/Policies/.gitkeep
Normal file
0
app/Policies/.gitkeep
Normal file
32
app/Providers/AppServiceProvider.php
Normal file
32
app/Providers/AppServiceProvider.php
Normal file
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
class AppServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Bootstrap any application services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
// Add some custom validation rules
|
||||
Validator::extend('valid_path', function($attribute, $value, $parameters, $validator) {
|
||||
return (is_dir($value) && is_readable($value));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Register any application services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
31
app/Providers/AuthServiceProvider.php
Normal file
31
app/Providers/AuthServiceProvider.php
Normal file
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Contracts\Auth\Access\Gate as GateContract;
|
||||
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
|
||||
|
||||
class AuthServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* The policy mappings for the application.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $policies = [
|
||||
'App\Model' => 'App\Policies\ModelPolicy',
|
||||
];
|
||||
|
||||
/**
|
||||
* Register any application authentication / authorization services.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Auth\Access\Gate $gate
|
||||
* @return void
|
||||
*/
|
||||
public function boot(GateContract $gate)
|
||||
{
|
||||
$this->registerPolicies($gate);
|
||||
|
||||
//
|
||||
}
|
||||
}
|
45
app/Providers/EventServiceProvider.php
Normal file
45
app/Providers/EventServiceProvider.php
Normal file
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use App\Facades\Media;
|
||||
use App\Models\Song;
|
||||
use App\Models\Album;
|
||||
use Illuminate\Contracts\Events\Dispatcher as DispatcherContract;
|
||||
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
|
||||
|
||||
class EventServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* The event listener mappings for the application.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $listen = [
|
||||
'App\Events\SomeEvent' => [
|
||||
'App\Listeners\EventListener',
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* Register any other events for your application.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Events\Dispatcher $events
|
||||
*/
|
||||
public function boot(DispatcherContract $events)
|
||||
{
|
||||
parent::boot($events);
|
||||
|
||||
// Generate a unique hash for a song from its path to be the ID
|
||||
Song::creating(function ($song) {
|
||||
$song->id = Media::getHash($song->path);
|
||||
});
|
||||
|
||||
// Remove the cover file if the album is deleted
|
||||
Album::deleted(function ($album) {
|
||||
if ($album->hasCover) {
|
||||
@unlink(app()->publicPath().'/img/covers/'.$album->cover);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
31
app/Providers/MediaServiceProvider.php
Normal file
31
app/Providers/MediaServiceProvider.php
Normal file
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use App\Services\Media;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
class MediaServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Bootstrap the application services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the application services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
app()->singleton('Media', function() {
|
||||
return new Media();
|
||||
});
|
||||
}
|
||||
}
|
44
app/Providers/RouteServiceProvider.php
Normal file
44
app/Providers/RouteServiceProvider.php
Normal file
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Routing\Router;
|
||||
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
|
||||
|
||||
class RouteServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* This namespace is applied to the controller routes in your routes file.
|
||||
*
|
||||
* In addition, it is set as the URL generator's root namespace.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $namespace = 'App\Http\Controllers';
|
||||
|
||||
/**
|
||||
* Define your route model bindings, pattern filters, etc.
|
||||
*
|
||||
* @param \Illuminate\Routing\Router $router
|
||||
* @return void
|
||||
*/
|
||||
public function boot(Router $router)
|
||||
{
|
||||
//
|
||||
|
||||
parent::boot($router);
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the routes for the application.
|
||||
*
|
||||
* @param \Illuminate\Routing\Router $router
|
||||
* @return void
|
||||
*/
|
||||
public function map(Router $router)
|
||||
{
|
||||
$router->group(['namespace' => $this->namespace], function ($router) {
|
||||
require app_path('Http/routes.php');
|
||||
});
|
||||
}
|
||||
}
|
231
app/Services/Media.php
Normal file
231
app/Services/Media.php
Normal file
|
@ -0,0 +1,231 @@
|
|||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\Album;
|
||||
use App\Models\Artist;
|
||||
use App\Models\Setting;
|
||||
use App\Models\Song;
|
||||
use getID3;
|
||||
use getid3_lib;
|
||||
use Exception;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Symfony\Component\Finder\SplFileInfo;
|
||||
use Symfony\Component\Finder\Finder;
|
||||
use App\Console\Commands\SyncMedia;
|
||||
|
||||
class Media
|
||||
{
|
||||
/**
|
||||
* @var getID3
|
||||
*/
|
||||
protected $getID3;
|
||||
|
||||
protected $guarded = [];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->setGetID3();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync the media. Oh sync the media.
|
||||
*
|
||||
* @param string|null $path
|
||||
* @param SyncMedia $syncCommand The SyncMedia command object, to log to console if executed by artisan.
|
||||
*/
|
||||
public function sync($path = null, SyncMedia $syncCommand = null)
|
||||
{
|
||||
set_time_limit(env('APP_MAX_SCAN_TIME', 600));
|
||||
|
||||
$path = $path ?: Setting::get('media_path');
|
||||
|
||||
$results = [
|
||||
'good' => [], // Updated or added files
|
||||
'bad' => [], // Bad files
|
||||
'ugly' => [], // Unmodified files
|
||||
];
|
||||
|
||||
// For now we only care about mp3 and ogg files.
|
||||
// Support for other formats (AAC?) may be added in the future.
|
||||
$files = Finder::create()->files()->name('/\.(mp3|ogg)$/')->in($path);
|
||||
|
||||
foreach ($files as $file) {
|
||||
$song = $this->syncFile($file);
|
||||
|
||||
if ($song === true) {
|
||||
$results['ugly'][] = $file;
|
||||
} elseif ($song === false) {
|
||||
$results['bad'][] = $file;
|
||||
} else {
|
||||
$results['good'][] = $file;
|
||||
}
|
||||
|
||||
if ($syncCommand) {
|
||||
$syncCommand->logToConsole($file->getPathname(), $song);
|
||||
}
|
||||
}
|
||||
|
||||
// Delete non-existing songs.
|
||||
$hashes = array_map(function ($f) {
|
||||
return $this->getHash($f->getPathname());
|
||||
}, array_merge($results['ugly'], $results['good']));
|
||||
|
||||
Song::whereNotIn('id', $hashes)->delete();
|
||||
|
||||
// Empty albums and artists should be gone as well.
|
||||
$inUseAlbums = Song::select('album_id')->groupBy('album_id')->get()->lists('album_id');
|
||||
$inUseAlbums[] = Album::UNKNOWN_ID;
|
||||
Album::whereNotIn('id', $inUseAlbums)->delete();
|
||||
|
||||
$inUseArtists = Album::select('artist_id')->groupBy('artist_id')->get()->lists('artist_id');
|
||||
$inUseArtists[] = Artist::UNKNOWN_ID;
|
||||
Artist::whereNotIn('id', $inUseArtists)->delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync a song with all available media info against the database.
|
||||
*
|
||||
* @param SplFileInfo $file The SplFileInfo instance of the file.
|
||||
*
|
||||
* @return bool|Song A Song object on success,
|
||||
* true if file existing but unmodified,
|
||||
* or false on error.
|
||||
*/
|
||||
public function syncFile(SplFileInfo $file)
|
||||
{
|
||||
if (!$info = $this->getInfo($file)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$this->isNewOrChanged($file)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$artist = Artist::get($info['artist']);
|
||||
$album = Album::get($artist, $info['album']);
|
||||
|
||||
if ($info['cover'] && !$album->has_cover) {
|
||||
try {
|
||||
$album->generateCover($info['cover']);
|
||||
} catch (Exception $e) {
|
||||
Log::error($e);
|
||||
}
|
||||
}
|
||||
|
||||
$info['album_id'] = $album->id;
|
||||
|
||||
unset($info['artist']);
|
||||
unset($info['album']);
|
||||
unset($info['cover']);
|
||||
|
||||
$song = Song::updateOrCreate(['id' => $this->getHash($file->getPathname())], $info);
|
||||
$song->save();
|
||||
|
||||
return $song;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a media file is new or changed.
|
||||
* A file is considered existing and unchanged only when:
|
||||
* - its hash (ID) can be found in the database, and
|
||||
* - its last modified time is the same with that of the comparing file.
|
||||
*
|
||||
* @param SplFileInfo $file
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function isNewOrChanged(SplFileInfo $file)
|
||||
{
|
||||
return !Song::whereIdAndMtime($this->getHash($file->getPathname()), $file->getMTime())->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get ID3 info from a file.
|
||||
*
|
||||
* @param SplFileInfo $file
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
public function getInfo(SplFileInfo $file)
|
||||
{
|
||||
$info = $this->getID3->analyze($file->getPathname());
|
||||
|
||||
if (isset($info['error'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Copy the available tags over to comment.
|
||||
// This is a helper from getID3, though it doesn't really work well.
|
||||
// We'll still prefer getting ID3v2 tags directly later.
|
||||
// Read on.
|
||||
getid3_lib::CopyTagsToComments($info);
|
||||
|
||||
$props = [
|
||||
'artist' => '',
|
||||
'album' => '',
|
||||
'title' => '',
|
||||
'length' => $info['playtime_seconds'],
|
||||
'lyrics' => '',
|
||||
'cover' => array_get($info, 'comments.picture', [null])[0],
|
||||
'path' => $file->getPathname(),
|
||||
'mtime' => $file->getMTime(),
|
||||
];
|
||||
|
||||
if (!$comments = array_get($info, 'comments_html')) {
|
||||
return $props;
|
||||
}
|
||||
|
||||
// We prefer id3v2 tags over others.
|
||||
if (!$artist = array_get($info, 'tags.id3v2.artist', [null])[0]) {
|
||||
$artist = array_get($comments, 'artist', [''])[0];
|
||||
}
|
||||
|
||||
if (!$album = array_get($info, 'tags.id3v2.album', [null])[0]) {
|
||||
$album = array_get($comments, 'album', [''])[0];
|
||||
}
|
||||
|
||||
if (!$title = array_get($info, 'tags.id3v2.title', [null])[0]) {
|
||||
$title = array_get($comments, 'title', [''])[0];
|
||||
}
|
||||
|
||||
if (!$lyrics = array_get($info, 'tags.id3v2.unsynchronised_lyric', [null])[0]) {
|
||||
$lyrics = array_get($comments, 'unsynchronised_lyric', [''])[0];
|
||||
}
|
||||
|
||||
$props['artist'] = trim($artist);
|
||||
$props['album'] = trim($album);
|
||||
$props['title'] = trim($title);
|
||||
$props['lyrics'] = trim($lyrics);
|
||||
|
||||
return $props;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a unique hash for a file path.
|
||||
*
|
||||
* @param $path
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getHash($path)
|
||||
{
|
||||
return md5(config('app.key').$path);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return getID3
|
||||
*/
|
||||
public function getGetID3()
|
||||
{
|
||||
return $this->getID3;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param getID3 $getID3
|
||||
*/
|
||||
public function setGetID3($getID3 = null)
|
||||
{
|
||||
$this->getID3 = $getID3 ?: new getID3();
|
||||
}
|
||||
}
|
16
app/Traits/CanFilterByUser.php
Normal file
16
app/Traits/CanFilterByUser.php
Normal file
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace App\Traits;
|
||||
|
||||
/**
|
||||
* Indicate that a (Model) object collection can be filtered by the current authenticated user.
|
||||
*
|
||||
* @package App\Traits
|
||||
*/
|
||||
trait CanFilterByUser
|
||||
{
|
||||
public function scopeByCurrentUser($query)
|
||||
{
|
||||
return $query->whereUserId(auth()->user()->id);
|
||||
}
|
||||
}
|
BIN
apple-touch-icon-precomposed.png
Normal file
BIN
apple-touch-icon-precomposed.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
51
artisan
Executable file
51
artisan
Executable file
|
@ -0,0 +1,51 @@
|
|||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Register The Auto Loader
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Composer provides a convenient, automatically generated class loader
|
||||
| for our application. We just need to utilize it! We'll require it
|
||||
| into the script here so that we do not have to worry about the
|
||||
| loading of any our classes "manually". Feels great to relax.
|
||||
|
|
||||
*/
|
||||
|
||||
require __DIR__.'/bootstrap/autoload.php';
|
||||
|
||||
$app = require_once __DIR__.'/bootstrap/app.php';
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Run The Artisan Application
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When we run the console application, the current CLI command will be
|
||||
| executed in this console and the response sent back to a terminal
|
||||
| or another output device for the developers. Here goes nothing!
|
||||
|
|
||||
*/
|
||||
|
||||
$kernel = $app->make(Illuminate\Contracts\Console\Kernel::class);
|
||||
|
||||
$status = $kernel->handle(
|
||||
$input = new Symfony\Component\Console\Input\ArgvInput,
|
||||
new Symfony\Component\Console\Output\ConsoleOutput
|
||||
);
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Shutdown The Application
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Once Artisan has finished running. We will fire off the shutdown events
|
||||
| so that any final work may be done by the application before we shut
|
||||
| down the process. This is the last thing to happen to the request.
|
||||
|
|
||||
*/
|
||||
|
||||
$kernel->terminate($input, $status);
|
||||
|
||||
exit($status);
|
55
bootstrap/app.php
Normal file
55
bootstrap/app.php
Normal file
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Create The Application
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The first thing we will do is create a new Laravel application instance
|
||||
| which serves as the "glue" for all the components of Laravel, and is
|
||||
| the IoC container for the system binding all of the various parts.
|
||||
|
|
||||
*/
|
||||
|
||||
$app = new App\Application(
|
||||
realpath(__DIR__.'/../')
|
||||
);
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Bind Important Interfaces
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Next, we need to bind some important interfaces into the container so
|
||||
| we will be able to resolve them when needed. The kernels serve the
|
||||
| incoming requests to this application from both the web and CLI.
|
||||
|
|
||||
*/
|
||||
|
||||
$app->singleton(
|
||||
Illuminate\Contracts\Http\Kernel::class,
|
||||
App\Http\Kernel::class
|
||||
);
|
||||
|
||||
$app->singleton(
|
||||
Illuminate\Contracts\Console\Kernel::class,
|
||||
App\Console\Kernel::class
|
||||
);
|
||||
|
||||
$app->singleton(
|
||||
Illuminate\Contracts\Debug\ExceptionHandler::class,
|
||||
App\Exceptions\Handler::class
|
||||
);
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Return The Application
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This script returns the application instance. The instance is given to
|
||||
| the calling script so we can separate the building of the instances
|
||||
| from the actual running of the application and sending responses.
|
||||
|
|
||||
*/
|
||||
|
||||
return $app;
|
34
bootstrap/autoload.php
Normal file
34
bootstrap/autoload.php
Normal file
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
define('LARAVEL_START', microtime(true));
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Register The Composer Auto Loader
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Composer provides a convenient, automatically generated class loader
|
||||
| for our application. We just need to utilize it! We'll require it
|
||||
| into the script here so that we do not have to worry about the
|
||||
| loading of any our classes "manually". Feels great to relax.
|
||||
|
|
||||
*/
|
||||
|
||||
require __DIR__.'/../vendor/autoload.php';
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Include The Compiled Class File
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| To dramatically increase your application's performance, you may use a
|
||||
| compiled class file which contains all of the classes commonly used
|
||||
| by a request. The Artisan "optimize" is used to create this file.
|
||||
|
|
||||
*/
|
||||
|
||||
$compiledPath = __DIR__.'/cache/compiled.php';
|
||||
|
||||
if (file_exists($compiledPath)) {
|
||||
require $compiledPath;
|
||||
}
|
2
bootstrap/cache/.gitignore
vendored
Normal file
2
bootstrap/cache/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
*
|
||||
!.gitignore
|
29
bower.json
Normal file
29
bower.json
Normal file
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"name": "koel",
|
||||
"version": "0.0.1",
|
||||
"authors": [
|
||||
"An Phan <me@phanan.net>"
|
||||
],
|
||||
"moduleType": [
|
||||
"es6"
|
||||
],
|
||||
"keywords": [
|
||||
"koel",
|
||||
"audio",
|
||||
"streaming",
|
||||
"mp3"
|
||||
],
|
||||
"license": "MIT",
|
||||
"homepage": "http://koel.phanan.net",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests"
|
||||
],
|
||||
"devDependencies": {
|
||||
"plyr": "~1.3.6",
|
||||
"fontawesome": "~4.5.0"
|
||||
}
|
||||
}
|
54
composer.json
Normal file
54
composer.json
Normal file
|
@ -0,0 +1,54 @@
|
|||
{
|
||||
"name": "phanan/koel",
|
||||
"description": "Personal audio streaming service that works.",
|
||||
"keywords": ["audio", "stream", "mp3"],
|
||||
"license": "MIT",
|
||||
"type": "project",
|
||||
"require": {
|
||||
"php": ">=5.5.9",
|
||||
"laravel/framework": "5.1.*",
|
||||
"james-heinrich/getid3": "^1.9"
|
||||
},
|
||||
"require-dev": {
|
||||
"fzaninotto/faker": "~1.4",
|
||||
"mockery/mockery": "0.9.*",
|
||||
"phpunit/phpunit": "~5.0",
|
||||
"phpspec/phpspec": "~2.1",
|
||||
"barryvdh/laravel-ide-helper": "^2.1",
|
||||
"phanan/cascading-config": "~2.0"
|
||||
},
|
||||
"autoload": {
|
||||
"classmap": [
|
||||
"database"
|
||||
],
|
||||
"psr-4": {
|
||||
"App\\": "app/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"classmap": [
|
||||
"tests/TestCase.php"
|
||||
]
|
||||
},
|
||||
"scripts": {
|
||||
"post-install-cmd": [
|
||||
"php artisan clear-compiled",
|
||||
"php artisan optimize"
|
||||
],
|
||||
"pre-update-cmd": [
|
||||
"php artisan clear-compiled"
|
||||
],
|
||||
"post-update-cmd": [
|
||||
"php artisan optimize"
|
||||
],
|
||||
"post-root-package-install": [
|
||||
"php -r \"copy('.env.example', '.env');\""
|
||||
],
|
||||
"post-create-project-cmd": [
|
||||
"php artisan key:generate"
|
||||
]
|
||||
},
|
||||
"config": {
|
||||
"preferred-install": "dist"
|
||||
}
|
||||
}
|
3241
composer.lock
generated
Normal file
3241
composer.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
206
config/app.php
Normal file
206
config/app.php
Normal file
|
@ -0,0 +1,206 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'tagline' => 'Personal audio streaming service that works.',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Debug Mode
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When your application is in debug mode, detailed error messages with
|
||||
| stack traces will be shown on every error that occurs within your
|
||||
| application. If disabled, a simple generic error page is shown.
|
||||
|
|
||||
*/
|
||||
|
||||
'debug' => env('APP_DEBUG', false),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application URL
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This URL is used by the console to properly generate URLs when using
|
||||
| the Artisan command line tool. You should set this to the root of
|
||||
| your application so that it is used when running Artisan tasks.
|
||||
|
|
||||
*/
|
||||
|
||||
'url' => 'http://localhost',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Timezone
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may specify the default timezone for your application, which
|
||||
| will be used by the PHP date and date-time functions. We have gone
|
||||
| ahead and set this to a sensible default for you out of the box.
|
||||
|
|
||||
*/
|
||||
|
||||
'timezone' => 'UTC',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Locale Configuration
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The application locale determines the default locale that will be used
|
||||
| by the translation service provider. You are free to set this value
|
||||
| to any of the locales which will be supported by the application.
|
||||
|
|
||||
*/
|
||||
|
||||
'locale' => 'en',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Fallback Locale
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The fallback locale determines the locale to use when the current one
|
||||
| is not available. You may change the value to correspond to any of
|
||||
| the language folders that are provided through your application.
|
||||
|
|
||||
*/
|
||||
|
||||
'fallback_locale' => 'en',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Encryption Key
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This key is used by the Illuminate encrypter service and should be set
|
||||
| to a random, 32 character string, otherwise these encrypted strings
|
||||
| will not be safe. Please do this before deploying an application!
|
||||
|
|
||||
*/
|
||||
|
||||
'key' => env('APP_KEY', 'SomeRandomString'),
|
||||
|
||||
'cipher' => 'AES-256-CBC',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Logging Configuration
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may configure the log settings for your application. Out of
|
||||
| the box, Laravel uses the Monolog PHP logging library. This gives
|
||||
| you a variety of powerful log handlers / formatters to utilize.
|
||||
|
|
||||
| Available Settings: "single", "daily", "syslog", "errorlog"
|
||||
|
|
||||
*/
|
||||
|
||||
'log' => env('APP_LOG', 'single'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Autoloaded Service Providers
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The service providers listed here will be automatically loaded on the
|
||||
| request to your application. Feel free to add your own services to
|
||||
| this array to grant expanded functionality to your applications.
|
||||
|
|
||||
*/
|
||||
|
||||
'providers' => [
|
||||
|
||||
/*
|
||||
* Laravel Framework Service Providers...
|
||||
*/
|
||||
Illuminate\Foundation\Providers\ArtisanServiceProvider::class,
|
||||
Illuminate\Auth\AuthServiceProvider::class,
|
||||
Illuminate\Broadcasting\BroadcastServiceProvider::class,
|
||||
Illuminate\Bus\BusServiceProvider::class,
|
||||
Illuminate\Cache\CacheServiceProvider::class,
|
||||
Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class,
|
||||
Illuminate\Routing\ControllerServiceProvider::class,
|
||||
Illuminate\Cookie\CookieServiceProvider::class,
|
||||
Illuminate\Database\DatabaseServiceProvider::class,
|
||||
Illuminate\Encryption\EncryptionServiceProvider::class,
|
||||
Illuminate\Filesystem\FilesystemServiceProvider::class,
|
||||
Illuminate\Foundation\Providers\FoundationServiceProvider::class,
|
||||
Illuminate\Hashing\HashServiceProvider::class,
|
||||
Illuminate\Mail\MailServiceProvider::class,
|
||||
Illuminate\Pagination\PaginationServiceProvider::class,
|
||||
Illuminate\Pipeline\PipelineServiceProvider::class,
|
||||
Illuminate\Queue\QueueServiceProvider::class,
|
||||
Illuminate\Redis\RedisServiceProvider::class,
|
||||
Illuminate\Auth\Passwords\PasswordResetServiceProvider::class,
|
||||
Illuminate\Session\SessionServiceProvider::class,
|
||||
Illuminate\Translation\TranslationServiceProvider::class,
|
||||
Illuminate\Validation\ValidationServiceProvider::class,
|
||||
Illuminate\View\ViewServiceProvider::class,
|
||||
|
||||
PhanAn\CascadingConfig\CascadingConfigServiceProvider::class,
|
||||
Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class,
|
||||
|
||||
/*
|
||||
* Application Service Providers...
|
||||
*/
|
||||
App\Providers\AppServiceProvider::class,
|
||||
App\Providers\AuthServiceProvider::class,
|
||||
App\Providers\EventServiceProvider::class,
|
||||
App\Providers\RouteServiceProvider::class,
|
||||
App\Providers\MediaServiceProvider::class,
|
||||
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Class Aliases
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This array of class aliases will be registered when this application
|
||||
| is started. However, feel free to register as many as you wish as
|
||||
| the aliases are "lazy" loaded so they don't hinder performance.
|
||||
|
|
||||
*/
|
||||
|
||||
'aliases' => [
|
||||
|
||||
'App' => Illuminate\Support\Facades\App::class,
|
||||
'Artisan' => Illuminate\Support\Facades\Artisan::class,
|
||||
'Auth' => Illuminate\Support\Facades\Auth::class,
|
||||
'Blade' => Illuminate\Support\Facades\Blade::class,
|
||||
'Bus' => Illuminate\Support\Facades\Bus::class,
|
||||
'Cache' => Illuminate\Support\Facades\Cache::class,
|
||||
'Config' => Illuminate\Support\Facades\Config::class,
|
||||
'Cookie' => Illuminate\Support\Facades\Cookie::class,
|
||||
'Crypt' => Illuminate\Support\Facades\Crypt::class,
|
||||
'DB' => Illuminate\Support\Facades\DB::class,
|
||||
'Eloquent' => Illuminate\Database\Eloquent\Model::class,
|
||||
'Event' => Illuminate\Support\Facades\Event::class,
|
||||
'File' => Illuminate\Support\Facades\File::class,
|
||||
'Gate' => Illuminate\Support\Facades\Gate::class,
|
||||
'Hash' => Illuminate\Support\Facades\Hash::class,
|
||||
'Input' => Illuminate\Support\Facades\Input::class,
|
||||
'Inspiring' => Illuminate\Foundation\Inspiring::class,
|
||||
'Lang' => Illuminate\Support\Facades\Lang::class,
|
||||
'Log' => Illuminate\Support\Facades\Log::class,
|
||||
'Mail' => Illuminate\Support\Facades\Mail::class,
|
||||
'Password' => Illuminate\Support\Facades\Password::class,
|
||||
'Queue' => Illuminate\Support\Facades\Queue::class,
|
||||
'Redirect' => Illuminate\Support\Facades\Redirect::class,
|
||||
'Redis' => Illuminate\Support\Facades\Redis::class,
|
||||
'Request' => Illuminate\Support\Facades\Request::class,
|
||||
'Response' => Illuminate\Support\Facades\Response::class,
|
||||
'Route' => Illuminate\Support\Facades\Route::class,
|
||||
'Schema' => Illuminate\Support\Facades\Schema::class,
|
||||
'Session' => Illuminate\Support\Facades\Session::class,
|
||||
'Storage' => Illuminate\Support\Facades\Storage::class,
|
||||
'URL' => Illuminate\Support\Facades\URL::class,
|
||||
'Validator' => Illuminate\Support\Facades\Validator::class,
|
||||
'View' => Illuminate\Support\Facades\View::class,
|
||||
|
||||
'Media' => App\Facades\Media::class,
|
||||
|
||||
],
|
||||
|
||||
];
|
67
config/auth.php
Normal file
67
config/auth.php
Normal file
|
@ -0,0 +1,67 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Authentication Driver
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option controls the authentication driver that will be utilized.
|
||||
| This driver manages the retrieval and authentication of the users
|
||||
| attempting to get access to protected areas of your application.
|
||||
|
|
||||
| Supported: "database", "eloquent"
|
||||
|
|
||||
*/
|
||||
|
||||
'driver' => 'eloquent',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Authentication Model
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When using the "Eloquent" authentication driver, we need to know which
|
||||
| Eloquent model should be used to retrieve your users. Of course, it
|
||||
| is often just the "User" model but you may use whatever you like.
|
||||
|
|
||||
*/
|
||||
|
||||
'model' => App\Models\User::class,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Authentication Table
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When using the "Database" authentication driver, we need to know which
|
||||
| table should be used to retrieve your users. We have chosen a basic
|
||||
| default value but you may easily change it to any table you like.
|
||||
|
|
||||
*/
|
||||
|
||||
'table' => 'users',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Password Reset Settings
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may set the options for resetting passwords including the view
|
||||
| that is your password reset e-mail. You can also set the name of the
|
||||
| table that maintains all of the reset tokens for your application.
|
||||
|
|
||||
| The expire time is the number of minutes that the reset token should be
|
||||
| considered valid. This security feature keeps tokens short-lived so
|
||||
| they have less time to be guessed. You may change this as needed.
|
||||
|
|
||||
*/
|
||||
|
||||
'password' => [
|
||||
'email' => 'emails.password',
|
||||
'table' => 'password_resets',
|
||||
'expire' => 60,
|
||||
],
|
||||
|
||||
];
|
52
config/broadcasting.php
Normal file
52
config/broadcasting.php
Normal file
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Broadcaster
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option controls the default broadcaster that will be used by the
|
||||
| framework when an event needs to be broadcast. You may set this to
|
||||
| any of the connections defined in the "connections" array below.
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => env('BROADCAST_DRIVER', 'pusher'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Broadcast Connections
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may define all of the broadcast connections that will be used
|
||||
| to broadcast events to other systems or over websockets. Samples of
|
||||
| each available type of connection are provided inside this array.
|
||||
|
|
||||
*/
|
||||
|
||||
'connections' => [
|
||||
|
||||
'pusher' => [
|
||||
'driver' => 'pusher',
|
||||
'key' => env('PUSHER_KEY'),
|
||||
'secret' => env('PUSHER_SECRET'),
|
||||
'app_id' => env('PUSHER_APP_ID'),
|
||||
'options' => [
|
||||
//
|
||||
],
|
||||
],
|
||||
|
||||
'redis' => [
|
||||
'driver' => 'redis',
|
||||
'connection' => 'default',
|
||||
],
|
||||
|
||||
'log' => [
|
||||
'driver' => 'log',
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
];
|
79
config/cache.php
Normal file
79
config/cache.php
Normal file
|
@ -0,0 +1,79 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Cache Store
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option controls the default cache connection that gets used while
|
||||
| using this caching library. This connection is used when another is
|
||||
| not explicitly specified when executing a given caching function.
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => env('CACHE_DRIVER', 'file'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Cache Stores
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may define all of the cache "stores" for your application as
|
||||
| well as their drivers. You may even define multiple stores for the
|
||||
| same cache driver to group types of items stored in your caches.
|
||||
|
|
||||
*/
|
||||
|
||||
'stores' => [
|
||||
|
||||
'apc' => [
|
||||
'driver' => 'apc',
|
||||
],
|
||||
|
||||
'array' => [
|
||||
'driver' => 'array',
|
||||
],
|
||||
|
||||
'database' => [
|
||||
'driver' => 'database',
|
||||
'table' => 'cache',
|
||||
'connection' => null,
|
||||
],
|
||||
|
||||
'file' => [
|
||||
'driver' => 'file',
|
||||
'path' => storage_path('framework/cache'),
|
||||
],
|
||||
|
||||
'memcached' => [
|
||||
'driver' => 'memcached',
|
||||
'servers' => [
|
||||
[
|
||||
'host' => '127.0.0.1', 'port' => 11211, 'weight' => 100,
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
'redis' => [
|
||||
'driver' => 'redis',
|
||||
'connection' => 'default',
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Cache Key Prefix
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When utilizing a RAM based store such as APC or Memcached, there might
|
||||
| be other applications utilizing the same cache. So, we'll specify a
|
||||
| value to get prefixed to all our keys so we can avoid collisions.
|
||||
|
|
||||
*/
|
||||
|
||||
'prefix' => 'laravel',
|
||||
|
||||
];
|
35
config/compile.php
Normal file
35
config/compile.php
Normal file
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Additional Compiled Classes
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may specify additional classes to include in the compiled file
|
||||
| generated by the `artisan optimize` command. These should be classes
|
||||
| that are included on basically every request into the application.
|
||||
|
|
||||
*/
|
||||
|
||||
'files' => [
|
||||
//
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Compiled File Providers
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may list service providers which define a "compiles" function
|
||||
| that returns additional files that should be compiled, providing an
|
||||
| easy way to get common files from any packages you are utilizing.
|
||||
|
|
||||
*/
|
||||
|
||||
'providers' => [
|
||||
//
|
||||
],
|
||||
|
||||
];
|
126
config/database.php
Normal file
126
config/database.php
Normal file
|
@ -0,0 +1,126 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| PDO Fetch Style
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| By default, database results will be returned as instances of the PHP
|
||||
| stdClass object; however, you may desire to retrieve records in an
|
||||
| array format for simplicity. Here you can tweak the fetch style.
|
||||
|
|
||||
*/
|
||||
|
||||
'fetch' => PDO::FETCH_CLASS,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Database Connection Name
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may specify which of the database connections below you wish
|
||||
| to use as your default connection for all database work. Of course
|
||||
| you may use many connections at once using the Database library.
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => env('DB_CONNECTION', 'mysql'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Database Connections
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here are each of the database connections setup for your application.
|
||||
| Of course, examples of configuring each database platform that is
|
||||
| supported by Laravel is shown below to make development simple.
|
||||
|
|
||||
|
|
||||
| All database work in Laravel is done through the PHP PDO facilities
|
||||
| so make sure you have the driver for your particular database of
|
||||
| choice installed on your machine before you begin development.
|
||||
|
|
||||
*/
|
||||
|
||||
'connections' => [
|
||||
|
||||
'sqlite' => [
|
||||
'driver' => 'sqlite',
|
||||
'database' => database_path('database.sqlite'),
|
||||
'prefix' => '',
|
||||
],
|
||||
|
||||
'mysql' => [
|
||||
'driver' => 'mysql',
|
||||
'host' => env('DB_HOST', 'localhost'),
|
||||
'database' => env('DB_DATABASE', 'forge'),
|
||||
'username' => env('DB_USERNAME', 'forge'),
|
||||
'password' => env('DB_PASSWORD', ''),
|
||||
'charset' => 'utf8',
|
||||
'collation' => 'utf8_unicode_ci',
|
||||
'prefix' => '',
|
||||
'strict' => false,
|
||||
],
|
||||
|
||||
'pgsql' => [
|
||||
'driver' => 'pgsql',
|
||||
'host' => env('DB_HOST', 'localhost'),
|
||||
'database' => env('DB_DATABASE', 'forge'),
|
||||
'username' => env('DB_USERNAME', 'forge'),
|
||||
'password' => env('DB_PASSWORD', ''),
|
||||
'charset' => 'utf8',
|
||||
'prefix' => '',
|
||||
'schema' => 'public',
|
||||
],
|
||||
|
||||
'sqlsrv' => [
|
||||
'driver' => 'sqlsrv',
|
||||
'host' => env('DB_HOST', 'localhost'),
|
||||
'database' => env('DB_DATABASE', 'forge'),
|
||||
'username' => env('DB_USERNAME', 'forge'),
|
||||
'password' => env('DB_PASSWORD', ''),
|
||||
'charset' => 'utf8',
|
||||
'prefix' => '',
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Migration Repository Table
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This table keeps track of all the migrations that have already run for
|
||||
| your application. Using this information, we can determine which of
|
||||
| the migrations on disk haven't actually been run in the database.
|
||||
|
|
||||
*/
|
||||
|
||||
'migrations' => 'migrations',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Redis Databases
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Redis is an open source, fast, and advanced key-value store that also
|
||||
| provides a richer set of commands than a typical key-value systems
|
||||
| such as APC or Memcached. Laravel makes it easy to dig right in.
|
||||
|
|
||||
*/
|
||||
|
||||
'redis' => [
|
||||
|
||||
'cluster' => false,
|
||||
|
||||
'default' => [
|
||||
'host' => '127.0.0.1',
|
||||
'port' => 6379,
|
||||
'database' => 0,
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
];
|
85
config/filesystems.php
Normal file
85
config/filesystems.php
Normal file
|
@ -0,0 +1,85 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Filesystem Disk
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may specify the default filesystem disk that should be used
|
||||
| by the framework. A "local" driver, as well as a variety of cloud
|
||||
| based drivers are available for your choosing. Just store away!
|
||||
|
|
||||
| Supported: "local", "ftp", "s3", "rackspace"
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => 'local',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Cloud Filesystem Disk
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Many applications store files both locally and in the cloud. For this
|
||||
| reason, you may specify a default "cloud" driver here. This driver
|
||||
| will be bound as the Cloud disk implementation in the container.
|
||||
|
|
||||
*/
|
||||
|
||||
'cloud' => 's3',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Filesystem Disks
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may configure as many filesystem "disks" as you wish, and you
|
||||
| may even configure multiple disks of the same driver. Defaults have
|
||||
| been setup for each driver as an example of the required options.
|
||||
|
|
||||
*/
|
||||
|
||||
'disks' => [
|
||||
|
||||
'local' => [
|
||||
'driver' => 'local',
|
||||
'root' => storage_path('app'),
|
||||
],
|
||||
|
||||
'ftp' => [
|
||||
'driver' => 'ftp',
|
||||
'host' => 'ftp.example.com',
|
||||
'username' => 'your-username',
|
||||
'password' => 'your-password',
|
||||
|
||||
// Optional FTP Settings...
|
||||
// 'port' => 21,
|
||||
// 'root' => '',
|
||||
// 'passive' => true,
|
||||
// 'ssl' => true,
|
||||
// 'timeout' => 30,
|
||||
],
|
||||
|
||||
's3' => [
|
||||
'driver' => 's3',
|
||||
'key' => 'your-key',
|
||||
'secret' => 'your-secret',
|
||||
'region' => 'your-region',
|
||||
'bucket' => 'your-bucket',
|
||||
],
|
||||
|
||||
'rackspace' => [
|
||||
'driver' => 'rackspace',
|
||||
'username' => 'your-username',
|
||||
'key' => 'your-key',
|
||||
'container' => 'your-container',
|
||||
'endpoint' => 'https://identity.api.rackspacecloud.com/v2.0/',
|
||||
'region' => 'IAD',
|
||||
'url_type' => 'publicURL',
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
];
|
124
config/mail.php
Normal file
124
config/mail.php
Normal file
|
@ -0,0 +1,124 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Mail Driver
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Laravel supports both SMTP and PHP's "mail" function as drivers for the
|
||||
| sending of e-mail. You may specify which one you're using throughout
|
||||
| your application here. By default, Laravel is setup for SMTP mail.
|
||||
|
|
||||
| Supported: "smtp", "mail", "sendmail", "mailgun", "mandrill", "ses", "log"
|
||||
|
|
||||
*/
|
||||
|
||||
'driver' => env('MAIL_DRIVER', 'smtp'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| SMTP Host Address
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may provide the host address of the SMTP server used by your
|
||||
| applications. A default option is provided that is compatible with
|
||||
| the Mailgun mail service which will provide reliable deliveries.
|
||||
|
|
||||
*/
|
||||
|
||||
'host' => env('MAIL_HOST', 'smtp.mailgun.org'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| SMTP Host Port
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This is the SMTP port used by your application to deliver e-mails to
|
||||
| users of the application. Like the host we have set this value to
|
||||
| stay compatible with the Mailgun e-mail application by default.
|
||||
|
|
||||
*/
|
||||
|
||||
'port' => env('MAIL_PORT', 587),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Global "From" Address
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| You may wish for all e-mails sent by your application to be sent from
|
||||
| the same address. Here, you may specify a name and address that is
|
||||
| used globally for all e-mails that are sent by your application.
|
||||
|
|
||||
*/
|
||||
|
||||
'from' => ['address' => null, 'name' => null],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| E-Mail Encryption Protocol
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may specify the encryption protocol that should be used when
|
||||
| the application send e-mail messages. A sensible default using the
|
||||
| transport layer security protocol should provide great security.
|
||||
|
|
||||
*/
|
||||
|
||||
'encryption' => env('MAIL_ENCRYPTION', 'tls'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| SMTP Server Username
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| If your SMTP server requires a username for authentication, you should
|
||||
| set it here. This will get used to authenticate with your server on
|
||||
| connection. You may also set the "password" value below this one.
|
||||
|
|
||||
*/
|
||||
|
||||
'username' => env('MAIL_USERNAME'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| SMTP Server Password
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may set the password required by your SMTP server to send out
|
||||
| messages from your application. This will be given to the server on
|
||||
| connection so that the application will be able to send messages.
|
||||
|
|
||||
*/
|
||||
|
||||
'password' => env('MAIL_PASSWORD'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Sendmail System Path
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When using the "sendmail" driver to send e-mails, we will need to know
|
||||
| the path to where Sendmail lives on this server. A default path has
|
||||
| been provided here, which will work well on most of your systems.
|
||||
|
|
||||
*/
|
||||
|
||||
'sendmail' => '/usr/sbin/sendmail -bs',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Mail "Pretend"
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When this option is enabled, e-mail will not actually be sent over the
|
||||
| web and will instead be written to your application's logs files so
|
||||
| you may inspect the message. This is great for local development.
|
||||
|
|
||||
*/
|
||||
|
||||
'pretend' => env('MAIL_PRETEND', false),
|
||||
|
||||
];
|
94
config/queue.php
Normal file
94
config/queue.php
Normal file
|
@ -0,0 +1,94 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Queue Driver
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The Laravel queue API supports a variety of back-ends via an unified
|
||||
| API, giving you convenient access to each back-end using the same
|
||||
| syntax for each one. Here you may set the default queue driver.
|
||||
|
|
||||
| Supported: "null", "sync", "database", "beanstalkd",
|
||||
| "sqs", "iron", "redis"
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => env('QUEUE_DRIVER', 'sync'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Queue Connections
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may configure the connection information for each server that
|
||||
| is used by your application. A default configuration has been added
|
||||
| for each back-end shipped with Laravel. You are free to add more.
|
||||
|
|
||||
*/
|
||||
|
||||
'connections' => [
|
||||
|
||||
'sync' => [
|
||||
'driver' => 'sync',
|
||||
],
|
||||
|
||||
'database' => [
|
||||
'driver' => 'database',
|
||||
'table' => 'jobs',
|
||||
'queue' => 'default',
|
||||
'expire' => 60,
|
||||
],
|
||||
|
||||
'beanstalkd' => [
|
||||
'driver' => 'beanstalkd',
|
||||
'host' => 'localhost',
|
||||
'queue' => 'default',
|
||||
'ttr' => 60,
|
||||
],
|
||||
|
||||
'sqs' => [
|
||||
'driver' => 'sqs',
|
||||
'key' => 'your-public-key',
|
||||
'secret' => 'your-secret-key',
|
||||
'queue' => 'your-queue-url',
|
||||
'region' => 'us-east-1',
|
||||
],
|
||||
|
||||
'iron' => [
|
||||
'driver' => 'iron',
|
||||
'host' => 'mq-aws-us-east-1.iron.io',
|
||||
'token' => 'your-token',
|
||||
'project' => 'your-project-id',
|
||||
'queue' => 'your-queue-name',
|
||||
'encrypt' => true,
|
||||
],
|
||||
|
||||
'redis' => [
|
||||
'driver' => 'redis',
|
||||
'connection' => 'default',
|
||||
'queue' => 'default',
|
||||
'expire' => 60,
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Failed Queue Jobs
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| These options configure the behavior of failed queue job logging so you
|
||||
| can control which database and table are used to store the jobs that
|
||||
| have failed. You may change them to any database / table you wish.
|
||||
|
|
||||
*/
|
||||
|
||||
'failed' => [
|
||||
'database' => env('DB_CONNECTION', 'mysql'),
|
||||
'table' => 'failed_jobs',
|
||||
],
|
||||
|
||||
];
|
38
config/services.php
Normal file
38
config/services.php
Normal file
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Third Party Services
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This file is for storing the credentials for third party services such
|
||||
| as Stripe, Mailgun, Mandrill, and others. This file provides a sane
|
||||
| default location for this type of information, allowing packages
|
||||
| to have a conventional place to find your various credentials.
|
||||
|
|
||||
*/
|
||||
|
||||
'mailgun' => [
|
||||
'domain' => env('MAILGUN_DOMAIN'),
|
||||
'secret' => env('MAILGUN_SECRET'),
|
||||
],
|
||||
|
||||
'mandrill' => [
|
||||
'secret' => env('MANDRILL_SECRET'),
|
||||
],
|
||||
|
||||
'ses' => [
|
||||
'key' => env('SES_KEY'),
|
||||
'secret' => env('SES_SECRET'),
|
||||
'region' => 'us-east-1',
|
||||
],
|
||||
|
||||
'stripe' => [
|
||||
'model' => App\User::class,
|
||||
'key' => env('STRIPE_KEY'),
|
||||
'secret' => env('STRIPE_SECRET'),
|
||||
],
|
||||
|
||||
];
|
153
config/session.php
Normal file
153
config/session.php
Normal file
|
@ -0,0 +1,153 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Session Driver
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option controls the default session "driver" that will be used on
|
||||
| requests. By default, we will use the lightweight native driver but
|
||||
| you may specify any of the other wonderful drivers provided here.
|
||||
|
|
||||
| Supported: "file", "cookie", "database", "apc",
|
||||
| "memcached", "redis", "array"
|
||||
|
|
||||
*/
|
||||
|
||||
'driver' => env('SESSION_DRIVER', 'file'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Lifetime
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may specify the number of minutes that you wish the session
|
||||
| to be allowed to remain idle before it expires. If you want them
|
||||
| to immediately expire on the browser closing, set that option.
|
||||
|
|
||||
*/
|
||||
|
||||
'lifetime' => 30*24*60,
|
||||
|
||||
'expire_on_close' => false,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Encryption
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option allows you to easily specify that all of your session data
|
||||
| should be encrypted before it is stored. All encryption will be run
|
||||
| automatically by Laravel and you can use the Session like normal.
|
||||
|
|
||||
*/
|
||||
|
||||
'encrypt' => true,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session File Location
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When using the native session driver, we need a location where session
|
||||
| files may be stored. A default has been set for you but a different
|
||||
| location may be specified. This is only needed for file sessions.
|
||||
|
|
||||
*/
|
||||
|
||||
'files' => storage_path('framework/sessions'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Database Connection
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When using the "database" or "redis" session drivers, you may specify a
|
||||
| connection that should be used to manage these sessions. This should
|
||||
| correspond to a connection in your database configuration options.
|
||||
|
|
||||
*/
|
||||
|
||||
'connection' => null,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Database Table
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When using the "database" session driver, you may specify the table we
|
||||
| should use to manage the sessions. Of course, a sensible default is
|
||||
| provided for you; however, you are free to change this as needed.
|
||||
|
|
||||
*/
|
||||
|
||||
'table' => 'sessions',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Sweeping Lottery
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Some session drivers must manually sweep their storage location to get
|
||||
| rid of old sessions from storage. Here are the chances that it will
|
||||
| happen on a given request. By default, the odds are 2 out of 100.
|
||||
|
|
||||
*/
|
||||
|
||||
'lottery' => [2, 100],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Cookie Name
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may change the name of the cookie used to identify a session
|
||||
| instance by ID. The name specified here will get used every time a
|
||||
| new session cookie is created by the framework for every driver.
|
||||
|
|
||||
*/
|
||||
|
||||
'cookie' => 'remember_me_before_the_war',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Cookie Path
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The session cookie path determines the path for which the cookie will
|
||||
| be regarded as available. Typically, this will be the root path of
|
||||
| your application but you are free to change this when necessary.
|
||||
|
|
||||
*/
|
||||
|
||||
'path' => '/',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Cookie Domain
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may change the domain of the cookie used to identify a session
|
||||
| in your application. This will determine which domains the cookie is
|
||||
| available to in your application. A sensible default has been set.
|
||||
|
|
||||
*/
|
||||
|
||||
'domain' => null,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| HTTPS Only Cookies
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| By setting this option to true, session cookies will only be sent back
|
||||
| to the server if the browser has a HTTPS connection. This will keep
|
||||
| the cookie from being sent to you if it can not be done securely.
|
||||
|
|
||||
*/
|
||||
|
||||
'secure' => false,
|
||||
|
||||
];
|
33
config/view.php
Normal file
33
config/view.php
Normal file
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| View Storage Paths
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Most templating systems load templates from disk. Here you may specify
|
||||
| an array of paths that should be checked for your views. Of course
|
||||
| the usual Laravel view path has already been registered for you.
|
||||
|
|
||||
*/
|
||||
|
||||
'paths' => [
|
||||
realpath(base_path('resources/views')),
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Compiled View Path
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option determines where all the compiled Blade templates will be
|
||||
| stored for your application. Typically, this is within the storage
|
||||
| directory. However, as usual, you are free to change this value.
|
||||
|
|
||||
*/
|
||||
|
||||
'compiled' => realpath(storage_path('framework/views')),
|
||||
|
||||
];
|
1
database/.gitignore
vendored
Normal file
1
database/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
*.sqlite
|
46
database/factories/ModelFactory.php
Normal file
46
database/factories/ModelFactory.php
Normal file
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
|
||||
$factory->define(App\Models\User::class, function ($faker) {
|
||||
return [
|
||||
'name' => $faker->name,
|
||||
'email' => $faker->email,
|
||||
'password' => bcrypt(str_random(10)),
|
||||
'is_admin' => false,
|
||||
'remember_token' => str_random(10),
|
||||
];
|
||||
});
|
||||
|
||||
$factory->defineAs(App\Models\User::class, 'admin', function ($faker) use ($factory) {
|
||||
$user = $factory->raw(App\Models\User::class);
|
||||
|
||||
return array_merge($user, ['is_admin' => true]);
|
||||
});
|
||||
|
||||
$factory->define(App\Models\Artist::class, function ($faker) {
|
||||
return [
|
||||
'name' => $faker->name,
|
||||
];
|
||||
});
|
||||
|
||||
$factory->define(App\Models\Album::class, function ($faker) {
|
||||
return [
|
||||
'name' => $faker->sentence,
|
||||
'cover' => md5(uniqid()).'.jpg',
|
||||
];
|
||||
});
|
||||
|
||||
$factory->define(App\Models\Song::class, function ($faker) {
|
||||
return [
|
||||
'title' => $faker->sentence,
|
||||
'length' => $faker->randomFloat(2, 10, 500),
|
||||
'lyrics' => $faker->paragraph(),
|
||||
'path' => '/tmp/'.uniqid().'.mp3',
|
||||
'mtime' => time(),
|
||||
];
|
||||
});
|
||||
|
||||
$factory->define(App\Models\Playlist::class, function ($faker) {
|
||||
return [
|
||||
'name' => $faker->name,
|
||||
];
|
||||
});
|
0
database/migrations/.gitkeep
Normal file
0
database/migrations/.gitkeep
Normal file
35
database/migrations/2014_10_12_000000_create_users_table.php
Normal file
35
database/migrations/2014_10_12_000000_create_users_table.php
Normal file
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CreateUsersTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('users', function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->string('name');
|
||||
$table->string('email')->unique();
|
||||
$table->string('password', 60);
|
||||
$table->boolean('is_admin')->default(false);
|
||||
$table->rememberToken();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::drop('users');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CreatePasswordResetsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('password_resets', function (Blueprint $table) {
|
||||
$table->string('email')->index();
|
||||
$table->string('token')->index();
|
||||
$table->timestamp('created_at');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::drop('password_resets');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CreateArtistsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('artists', function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->string('name')->unique();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::drop('artists');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CreateAlbumsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('albums', function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->integer('artist_id')->unsigned();
|
||||
$table->string('name');
|
||||
$table->string('cover')->default('');
|
||||
$table->timestamps();
|
||||
|
||||
$table->foreign('artist_id')->references('id')->on('artists')->onDelete('cascade');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::drop('albums');
|
||||
}
|
||||
}
|
39
database/migrations/2015_11_23_074713_create_songs_table.php
Normal file
39
database/migrations/2015_11_23_074713_create_songs_table.php
Normal file
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CreateSongsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('songs', function (Blueprint $table) {
|
||||
$table->string('id', 32);
|
||||
$table->integer('album_id')->unsigned();
|
||||
$table->string('title');
|
||||
$table->float('length');
|
||||
$table->text('lyrics');
|
||||
$table->text('path');
|
||||
$table->integer('mtime');
|
||||
$table->timestamps();
|
||||
|
||||
$table->primary('id');
|
||||
$table->foreign('album_id')->references('id')->on('albums');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::drop('songs');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CreatePlaylistsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('playlists', function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->integer('user_id')->unsigned();
|
||||
$table->string('name');
|
||||
$table->timestamps();
|
||||
|
||||
$table->foreign('user_id')->references('id')->on('users');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::drop('playlists');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CreateInteractionsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('interactions', function (Blueprint $table) {
|
||||
$table->bigIncrements('id');
|
||||
$table->integer('user_id')->unsigned();
|
||||
$table->string('song_id', 32);
|
||||
$table->boolean('liked')->default(false);
|
||||
$table->integer('play_count')->default(0);
|
||||
$table->timestamps();
|
||||
|
||||
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
|
||||
$table->foreign('song_id')->references('id')->on('songs')->onDelete('cascade');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::drop('interactions');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CreatePlaylistSongTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('playlist_song', function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->integer('playlist_id')->unsigned();
|
||||
$table->string('song_id', 32);
|
||||
|
||||
$table->foreign('playlist_id')->references('id')->on('playlists')->onDelete('cascade');
|
||||
$table->foreign('song_id')->references('id')->on('songs')->onDelete('cascade');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::drop('playlist_song');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CreateSettingsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('settings', function (Blueprint $table) {
|
||||
$table->string('key');
|
||||
$table->text('value');
|
||||
|
||||
$table->primary('key');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::drop('settings');
|
||||
}
|
||||
}
|
0
database/seeds/.gitkeep
Normal file
0
database/seeds/.gitkeep
Normal file
21
database/seeds/AlbumTableSeeder.php
Normal file
21
database/seeds/AlbumTableSeeder.php
Normal file
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
use App\Models\Album;
|
||||
use App\Models\Artist;
|
||||
|
||||
class AlbumTableSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
Album::create([
|
||||
'id' => Album::UNKNOWN_ID,
|
||||
'artist_id' => Artist::UNKNOWN_ID,
|
||||
'name' => Album::UNKNOWN_NAME,
|
||||
'cover' => Album::UNKNOWN_COVER,
|
||||
]);
|
||||
}
|
||||
}
|
18
database/seeds/ArtistTableSeeder.php
Normal file
18
database/seeds/ArtistTableSeeder.php
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
use App\Models\Artist;
|
||||
|
||||
class ArtistTableSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
Artist::create([
|
||||
'id' => Artist::UNKNOWN_ID,
|
||||
'name' => Artist::UNKNOWN_NAME,
|
||||
]);
|
||||
}
|
||||
}
|
24
database/seeds/DatabaseSeeder.php
Normal file
24
database/seeds/DatabaseSeeder.php
Normal file
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class DatabaseSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
Model::unguard();
|
||||
|
||||
// This must be run first, to check for dependencies
|
||||
$this->call(UserTableSeeder::class);
|
||||
|
||||
$this->call(ArtistTableSeeder::class);
|
||||
$this->call(AlbumTableSeeder::class);
|
||||
$this->call(SettingTableSeeder::class);
|
||||
|
||||
Model::reguard();
|
||||
}
|
||||
}
|
17
database/seeds/SettingTableSeeder.php
Normal file
17
database/seeds/SettingTableSeeder.php
Normal file
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
use App\Models\Setting;
|
||||
|
||||
class SettingTableSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
Setting::set('media_path', '');
|
||||
}
|
||||
}
|
29
database/seeds/UserTableSeeder.php
Normal file
29
database/seeds/UserTableSeeder.php
Normal file
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
use App\Models\User;
|
||||
|
||||
class UserTableSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
if (!env('ADMIN_NAME') || !env('ADMIN_EMAIL') || !env('ADMIN_PASSWORD')) {
|
||||
$this->command->error('Please fill in initial admin details in .env file first.');
|
||||
abort(422);
|
||||
}
|
||||
|
||||
User::create([
|
||||
'name' => env('ADMIN_NAME'),
|
||||
'email' => env('ADMIN_EMAIL'),
|
||||
'password' => Hash::make(env('ADMIN_PASSWORD')),
|
||||
'is_admin' => true,
|
||||
]);
|
||||
|
||||
if (app()->environment() !== 'testing') {
|
||||
$this->command->info('Admin user created. You can (and should) remove the auth details from .env now.');
|
||||
}
|
||||
}
|
||||
}
|
BIN
favicon.ico
Normal file
BIN
favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
23
gulpfile.js
Normal file
23
gulpfile.js
Normal file
|
@ -0,0 +1,23 @@
|
|||
require('events').EventEmitter.defaultMaxListeners = 30;
|
||||
|
||||
var elixir = require('laravel-elixir');
|
||||
require('laravel-elixir-vueify');
|
||||
|
||||
elixir(function (mix) {
|
||||
mix.browserify('main.js');
|
||||
mix.sass('app.scss');
|
||||
|
||||
mix.copy('resources/assets/img', 'public/img')
|
||||
.copy('bower_components/fontawesome/fonts', 'public/build/fonts');
|
||||
|
||||
mix.scripts([
|
||||
'bower_components/plyr/dist/plyr.js'
|
||||
], 'public/js/vendors.js', './')
|
||||
.styles([
|
||||
'resources/assets/css/**/*.css',
|
||||
'bower_components/fontawesome/css/font-awesome.min.css',
|
||||
'bower_components/plyr/dist/plyr.css'
|
||||
], 'public/css/vendors.css', './');
|
||||
|
||||
mix.version(['css/vendors.css', 'css/app.css', 'js/vendors.js', 'js/main.js']);
|
||||
});
|
58
index.php
Normal file
58
index.php
Normal file
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Laravel - A PHP Framework For Web Artisans
|
||||
*
|
||||
* @package Laravel
|
||||
* @author Taylor Otwell <taylorotwell@gmail.com>
|
||||
*/
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Register The Auto Loader
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Composer provides a convenient, automatically generated class loader for
|
||||
| our application. We just need to utilize it! We'll simply require it
|
||||
| into the script here so that we don't have to worry about manual
|
||||
| loading any of our classes later on. It feels nice to relax.
|
||||
|
|
||||
*/
|
||||
|
||||
require __DIR__.'/bootstrap/autoload.php';
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Turn On The Lights
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| We need to illuminate PHP development, so let us turn on the lights.
|
||||
| This bootstraps the framework and gets it ready for use, then it
|
||||
| will load up this application so that we can run it and send
|
||||
| the responses back to the browser and delight our users.
|
||||
|
|
||||
*/
|
||||
|
||||
$app = require_once __DIR__.'/bootstrap/app.php';
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Run The Application
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Once we have the application, we can handle the incoming request
|
||||
| through the kernel, and send the associated response back to
|
||||
| the client's browser allowing them to enjoy the creative
|
||||
| and wonderful application we have prepared for them.
|
||||
|
|
||||
*/
|
||||
|
||||
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
|
||||
|
||||
$response = $kernel->handle(
|
||||
$request = Illuminate\Http\Request::capture()
|
||||
);
|
||||
|
||||
$response->send();
|
||||
|
||||
$kernel->terminate($request, $response);
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue