T350: Resolved merge conflicts.

This commit is contained in:
Peter Deltchev 2015-09-12 16:40:45 -07:00
commit 86a3248a7c
548 changed files with 12839 additions and 9390 deletions

View file

@ -1,4 +1,4 @@
{
"phabricator.uri" : "https://phabricator.poniverse.net/",
"history.immutable" : true
}
}

7
.gitattributes vendored
View file

@ -1,4 +1,7 @@
* text=auto
app/config/production/* filter=git-crypt diff=git-crypt
app/config/stage/* filter=git-crypt diff=git-crypt
resources/environments/.env.stage filter=git-crypt diff=git-crypt
resources/environments/.env.production filter=git-crypt diff=git-crypt
*.css linguist-vendored
*.less linguist-vendored

52
.gitignore vendored
View file

@ -1,48 +1,8 @@
.vagrant
# Numerous always-ignore extensions
*.diff
*.err
*.orig
*.log
*.rej
*.swo
*.swp
*.vi
*~
*.sass-cache
# OS or Editor folders
.DS_Store
Thumbs.db
.cache
.project
.settings
.tmproj
*.esproj
nbproject
*.iml
# Dreamweaver added files
_notes
dwsync.xml
# Komodo
*.komodoproject
.komodotools
# Folders to ignore
.hg
.svn
.CVS
intermediate
publish
.idea
node_modules
/public/build/
/bootstrap/compiled.php
/vendor
composer.phar
atlassian-ide-plugin.xml
/node_modules
/public/build
/logs
Homestead.yaml
.env
.vagrant
_ide_helper.php

View file

@ -1,3 +0,0 @@
# Contribution Guidelines
Please submit all issues and pull requests to the [laravel/framework](http://github.com/laravel/framework) repository!

319
app/Album.php Normal file
View file

@ -0,0 +1,319 @@
<?php
namespace App;
use Exception;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\URL;
use App\Traits\SlugTrait;
class Album extends Model
{
use SoftDeletes, SlugTrait;
protected $dates = ['deleted_at'];
public static function summary()
{
return self::select('id', 'title', 'user_id', 'slug', 'created_at', 'cover_id', 'comment_count',
'download_count', 'view_count', 'favourite_count');
}
public function scopeUserDetails($query)
{
if (Auth::check()) {
$query->with([
'users' => function ($query) {
$query->whereUserId(Auth::user()->id);
}
]);
}
return !$query;
}
protected $table = 'albums';
public function user()
{
return $this->belongsTo('App\User');
}
public function users()
{
return $this->hasMany('App\ResourceUser');
}
public function favourites()
{
return $this->hasMany('App\Favourite');
}
public function cover()
{
return $this->belongsTo('App\Image');
}
public function tracks()
{
return $this->hasMany('App\Track')->orderBy('track_number', 'asc');
}
public function comments()
{
return $this->hasMany('App\Comment')->orderBy('created_at', 'desc');
}
public static function mapPublicAlbumShow($album)
{
$tracks = [];
foreach ($album->tracks as $track) {
$tracks[] = Track::mapPublicTrackSummary($track);
}
$formats = [];
foreach (Track::$Formats as $name => $format) {
$formats[] = [
'name' => $name,
'extension' => $format['extension'],
'url' => $album->getDownloadUrl($name),
'size' => Helpers::formatBytes($album->getFilesize($name))
];
}
$comments = [];
foreach ($album->comments as $comment) {
$comments[] = Comment::mapPublic($comment);
}
$is_downloadable = 0;
foreach ($album->tracks as $track) {
if ($track->is_downloadable == 1) {
$is_downloadable = 1;
break;
}
}
$data = self::mapPublicAlbumSummary($album);
$data['tracks'] = $tracks;
$data['comments'] = $comments;
$data['formats'] = $formats;
$data['description'] = $album->description;
$data['is_downloadable'] = $is_downloadable;
$data['share'] = [
'url' => URL::to('/a' . $album->id),
'tumblrUrl' => 'http://www.tumblr.com/share/link?url=' . urlencode($album->url) . '&name=' . urlencode($album->title) . '&description=' . urlencode($album->description),
'twitterUrl' => 'https://platform.twitter.com/widgets/tweet_button.html?text=' . $album->title . ' by ' . $album->user->display_name . ' on Pony.fm'
];
return $data;
}
public static function mapPublicAlbumSummary($album)
{
$userData = [
'stats' => [
'views' => 0,
'downloads' => 0
],
'is_favourited' => false
];
if (Auth::check() && $album->users->count()) {
$userRow = $album->users[0];
$userData = [
'stats' => [
'views' => (int) $userRow->view_count,
'downloads' => (int) $userRow->download_count,
],
'is_favourited' => (bool) $userRow->is_favourited
];
}
return [
'id' => (int) $album->id,
'track_count' => (int) $album->track_count,
'title' => $album->title,
'slug' => $album->slug,
'created_at' => $album->created_at,
'stats' => [
'views' => (int) $album->view_count,
'downloads' => (int) $album->download_count,
'comments' => (int) $album->comment_count,
'favourites' => (int) $album->favourite_count
],
'covers' => [
'small' => $album->getCoverUrl(Image::SMALL),
'normal' => $album->getCoverUrl(Image::NORMAL)
],
'url' => $album->url,
'user' => [
'id' => (int) $album->user->id,
'name' => $album->user->display_name,
'url' => $album->user->url,
],
'user_data' => $userData,
'permissions' => [
'delete' => Auth::check() && Auth::user()->id == $album->user_id,
'edit' => Auth::check() && Auth::user()->id == $album->user_id
]
];
}
public function hasCover()
{
return $this->cover_id != null;
}
public function getUrlAttribute()
{
return URL::to('albums/' . $this->id . '-' . $this->slug);
}
public function getDownloadUrl($format)
{
return URL::to('a' . $this->id . '/dl.' . Track::$Formats[$format]['extension']);
}
public function getFilesize($format)
{
$tracks = $this->tracks;
if (!count($tracks)) {
return 0;
}
return Cache::remember($this->getCacheKey('filesize-' . $format), 1440, function () use ($tracks, $format) {
$size = 0;
foreach ($tracks as $track) {
// Ensure that only downloadable tracks are added onto the file size
if ($track->is_downloadable == 1) {
$size += $track->getFilesize($format);
}
}
return $size;
});
}
public function getCoverUrl($type = Image::NORMAL)
{
if (!$this->hasCover()) {
return $this->user->getAvatarUrl($type);
}
return $this->cover->getUrl($type);
}
public function getDirectory()
{
$dir = (string)(floor($this->id / 100) * 100);
return \Config::get('ponyfm.files_directory') . '/tracks/' . $dir;
}
public function getDates()
{
return ['created_at', 'deleted_at', 'published_at'];
}
public function getFilenameFor($format)
{
if (!isset(Track::$Formats[$format])) {
throw new Exception("$format is not a valid format!");
}
$format = Track::$Formats[$format];
return "{$this->id}.{$format['extension']}.zip";
}
public function updateTrackNumbers()
{
$tracks = Track::whereAlbumId($this->id)->get();
$index = 1;
foreach ($tracks as $track) {
$track->track_number = $index;
$index++;
$track->updateTags();
$track->save();
}
}
public function syncTrackIds($trackIds)
{
$trackIdsInAlbum = [];
foreach ($this->tracks as $track) {
$trackIdsInAlbum[] = $track->id;
}
$trackIdsCount = count($trackIds);
$trackIdsInAlbumCount = count($trackIdsInAlbum);
$isSame = true;
if ($trackIdsInAlbumCount != $trackIdsCount) {
$isSame = false;
} else {
for ($i = 0; $i < $trackIdsInAlbumCount; $i++) {
if ($i >= $trackIdsCount || $trackIdsInAlbum[$i] != $trackIds[$i]) {
$isSame = false;
break;
}
}
}
if ($isSame) {
return;
}
$index = 1;
$tracksToRemove = [];
$albumsToFix = [];
foreach ($this->tracks as $track) {
$tracksToRemove[$track->id] = $track;
}
foreach ($trackIds as $trackId) {
if (!strlen(trim($trackId))) {
continue;
}
$track = Track::find($trackId);
if ($track->album_id != null && $track->album_id != $this->id) {
$albumsToFix[] = $track->album;
}
$track->album_id = $this->id;
$track->track_number = $index;
$track->updateTags();
$track->save();
unset($tracksToRemove[$track->id]);
$index++;
}
foreach ($tracksToRemove as $track) {
$track->album_id = null;
$track->track_number = null;
$track->updateTags();
$track->save();
}
foreach ($albumsToFix as $album) {
$album->updateTrackNumbers();
}
foreach (Track::$Formats as $name => $format) {
Cache::forget($this->getCacheKey('filesize' . $name));
}
}
private function getCacheKey($key)
{
return 'album-' . $this->id . '-' . $key;
}
}

53
app/AlbumDownloader.php Normal file
View file

@ -0,0 +1,53 @@
<?php
class AlbumDownloader
{
private $_album;
private $_format;
function __construct($album, $format)
{
$this->_album = $album;
$this->_format = $format;
}
function download()
{
$zip = new ZipStream($this->_album->user->display_name . ' - ' . $this->_album->title . '.zip');
$zip->setComment(
'Album: ' . $this->_album->title . "\r\n" .
'Artist: ' . $this->_album->user->display_name . "\r\n" .
'URL: ' . $this->_album->url . "\r\n" . "\r\n" .
'Downloaded on ' . date('l, F jS, Y, \a\t h:i:s A') . '.'
);
$directory = $this->_album->user->display_name . '/' . $this->_album->title . '/';
$notes =
'Album: ' . $this->_album->title . "\r\n" .
'Artist: ' . $this->_album->user->display_name . "\r\n" .
'URL: ' . $this->_album->url . "\r\n" .
"\r\n" .
$this->_album->description . "\r\n" .
"\r\n" .
"\r\n" .
'Tracks' . "\r\n" .
"\r\n";
foreach ($this->_album->tracks as $track) {
if (!$track->is_downloadable) {
continue;
}
$zip->addLargeFile($track->getFileFor($this->_format),
$directory . $track->getDownloadFilenameFor($this->_format));
$notes .=
$track->track_number . '. ' . $track->title . "\r\n" .
$track->description . "\r\n" .
"\r\n";
}
$zip->addFile($notes, $directory . 'Album Notes.txt');
$zip->finalize();
}
}

View file

@ -0,0 +1,46 @@
<?php
namespace App\Commands;
use App\Playlist;
use App\Track;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
class AddTrackToPlaylistCommand extends CommandBase
{
private $_track;
private $_playlist;
function __construct($playlistId, $trackId)
{
$this->_playlist = Playlist::find($playlistId);
$this->_track = Track::find($trackId);
}
/**
* @return bool
*/
public function authorize()
{
$user = Auth::user();
return $user != null && $this->_playlist && $this->_track && $this->_playlist->user_id == $user->id;
}
/**
* @throws \Exception
* @return CommandResponse
*/
public function execute()
{
$songIndex = $this->_playlist->tracks()->count() + 1;
$this->_playlist->tracks()->attach($this->_track, ['position' => $songIndex]);
Playlist::whereId($this->_playlist->id)->update([
'track_count' => DB::raw('(SELECT COUNT(id) FROM playlist_track WHERE playlist_id = ' . $this->_playlist->id . ')')
]);
return CommandResponse::succeed(['message' => 'Track added!']);
}
}

View file

@ -0,0 +1,33 @@
<?php
namespace App\Commands;
abstract class CommandBase
{
private $_listeners = array();
public function listen($listener)
{
$this->_listeners[] = $listener;
}
protected function notify($message, $progress)
{
foreach ($this->_listeners as $listener) {
$listener($message, $progress);
}
}
/**
* @return bool
*/
public function authorize()
{
return true;
}
/**
* @return CommandResponse
*/
public abstract function execute();
}

View file

@ -0,0 +1,58 @@
<?php
namespace App\Commands;
use Illuminate\Validation\Validator;
class CommandResponse
{
public static function fail($validator)
{
$response = new CommandResponse();
$response->_didFail = true;
$response->_validator = $validator;
return $response;
}
public static function succeed($response = null)
{
$cmdResponse = new CommandResponse();
$cmdResponse->_didFail = false;
$cmdResponse->_response = $response;
return $cmdResponse;
}
private $_validator;
private $_response;
private $_didFail;
private function __construct()
{
}
/**
* @return bool
*/
public function didFail()
{
return $this->_didFail;
}
/**
* @return mixed
*/
public function getResponse()
{
return $this->_response;
}
/**
* @return Validator
*/
public function getValidator()
{
return $this->_validator;
}
}

View file

@ -0,0 +1,72 @@
<?php
namespace App\Commands;
use App\Album;
use App\Image;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Validator;
class CreateAlbumCommand extends CommandBase
{
private $_input;
function __construct($input)
{
$this->_input = $input;
}
/**
* @return bool
*/
public function authorize()
{
$user = \Auth::user();
return $user != null;
}
/**
* @throws \Exception
* @return CommandResponse
*/
public function execute()
{
$rules = [
'title' => 'required|min:3|max:50',
'cover' => 'image|mimes:png|min_width:350|min_height:350',
'cover_id' => 'exists:images,id',
'track_ids' => 'exists:tracks,id'
];
$validator = Validator::make($this->_input, $rules);
if ($validator->fails()) {
return CommandResponse::fail($validator);
}
$album = new Album();
$album->user_id = Auth::user()->id;
$album->title = $this->_input['title'];
$album->description = $this->_input['description'];
if (isset($this->_input['cover_id'])) {
$album->cover_id = $this->_input['cover_id'];
} else {
if (isset($this->_input['cover'])) {
$cover = $this->_input['cover'];
$album->cover_id = Image::upload($cover, Auth::user())->id;
} else {
if (isset($this->_input['remove_cover']) && $this->_input['remove_cover'] == 'true') {
$album->cover_id = null;
}
}
}
$trackIds = explode(',', $this->_input['track_ids']);
$album->save();
$album->syncTrackIds($trackIds);
return CommandResponse::succeed(['id' => $album->id]);
}
}

View file

@ -0,0 +1,79 @@
<?php
namespace App\Commands;
use App\Comment;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Validator;
class CreateCommentCommand extends CommandBase
{
private $_input;
private $_id;
private $_type;
function __construct($type, $id, $input)
{
$this->_input = $input;
$this->_id = $id;
$this->_type = $type;
}
/**
* @return bool
*/
public function authorize()
{
$user = \Auth::user();
return $user != null;
}
/**
* @throws \Exception
* @return CommandResponse
*/
public function execute()
{
$rules = [
'content' => 'required',
'track_id' => 'exists:tracks,id',
'albums_id' => 'exists:albums,id',
'playlist_id' => 'exists:playlists,id',
'profile_id' => 'exists:users,id',
];
$validator = Validator::make($this->_input, $rules);
if ($validator->fails()) {
return CommandResponse::fail($validator);
}
$comment = new Comment();
$comment->user_id = Auth::user()->id;
$comment->content = $this->_input['content'];
if ($this->_type == 'track') {
$column = 'track_id';
} else {
if ($this->_type == 'user') {
$column = 'profile_id';
} else {
if ($this->_type == 'album') {
$column = 'album_id';
} else {
if ($this->_type == 'playlist') {
$column = 'playlist_id';
} else {
App::abort(500);
}
}
}
}
$comment->$column = $this->_id;
$comment->save();
return CommandResponse::succeed(Comment::mapPublic($comment));
}
}

View file

@ -0,0 +1,69 @@
<?php
namespace App\Commands;
use App\Playlist;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Validator;
class CreatePlaylistCommand extends CommandBase
{
private $_input;
function __construct($input)
{
$this->_input = $input;
}
/**
* @return bool
*/
public function authorize()
{
$user = \Auth::user();
return $user != null;
}
/**
* @throws \Exception
* @return CommandResponse
*/
public function execute()
{
$rules = [
'title' => 'required|min:3|max:50',
'is_public' => 'required',
'is_pinned' => 'required'
];
$validator = Validator::make($this->_input, $rules);
if ($validator->fails()) {
return CommandResponse::fail($validator);
}
$playlist = new Playlist();
$playlist->user_id = Auth::user()->id;
$playlist->title = $this->_input['title'];
$playlist->description = $this->_input['description'];
$playlist->is_public = $this->_input['is_public'] == 'true';
$playlist->save();
if ($this->_input['is_pinned'] == 'true') {
$playlist->pin(Auth::user()->id);
}
return CommandResponse::succeed([
'id' => $playlist->id,
'title' => $playlist->title,
'slug' => $playlist->slug,
'created_at' => $playlist->created_at,
'description' => $playlist->description,
'url' => $playlist->url,
'is_pinned' => $this->_input['is_pinned'] == 'true',
'is_public' => $this->_input['is_public'] == 'true'
]);
}
}

View file

@ -0,0 +1,46 @@
<?php
namespace App\Commands;
use App\Album;
use Illuminate\Support\Facades\Auth;
class DeleteAlbumCommand extends CommandBase
{
private $_albumId;
private $_album;
function __construct($albumId)
{
$this->_albumId = $albumId;
$this->_album = ALbum::find($albumId);
}
/**
* @return bool
*/
public function authorize()
{
$user = Auth::user();
return $this->_album && $user != null && $this->_album->user_id == $user->id;
}
/**
* @throws \Exception
* @return CommandResponse
*/
public function execute()
{
foreach ($this->_album->tracks as $track) {
$track->album_id = null;
$track->track_number = null;
$track->updateTags();
$track->save();
}
$this->_album->delete();
return CommandResponse::succeed();
}
}

View file

@ -0,0 +1,43 @@
<?php
namespace App\Commands;
use App\Playlist;
use Illuminate\Support\Facades\Auth;
class DeletePlaylistCommand extends CommandBase
{
private $_playlistId;
private $_playlist;
function __construct($playlistId)
{
$this->_playlistId = $playlistId;
$this->_playlist = Playlist::find($playlistId);
}
/**
* @return bool
*/
public function authorize()
{
$user = Auth::user();
return $this->_playlist && $user != null && $this->_playlist->user_id == $user->id;
}
/**
* @throws \Exception
* @return CommandResponse
*/
public function execute()
{
foreach ($this->_playlist->pins as $pin) {
$pin->delete();
}
$this->_playlist->delete();
return CommandResponse::succeed();
}
}

View file

@ -0,0 +1,46 @@
<?php
namespace App\Commands;
use App\Track;
class DeleteTrackCommand extends CommandBase
{
private $_trackId;
private $_track;
function __construct($trackId)
{
$this->_trackId = $trackId;
$this->_track = Track::find($trackId);
}
/**
* @return bool
*/
public function authorize()
{
$user = \Auth::user();
return $this->_track && $user != null && $this->_track->user_id == $user->id;
}
/**
* @throws \Exception
* @return CommandResponse
*/
public function execute()
{
if ($this->_track->album_id != null) {
$album = $this->_track->album;
$this->_track->album_id = null;
$this->_track->track_number = null;
$this->_track->delete();
$album->updateTrackNumbers();
} else {
$this->_track->delete();
}
return CommandResponse::succeed();
}
}

View file

@ -0,0 +1,79 @@
<?php
namespace App\Commands;
use App\Album;
use App\Image;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Validator;
class EditAlbumCommand extends CommandBase
{
private $_input;
private $_albumId;
private $_album;
function __construct($trackId, $input)
{
$this->_input = $input;
$this->_albumId = $trackId;
$this->_album = Album::find($trackId);
}
/**
* @return bool
*/
public function authorize()
{
$user = Auth::user();
return $this->_album && $user != null && $this->_album->user_id == $user->id;
}
/**
* @throws \Exception
* @return CommandResponse
*/
public function execute()
{
$rules = [
'title' => 'required|min:3|max:50',
'cover' => 'image|mimes:png|min_width:350|min_height:350',
'cover_id' => 'exists:images,id',
'track_ids' => 'exists:tracks,id'
];
$validator = Validator::make($this->_input, $rules);
if ($validator->fails()) {
return CommandResponse::fail($validator);
}
$this->_album->title = $this->_input['title'];
$this->_album->description = $this->_input['description'];
if (isset($this->_input['cover_id'])) {
$this->_album->cover_id = $this->_input['cover_id'];
} else {
if (isset($this->_input['cover'])) {
$cover = $this->_input['cover'];
$this->_album->cover_id = Image::upload($cover, Auth::user())->id;
} else {
if (isset($this->_input['remove_cover']) && $this->_input['remove_cover'] == 'true') {
$this->_album->cover_id = null;
}
}
}
$trackIds = explode(',', $this->_input['track_ids']);
$this->_album->syncTrackIds($trackIds);
$this->_album->save();
Album::whereId($this->_album->id)->update([
'track_count' => DB::raw('(SELECT COUNT(id) FROM tracks WHERE album_id = ' . $this->_album->id . ')')
]);
return CommandResponse::succeed(['real_cover_url' => $this->_album->getCoverUrl(Image::NORMAL)]);
}
}

View file

@ -0,0 +1,77 @@
<?php
namespace App\Commands;
use App\PinnedPlaylist;
use App\Playlist;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Validator;
class EditPlaylistCommand extends CommandBase
{
private $_input;
private $_playlistId;
private $_playlist;
function __construct($playlistId, $input)
{
$this->_input = $input;
$this->_playlistId = $playlistId;
$this->_playlist = Playlist::find($playlistId);
}
/**
* @return bool
*/
public function authorize()
{
$user = Auth::user();
return $this->_playlist && $user != null && $this->_playlist->user_id == $user->id;
}
/**
* @throws \Exception
* @return CommandResponse
*/
public function execute()
{
$rules = [
'title' => 'required|min:3|max:50',
'is_public' => 'required',
'is_pinned' => 'required'
];
$validator = Validator::make($this->_input, $rules);
if ($validator->fails()) {
return CommandResponse::fail($validator);
}
$this->_playlist->title = $this->_input['title'];
$this->_playlist->description = $this->_input['description'];
$this->_playlist->is_public = $this->_input['is_public'] == 'true';
$this->_playlist->save();
$pin = PinnedPlaylist::whereUserId(Auth::user()->id)->wherePlaylistId($this->_playlistId)->first();
if ($pin && $this->_input['is_pinned'] != 'true') {
$pin->delete();
} else {
if (!$pin && $this->_input['is_pinned'] == 'true') {
$this->_playlist->pin(Auth::user()->id);
}
}
return CommandResponse::succeed([
'id' => $this->_playlist->id,
'title' => $this->_playlist->title,
'slug' => $this->_playlist->slug,
'created_at' => $this->_playlist->created_at,
'description' => $this->_playlist->description,
'url' => $this->_playlist->url,
'is_pinned' => $this->_input['is_pinned'] == 'true',
'is_public' => $this->_input['is_public'] == 'true'
]);
}
}

View file

@ -0,0 +1,161 @@
<?php
namespace App\Commands;
use App\Album;
use App\Image;
use App\Track;
use App\User;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
class EditTrackCommand extends CommandBase
{
private $_trackId;
private $_track;
private $_input;
function __construct($trackId, $input)
{
$this->_trackId = $trackId;
$this->_track = Track::find($trackId);
$this->_input = $input;
}
/**
* @return bool
*/
public function authorize()
{
$user = \Auth::user();
return $this->_track && $user != null && $this->_track->user_id == $user->id;
}
/**
* @throws \Exception
* @return CommandResponse
*/
public function execute()
{
$isVocal = (isset($this->_input['is_vocal']) && $this->_input['is_vocal'] == 'true') ? true : false;
$rules = [
'title' => 'required|min:3|max:80',
'released_at' => 'before:' . (date('Y-m-d',
time() + (86400 * 2))) . (isset($this->_input['released_at']) && $this->_input['released_at'] != "" ? '|date' : ''),
'license_id' => 'required|exists:licenses,id',
'genre_id' => 'required|exists:genres,id',
'cover' => 'image|mimes:png|min_width:350|min_height:350',
'track_type_id' => 'required|exists:track_types,id',
'songs' => 'required_when:track_type,2|exists:songs,id',
'cover_id' => 'exists:images,id',
'album_id' => 'exists:albums,id'
];
if ($isVocal) {
$rules['lyrics'] = 'required';
}
if (isset($this->_input['track_type_id']) && $this->_input['track_type_id'] == 2) {
$rules['show_song_ids'] = 'required|exists:show_songs,id';
}
$validator = \Validator::make($this->_input, $rules);
if ($validator->fails()) {
return CommandResponse::fail($validator);
}
$track = $this->_track;
$track->title = $this->_input['title'];
$track->released_at = isset($this->_input['released_at']) && $this->_input['released_at'] != "" ? strtotime($this->_input['released_at']) : null;
$track->description = isset($this->_input['description']) ? $this->_input['description'] : '';
$track->lyrics = isset($this->_input['lyrics']) ? $this->_input['lyrics'] : '';
$track->license_id = $this->_input['license_id'];
$track->genre_id = $this->_input['genre_id'];
$track->track_type_id = $this->_input['track_type_id'];
$track->is_explicit = $this->_input['is_explicit'] == 'true';
$track->is_downloadable = $this->_input['is_downloadable'] == 'true';
$track->is_listed = $this->_input['is_listed'] == 'true';
$track->is_vocal = $isVocal;
if (isset($this->_input['album_id']) && strlen(trim($this->_input['album_id']))) {
if ($track->album_id != null && $track->album_id != $this->_input['album_id']) {
$this->removeTrackFromAlbum($track);
}
if ($track->album_id != $this->_input['album_id']) {
$album = Album::find($this->_input['album_id']);
$track->track_number = $album->tracks()->count() + 1;
$track->album_id = $this->_input['album_id'];
Album::whereId($album->id)->update([
'track_count' => DB::raw('(SELECT COUNT(id) FROM tracks WHERE album_id = ' . $album->id . ')')
]);
}
} else {
if ($track->album_id != null) {
$this->removeTrackFromAlbum($track);
}
$track->track_number = null;
$track->album_id = null;
}
if ($track->track_type_id == 2) {
$track->showSongs()->sync(explode(',', $this->_input['show_song_ids']));
} else {
$track->showSongs()->sync([]);
}
if ($track->published_at == null) {
$track->published_at = new \DateTime();
DB::table('tracks')->whereUserId($track->user_id)->update(['is_latest' => false]);
$track->is_latest = true;
}
if (isset($this->_input['cover_id'])) {
$track->cover_id = $this->_input['cover_id'];
} else {
if (isset($this->_input['cover'])) {
$cover = $this->_input['cover'];
$track->cover_id = Image::upload($cover, Auth::user())->id;
} else {
if ($this->_input['remove_cover'] == 'true') {
$track->cover_id = null;
}
}
}
$track->updateTags();
$track->save();
User::whereId($this->_track->user_id)->update([
'track_count' => DB::raw('(SELECT COUNT(id) FROM tracks WHERE deleted_at IS NULL AND published_at IS NOT NULL AND user_id = ' . $this->_track->user_id . ')')
]);
return CommandResponse::succeed(['real_cover_url' => $track->getCoverUrl(Image::NORMAL)]);
}
private function removeTrackFromAlbum($track)
{
$album = $track->album;
$index = 0;
foreach ($album->tracks as $track) {
if ($track->id == $this->_trackId) {
continue;
}
$track->track_number = ++$index;
$track->updateTags();
$track->save();
}
Album::whereId($album->id)->update([
'track_count' => DB::raw('(SELECT COUNT(id) FROM tracks WHERE album_id = ' . $album->id . ')')
]);
}
}

View file

@ -0,0 +1,88 @@
<?php
namespace App\Commands;
use App\Image;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Validator;
class SaveAccountSettingsCommand extends CommandBase
{
private $_input;
function __construct($input)
{
$this->_input = $input;
}
/**
* @return bool
*/
public function authorize()
{
return Auth::user() != null;
}
/**
* @throws \Exception
* @return CommandResponse
*/
public function execute()
{
$user = Auth::user();
$rules = [
'display_name' => 'required|min:3|max:26',
'bio' => 'textarea_length:250'
];
if ($this->_input['sync_names'] == 'true') {
$this->_input['display_name'] = $user->mlpforums_name;
}
if ($this->_input['uses_gravatar'] == 'true') {
$rules['gravatar'] = 'email';
} else {
$rules['avatar'] = 'image|mimes:png|min_width:350|min_height:350';
$rules['avatar_id'] = 'exists:images,id';
}
$validator = Validator::make($this->_input, $rules);
if ($validator->fails()) {
return CommandResponse::fail($validator);
}
if ($this->_input['uses_gravatar'] != 'true') {
if ($user->avatar_id == null && !isset($this->_input['avatar']) && !isset($this->_input['avatar_id'])) {
$validator->messages()->add('avatar',
'You must upload or select an avatar if you are not using gravatar!');
return CommandResponse::fail($validator);
}
}
$user->bio = $this->_input['bio'];
$user->display_name = $this->_input['display_name'];
$user->sync_names = $this->_input['sync_names'] == 'true';
$user->can_see_explicit_content = $this->_input['can_see_explicit_content'] == 'true';
$user->uses_gravatar = $this->_input['uses_gravatar'] == 'true';
if ($user->uses_gravatar) {
$user->avatar_id = null;
$user->gravatar = $this->_input['gravatar'];
} else {
if (isset($this->_input['avatar_id'])) {
$user->avatar_id = $this->_input['avatar_id'];
} else {
if (isset($this->_input['avatar'])) {
$user->avatar_id = Image::upload($this->_input['avatar'], $user)->id;
}
}
}
$user->save();
return CommandResponse::succeed();
}
}

View file

@ -0,0 +1,75 @@
<?php
namespace App\Commands;
use App\Favourite;
use App\ResourceUser;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
class ToggleFavouriteCommand extends CommandBase
{
private $_resourceType;
private $_resourceId;
function __construct($resourceType, $resourceId)
{
$this->_resourceId = $resourceId;
$this->_resourceType = $resourceType;
}
/**
* @return bool
*/
public function authorize()
{
$user = Auth::user();
return $user != null;
}
/**
* @throws \Exception
* @return CommandResponse
*/
public function execute()
{
$typeId = $this->_resourceType . '_id';
$existing = Favourite::where($typeId, '=', $this->_resourceId)->whereUserId(Auth::user()->id)->first();
$isFavourited = false;
if ($existing) {
$existing->delete();
} else {
$fav = new Favourite();
$fav->$typeId = $this->_resourceId;
$fav->user_id = Auth::user()->id;
$fav->created_at = time();
$fav->save();
$isFavourited = true;
}
$resourceUser = ResourceUser::get(Auth::user()->id, $this->_resourceType, $this->_resourceId);
$resourceUser->is_favourited = $isFavourited;
$resourceUser->save();
$resourceTable = $this->_resourceType . 's';
// We do this to prevent a race condition. Sure I could simply increment the count columns and re-save back to the db
// but that would require an additional SELECT and the operation would be non-atomic. If two log items are created
// for the same resource at the same time, the cached values will still be correct with this method.
DB::table($resourceTable)->whereId($this->_resourceId)->update([
'favourite_count' =>
DB::raw('(
SELECT
COUNT(id)
FROM
favourites
WHERE ' .
$typeId . ' = ' . $this->_resourceId . ')')
]);
return CommandResponse::succeed(['is_favourited' => $isFavourited]);
}
}

View file

@ -0,0 +1,57 @@
<?php
namespace App\Commands;
use App\Follower;
use App\ResourceUser;
use Illuminate\Support\Facades\Auth;
class ToggleFollowingCommand extends CommandBase
{
private $_resourceType;
private $_resourceId;
function __construct($resourceType, $resourceId)
{
$this->_resourceId = $resourceId;
$this->_resourceType = $resourceType;
}
/**
* @return bool
*/
public function authorize()
{
$user = Auth::user();
return $user != null;
}
/**
* @throws \Exception
* @return CommandResponse
*/
public function execute()
{
$typeId = $this->_resourceType . '_id';
$existing = Follower::where($typeId, '=', $this->_resourceId)->whereUserId(Auth::user()->id)->first();
$isFollowed = false;
if ($existing) {
$existing->delete();
} else {
$follow = new Follower();
$follow->$typeId = $this->_resourceId;
$follow->user_id = Auth::user()->id;
$follow->created_at = time();
$follow->save();
$isFollowed = true;
}
$resourceUser = ResourceUser::get(Auth::user()->id, $this->_resourceType, $this->_resourceId);
$resourceUser->is_followed = $isFollowed;
$resourceUser->save();
return CommandResponse::succeed(['is_followed' => $isFollowed]);
}
}

View file

@ -0,0 +1,154 @@
<?php
namespace App\Commands;
use App\Track;
use App\TrackFile;
use AudioCache;
use File;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
class UploadTrackCommand extends CommandBase
{
private $_allowLossy;
private $_allowShortTrack;
private $_losslessFormats = [
'flac',
'pcm_s16le ([1][0][0][0] / 0x0001)',
'pcm_s16be',
'adpcm_ms ([2][0][0][0] / 0x0002)',
'pcm_s24le ([1][0][0][0] / 0x0001)',
'pcm_s24be',
'pcm_f32le ([3][0][0][0] / 0x0003)',
'pcm_f32be (fl32 / 0x32336C66)'
];
public function __construct($allowLossy = false, $allowShortTrack = false)
{
$this->_allowLossy = $allowLossy;
$this->_allowShortTrack = $allowShortTrack;
}
/**
* @return bool
*/
public function authorize()
{
return \Auth::user() != null;
}
/**
* @throws \Exception
* @return CommandResponse
*/
public function execute()
{
$user = \Auth::user();
$trackFile = \Input::file('track');
$audio = \AudioCache::get($trackFile->getPathname());
$validator = \Validator::make(['track' => $trackFile], [
'track' =>
'required|'
. ($this->_allowLossy ? '' : 'audio_format:'. implode(',', $this->_losslessFormats).'|')
. ($this->_allowShortTrack ? '' : 'min_duration:30|')
. 'audio_channels:1,2|'
. 'sample_rate:44100,48000,88200,96000,176400,192000'
]);
if ($validator->fails()) {
return CommandResponse::fail($validator);
}
$track = new Track();
try {
$track->user_id = $user->id;
$track->title = pathinfo($trackFile->getClientOriginalName(), PATHINFO_FILENAME);
$track->duration = $audio->getDuration();
$track->is_listed = true;
$track->save();
$destination = $track->getDirectory();
$track->ensureDirectoryExists();
$source = $trackFile->getPathname();
$index = 0;
$processes = [];
// Lossy uploads need to be identified and set as the master file
// without being re-encoded.
$audioObject = AudioCache::get($source);
$isLossyUpload = !in_array($audioObject->getAudioCodec(), $this->_losslessFormats);
if ($isLossyUpload) {
if ($audioObject->getAudioCodec() === 'mp3') {
$masterFormat = 'MP3';
} else if (Str::startsWith($audioObject->getAudioCodec(), 'aac')) {
$masterFormat = 'AAC';
} else if ($audioObject->getAudioCodec() === 'vorbis') {
$masterFormat = 'OGG Vorbis';
} else {
$validator->messages()->add('track', 'The track does not contain audio in a known lossy format.');
return CommandResponse::fail($validator);
}
$trackFile = new TrackFile();
$trackFile->is_master = true;
$trackFile->format = $masterFormat;
$trackFile->track_id = $track->id;
$trackFile->save();
// Lossy masters are copied into the datastore - no re-encoding involved.
File::copy($source, $trackFile->getFile());
}
foreach (Track::$Formats as $name => $format) {
// Don't bother with lossless transcodes of lossy uploads, and
// don't re-encode the lossy master.
if ($isLossyUpload && ($format['is_lossless'] || $name === $masterFormat)) {
continue;
}
$trackFile = new TrackFile();
$trackFile->is_master = $name === 'FLAC' ? true : false;
$trackFile->format = $name;
$track->trackFiles()->save($trackFile);
$target = $destination . '/' . $trackFile->getFilename(); //$track->getFilenameFor($name);
$command = $format['command'];
$command = str_replace('{$source}', '"' . $source . '"', $command);
$command = str_replace('{$target}', '"' . $target . '"', $command);
Log::info('Encoding ' . $track->id . ' into ' . $target);
$this->notify('Encoding ' . $name, $index / count(Track::$Formats) * 100);
$pipes = [];
$proc = proc_open($command, [0 => ['pipe', 'r'], 1 => ['pipe', 'w'], 2 => ['pipe', 'a']], $pipes);
$processes[] = $proc;
}
foreach ($processes as $proc) {
proc_close($proc);
}
$track->updateTags();
} catch (\Exception $e) {
$track->delete();
throw $e;
}
return CommandResponse::succeed([
'id' => $track->id,
'name' => $track->name
]);
}
}

81
app/Comment.php Normal file
View file

@ -0,0 +1,81 @@
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Comment extends Model
{
use SoftDeletes;
protected $table = 'comments';
protected $dates = ['deleted_at'];
public function user()
{
return $this->belongsTo('App\User');
}
public function track()
{
return $this->belongsTo('App\Track');
}
public function album()
{
return $this->belongsTo('App\Album');
}
public function playlist()
{
return $this->belongsTo('App\Playlist');
}
public function profile()
{
return $this->belongsTo('App\User', 'profile_id');
}
public static function mapPublic($comment)
{
return [
'id' => $comment->id,
'created_at' => $comment->created_at,
'content' => $comment->content,
'user' => [
'name' => $comment->user->display_name,
'id' => $comment->user->id,
'url' => $comment->user->url,
'avatars' => [
'normal' => $comment->user->getAvatarUrl(Image::NORMAL),
'thumbnail' => $comment->user->getAvatarUrl(Image::THUMBNAIL),
'small' => $comment->user->getAvatarUrl(Image::SMALL),
]
]
];
}
public function getResourceAttribute()
{
if ($this->track_id !== null) {
return $this->track;
} else {
if ($this->album_id !== null) {
return $this->album;
} else {
if ($this->playlist_id !== null) {
return $this->playlist;
} else {
if ($this->profile_id !== null) {
return $this->profile;
} else {
return null;
}
}
}
}
}
}

View file

@ -0,0 +1,221 @@
<?php
namespace App\Console\Commands;
use App\ShowSong;
use App\Track;
use App\TrackType;
use DB;
use Illuminate\Console\Command;
use Illuminate\Support\Str;
class ClassifyMLPMA extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'mlpma:classify
{--startAt=1 : Track to start importing from. Useful for resuming an interrupted import.}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Adds Pony.fm-specific metadata to imported MLPMA tracks.';
/**
* A counter for the number of processed tracks.
*
* @var int
*/
protected $currentTrack = 0;
/**
* Create a new command instance.
*
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return void
*/
public function handle()
{
// Get the list of tracks that need classification
$tracks = DB::table('mlpma_tracks')
->orderBy('id')
->get();
$this->comment('Importing tracks...');
$totalTracks = sizeof($tracks);
$fileToStartAt = (int)$this->option('startAt') - 1;
$this->comment("Skipping $fileToStartAt files..." . PHP_EOL);
$tracks = array_slice($tracks, $fileToStartAt);
$this->currentTrack = $fileToStartAt;
foreach ($tracks as $track) {
$this->currentTrack++;
$this->comment('[' . $this->currentTrack . '/' . $totalTracks . '] Classifying track [' . $track->filename . ']...');
$parsedTags = json_decode($track->parsed_tags, true);
//==========================================================================================================
// Original, show song remix, fan song remix, show audio remix, or ponified song?
//==========================================================================================================
$sanitizedTrackTitle = $parsedTags['title'];
$sanitizedTrackTitle = str_replace(['-', '+', '~', 'ft.', '*', '(', ')', '.'], ' ', $sanitizedTrackTitle);
$queriedTitle = DB::connection()->getPdo()->quote($sanitizedTrackTitle);
$officialSongs = ShowSong::select(['id', 'title'])
->whereRaw("
MATCH (title)
AGAINST ($queriedTitle IN BOOLEAN MODE)
")
->get();
// If it has "Ingram" in the name, it's definitely an official song remix.
if (Str::contains(Str::lower($track->filename), 'ingram')) {
$this->info('This is an official song remix!');
list($trackType, $linkedSongIds) = $this->classifyTrack($track->filename, $officialSongs, true,
$parsedTags);
// If it has "remix" in the name, it's definitely a remix.
} else {
if (Str::contains(Str::lower($sanitizedTrackTitle), 'remix')) {
$this->info('This is some kind of remix!');
list($trackType, $linkedSongIds) = $this->classifyTrack($track->filename, $officialSongs, false,
$parsedTags);
// No idea what this is. Have the pony at the terminal figure it out!
} else {
list($trackType, $linkedSongIds) = $this->classifyTrack($track->filename, $officialSongs, false,
$parsedTags);
}
}
//==========================================================================================================
// Attach the data and publish the track!
//==========================================================================================================
$track = Track::find($track->track_id);
$track->track_type_id = $trackType;
$track->published_at = $parsedTags['released_at'];
$track->save();
if (sizeof($linkedSongIds) > 0) {
$track->showSongs()->sync($linkedSongIds);
}
echo PHP_EOL;
}
}
/**
* Determines what type of track the given file is. If unable to guess, the user
* is asked to identify it interactively.
*
* @param string $filename
* @param ShowSong[] $officialSongs
* @param bool|false $isRemixOfOfficialTrack
* @return array
*/
protected function classifyTrack($filename, $officialSongs, $isRemixOfOfficialTrack = false, $tags)
{
$trackTypeId = null;
$linkedSongIds = [];
foreach ($officialSongs as $song) {
$this->comment('=> Matched official song: [' . $song->id . '] ' . $song->title);
}
if ($isRemixOfOfficialTrack && sizeof($officialSongs) === 1) {
$linkedSongIds = [$officialSongs[0]->id];
} else {
if ($isRemixOfOfficialTrack && sizeof($officialSongs) > 1) {
$this->question('Multiple official songs matched! Please enter the ID of the correct one.');
} else {
if (sizeof($officialSongs) > 0) {
$this->question('This looks like a remix of an official song!');
$this->question('Press "r" if the match above is right!');
} else {
$this->question('Exactly what kind of track is this?');
}
}
$this->question('If this is a medley, multiple song ID\'s can be separated by commas. ');
$this->question(' ');
$this->question(' ' . $filename . ' ');
$this->question(' ');
$this->question(' Title: ' . $tags['title'] . ' ');
$this->question(' Album: ' . $tags['album'] . ' ');
$this->question(' Artist: ' . $tags['artist'] . ' ');
$this->question(' ');
$this->question(' r = official song remix (accept all "guessed" matches) ');
$this->question(' # = official song remix (enter the ID(s) of the show song(s)) ');
$this->question(' a = show audio remix ');
$this->question(' f = fan track remix ');
$this->question(' p = ponified track ');
$this->question(' o = original track ');
$this->question(' ');
$input = $this->ask('[r/#/a/f/p/o]: ');
switch ($input) {
case 'r':
$trackTypeId = TrackType::OFFICIAL_TRACK_REMIX;
foreach ($officialSongs as $officialSong) {
$linkedSongIds[] = (int)$officialSong->id;
}
break;
case 'a':
$trackTypeId = TrackType::OFFICIAL_AUDIO_REMIX;
break;
case 'f':
$trackTypeId = TrackType::FAN_TRACK_REMIX;
break;
case 'p':
$trackTypeId = TrackType::PONIFIED_TRACK;
break;
case 'o':
$trackTypeId = TrackType::ORIGINAL_TRACK;
break;
default:
$trackTypeId = TrackType::OFFICIAL_TRACK_REMIX;
$linkedSongIds = explode(',', $input);
$linkedSongIds = array_map(function ($item) {
return (int)$item;
}, $linkedSongIds);
}
}
return [$trackTypeId, $linkedSongIds];
}
}

View file

@ -0,0 +1,498 @@
<?php
namespace App\Console\Commands;
use App\Album;
use App\Commands\UploadTrackCommand;
use App\Genre;
use App\Image;
use App\Track;
use App\User;
use Auth;
use Carbon\Carbon;
use Config;
use DB;
use File;
use Illuminate\Console\Command;
use Illuminate\Support\Str;
use Input;
use Symfony\Component\HttpFoundation\File\UploadedFile;
require_once(app_path() . '/Library/getid3/getid3/getid3.php');
class ImportMLPMA extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'mlpma:import
{--startAt=1 : Track to start importing from. Useful for resuming an interrupted import.}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Imports the MLP Music Archive';
/**
* File extensions to ignore when importing the archive.
*
* @var array
*/
protected $ignoredExtensions = ['db', 'jpg', 'png', 'txt', 'rtf', 'wma'];
/**
* Used to stop the import process when a SIGINT is received.
*
* @var bool
*/
protected $isInterrupted = false;
/**
* A counter for the number of processed tracks.
*
* @var int
*/
protected $currentFile;
/**
* Create a new command instance.
*
*/
public function __construct()
{
parent::__construct();
}
public function handleInterrupt($signo)
{
$this->error('Import aborted!');
$this->error('Resume it from here using: --startAt=' . $this->currentFile);
$this->isInterrupted = true;
}
/**
* Execute the console command.
*
* @return void
*/
public function handle()
{
pcntl_signal(SIGINT, [$this, 'handleInterrupt']);
$mlpmaPath = Config::get('ponyfm.files_directory') . 'mlpma';
$tmpPath = Config::get('ponyfm.files_directory') . 'tmp';
if (!File::exists($tmpPath)) {
File::makeDirectory($tmpPath);
}
$UNKNOWN_GENRE = Genre::firstOrCreate([
'name' => 'Unknown',
'slug' => 'unknown'
]);
$this->comment('Enumerating MLP Music Archive source files...');
$files = File::allFiles($mlpmaPath);
$this->info(sizeof($files) . ' files found!');
$this->comment('Enumerating artists...');
$artists = File::directories($mlpmaPath);
$this->info(sizeof($artists) . ' artists found!');
$this->comment('Importing tracks...');
$totalFiles = sizeof($files);
$fileToStartAt = (int)$this->option('startAt') - 1;
$this->comment("Skipping $fileToStartAt files..." . PHP_EOL);
$files = array_slice($files, $fileToStartAt);
$this->currentFile = $fileToStartAt;
foreach ($files as $file) {
$this->currentFile++;
pcntl_signal_dispatch();
if ($this->isInterrupted) {
break;
}
$this->comment('[' . $this->currentFile . '/' . $totalFiles . '] Importing track [' . $file->getFilename() . ']...');
if (in_array($file->getExtension(), $this->ignoredExtensions)) {
$this->comment('This is not an audio file! Skipping...' . PHP_EOL);
continue;
}
// Has this track already been imported?
$importedTrack = DB::table('mlpma_tracks')
->where('filename', '=', $file->getFilename())
->first();
if ($importedTrack) {
$this->comment('This track has already been imported! Skipping...' . PHP_EOL);
continue;
}
//==========================================================================================================
// Extract the original tags.
//==========================================================================================================
$getId3 = new getID3;
// all tags read by getID3, including the cover art
$allTags = $getId3->analyze($file->getPathname());
// tags specific to a file format (ID3 or Atom), pre-normalization but with cover art removed
$rawTags = [];
// normalized tags used by Pony.fm
$parsedTags = [];
if (Str::lower($file->getExtension()) === 'mp3') {
list($parsedTags, $rawTags) = $this->getId3Tags($allTags);
} elseif (Str::lower($file->getExtension()) === 'm4a') {
list($parsedTags, $rawTags) = $this->getAtomTags($allTags);
} elseif (Str::lower($file->getExtension()) === 'ogg') {
list($parsedTags, $rawTags) = $this->getVorbisTags($allTags);
} elseif (Str::lower($file->getExtension()) === 'flac') {
list($parsedTags, $rawTags) = $this->getVorbisTags($allTags);
}
//==========================================================================================================
// Determine the release date.
//==========================================================================================================
$modifiedDate = Carbon::createFromTimeStampUTC(File::lastModified($file->getPathname()));
$taggedYear = $parsedTags['year'];
$this->info('Modification year: ' . $modifiedDate->year);
$this->info('Tagged year: ' . $taggedYear);
if ($taggedYear !== null && $modifiedDate->year === $taggedYear) {
$releasedAt = $modifiedDate;
} elseif ($taggedYear !== null && Str::length((string)$taggedYear) !== 4) {
$this->error('This track\'s tagged year makes no sense! Using the track\'s last modified date...');
$releasedAt = $modifiedDate;
} elseif ($taggedYear !== null && $modifiedDate->year !== $taggedYear) {
$this->error('Release years don\'t match! Using the tagged year...');
$releasedAt = Carbon::create($taggedYear);
} else {
// $taggedYear is null
$this->error('This track isn\'t tagged with its release year! Using the track\'s last modified date...');
$releasedAt = $modifiedDate;
}
// This is later used by the classification/publishing script to determine the publication date.
$parsedTags['released_at'] = $releasedAt->toDateTimeString();
//==========================================================================================================
// Does this track have vocals?
//==========================================================================================================
$isVocal = $parsedTags['lyrics'] !== null;
//==========================================================================================================
// Fill in the title tag if it's missing.
//==========================================================================================================
if (!$parsedTags['title']) {
$parsedTags['title'] = $file->getBasename('.' . $file->getExtension());
}
//==========================================================================================================
// Determine the genre.
//==========================================================================================================
$genreName = $parsedTags['genre'];
$genreSlug = Str::slug($genreName);
$this->info('Genre: ' . $genreName);
if ($genreName && $genreSlug !== '') {
$genre = Genre::where('name', '=', $genreName)->first();
if ($genre) {
$genreId = $genre->id;
} else {
$genre = new Genre();
$genre->name = $genreName;
$genre->slug = $genreSlug;
$genre->save();
$genreId = $genre->id;
$this->comment('Created a new genre!');
}
} else {
$genreId = $UNKNOWN_GENRE->id; // "Unknown" genre ID
}
//==========================================================================================================
// Determine which artist account this file belongs to using the containing directory.
//==========================================================================================================
$this->info('Path to file: ' . $file->getRelativePath());
$path_components = explode(DIRECTORY_SEPARATOR, $file->getRelativePath());
$artist_name = $path_components[0];
$album_name = array_key_exists(1, $path_components) ? $path_components[1] : null;
$this->info('Artist: ' . $artist_name);
$this->info('Album: ' . $album_name);
$artist = User::where('display_name', '=', $artist_name)->first();
if (!$artist) {
$artist = new User;
$artist->display_name = $artist_name;
$artist->email = null;
$artist->is_archived = true;
$artist->slug = Str::slug($artist_name);
$slugExists = User::where('slug', '=', $artist->slug)->first();
if ($slugExists) {
$this->error('Horsefeathers! The slug ' . $artist->slug . ' is already taken!');
$artist->slug = $artist->slug . '-' . Str::random(4);
}
$artist->save();
}
//==========================================================================================================
// Extract the cover art, if any exists.
//==========================================================================================================
$this->comment('Extracting cover art!');
$coverId = null;
if (array_key_exists('comments', $allTags) && array_key_exists('picture', $allTags['comments'])) {
$image = $allTags['comments']['picture'][0];
if ($image['image_mime'] === 'image/png') {
$extension = 'png';
} elseif ($image['image_mime'] === 'image/jpeg') {
$extension = 'jpg';
} elseif ($image['image_mime'] === 'image/gif') {
$extension = 'gif';
} else {
$this->error('Unknown cover art format!');
}
// write temporary image file
$imageFilename = $file->getFilename() . ".cover.$extension";
$imageFilePath = "$tmpPath/" . $imageFilename;
File::put($imageFilePath, $image['data']);
$imageFile = new UploadedFile($imageFilePath, $imageFilename, $image['image_mime']);
$cover = Image::upload($imageFile, $artist);
$coverId = $cover->id;
} else {
$this->comment('No cover art found!');
}
//==========================================================================================================
// Is this part of an album?
//==========================================================================================================
$albumId = null;
$albumName = $parsedTags['album'];
if ($albumName !== null) {
$album = Album::where('user_id', '=', $artist->id)
->where('title', '=', $albumName)
->first();
if (!$album) {
$album = new Album;
$album->title = $albumName;
$album->user_id = $artist->id;
$album->cover_id = $coverId;
$album->save();
}
$albumId = $album->id;
}
//==========================================================================================================
// Save this track.
//==========================================================================================================
// "Upload" the track to Pony.fm
$this->comment('Transcoding the track!');
Auth::loginUsingId($artist->id);
$trackFile = new UploadedFile($file->getPathname(), $file->getFilename(), $allTags['mime_type']);
Input::instance()->files->add(['track' => $trackFile]);
$upload = new UploadTrackCommand(true, true);
$result = $upload->execute();
if ($result->didFail()) {
$this->error(json_encode($result->getValidator()->messages()->getMessages(), JSON_PRETTY_PRINT));
} else {
// Save metadata.
$track = Track::find($result->getResponse()['id']);
$track->title = $parsedTags['title'];
$track->cover_id = $coverId;
$track->album_id = $albumId;
$track->genre_id = $genreId;
$track->track_number = $parsedTags['track_number'];
$track->released_at = $releasedAt;
$track->description = $parsedTags['comments'];
$track->is_downloadable = true;
$track->lyrics = $parsedTags['lyrics'];
$track->is_vocal = $isVocal;
$track->license_id = 2;
$track->save();
// If we made it to here, the track is intact! Log the import.
DB::table('mlpma_tracks')
->insert([
'track_id' => $result->getResponse()['id'],
'path' => $file->getRelativePath(),
'filename' => $file->getFilename(),
'extension' => $file->getExtension(),
'imported_at' => Carbon::now(),
'parsed_tags' => json_encode($parsedTags),
'raw_tags' => json_encode($rawTags),
]);
}
echo PHP_EOL . PHP_EOL;
}
}
/**
* @param array $rawTags
* @return array
*/
protected function getId3Tags($rawTags)
{
if (array_key_exists('tags', $rawTags) && array_key_exists('id3v2', $rawTags['tags'])) {
$tags = $rawTags['tags']['id3v2'];
} elseif (array_key_exists('tags', $rawTags) && array_key_exists('id3v1', $rawTags['tags'])) {
$tags = $rawTags['tags']['id3v1'];
} else {
$tags = [];
}
$comment = null;
if (isset($tags['comment'])) {
// The "comment" tag comes in with a badly encoded string index
// so its array key has to be used implicitly.
$key = array_keys($tags['comment'])[0];
// The comment may have a null byte at the end. trim() removes it.
$comment = trim($tags['comment'][$key]);
// Replace the malformed comment with the "fixed" one.
unset($tags['comment'][$key]);
$tags['comment'][0] = $comment;
}
return [
[
'title' => isset($tags['title']) ? $tags['title'][0] : null,
'artist' => isset($tags['artist']) ? $tags['artist'][0] : null,
'band' => isset($tags['band']) ? $tags['band'][0] : null,
'genre' => isset($tags['genre']) ? $tags['genre'][0] : null,
'track_number' => isset($tags['track_number']) ? $tags['track_number'][0] : null,
'album' => isset($tags['album']) ? $tags['album'][0] : null,
'year' => isset($tags['year']) ? (int)$tags['year'][0] : null,
'comments' => $comment,
'lyrics' => isset($tags['unsynchronised_lyric']) ? $tags['unsynchronised_lyric'][0] : null,
],
$tags
];
}
/**
* @param array $rawTags
* @return array
*/
protected function getAtomTags($rawTags)
{
if (array_key_exists('tags', $rawTags) && array_key_exists('quicktime', $rawTags['tags'])) {
$tags = $rawTags['tags']['quicktime'];
} else {
$tags = [];
}
$trackNumber = null;
if (isset($tags['track_number'])) {
$trackNumberComponents = explode('/', $tags['track_number'][0]);
$trackNumber = $trackNumberComponents[0];
}
return [
[
'title' => isset($tags['title']) ? $tags['title'][0] : null,
'artist' => isset($tags['artist']) ? $tags['artist'][0] : null,
'band' => isset($tags['band']) ? $tags['band'][0] : null,
'album_artist' => isset($tags['album_artist']) ? $tags['album_artist'][0] : null,
'genre' => isset($tags['genre']) ? $tags['genre'][0] : null,
'track_number' => $trackNumber,
'album' => isset($tags['album']) ? $tags['album'][0] : null,
'year' => isset($tags['year']) ? (int)$tags['year'][0] : null,
'comments' => isset($tags['comments']) ? $tags['comments'][0] : null,
'lyrics' => isset($tags['lyrics']) ? $tags['lyrics'][0] : null,
],
$tags
];
}
/**
* @param array $rawTags
* @return array
*/
protected function getVorbisTags($rawTags)
{
if (array_key_exists('tags', $rawTags) && array_key_exists('vorbiscomment', $rawTags['tags'])) {
$tags = $rawTags['tags']['vorbiscomment'];
} else {
$tags = [];
}
$trackNumber = null;
if (isset($tags['track_number'])) {
$trackNumberComponents = explode('/', $tags['track_number'][0]);
$trackNumber = $trackNumberComponents[0];
}
return [
[
'title' => isset($tags['title']) ? $tags['title'][0] : null,
'artist' => isset($tags['artist']) ? $tags['artist'][0] : null,
'band' => isset($tags['band']) ? $tags['band'][0] : null,
'album_artist' => isset($tags['album_artist']) ? $tags['album_artist'][0] : null,
'genre' => isset($tags['genre']) ? $tags['genre'][0] : null,
'track_number' => $trackNumber,
'album' => isset($tags['album']) ? $tags['album'][0] : null,
'year' => isset($tags['year']) ? (int)$tags['year'][0] : null,
'comments' => isset($tags['comments']) ? $tags['comments'][0] : null,
'lyrics' => isset($tags['lyrics']) ? $tags['lyrics'][0] : null,
],
$tags
];
}
}

View file

@ -0,0 +1,33 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Foundation\Inspiring;
class Inspire extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'inspire';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Display an inspiring quote';
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$this->comment(PHP_EOL.Inspiring::quote().PHP_EOL);
}
}

View file

@ -0,0 +1,382 @@
<?php
namespace App\Console\Commands;
use App\Image;
use App\ResourceLogItem;
use DB;
use Exception;
use Illuminate\Console\Command;
use Symfony\Component\HttpFoundation\File\UploadedFile;
class MigrateOldData extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'migrate-old-data';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Migrates data from the old pfm site.';
/**
* Create a new command instance.
*
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
DB::connection()->disableQueryLog();
$oldDb = DB::connection('old');
$this->call('migrate:refresh');
$oldUsers = $oldDb->table('users')->get();
$this->info('Syncing Users');
foreach ($oldUsers as $user) {
$displayName = $user->display_name;
if (!$displayName) {
$displayName = $user->username;
}
if (!$displayName) {
$displayName = $user->mlpforums_name;
}
if (!$displayName) {
continue;
}
DB::table('users')->insert([
'id' => $user->id,
'display_name' => $displayName,
'email' => $user->email,
'created_at' => $user->created_at,
'updated_at' => $user->updated_at,
'slug' => $user->slug,
'bio' => $user->bio,
'sync_names' => $user->sync_names,
'can_see_explicit_content' => $user->can_see_explicit_content,
'mlpforums_name' => $user->mlpforums_name,
'uses_gravatar' => $user->uses_gravatar,
'gravatar' => $user->gravatar,
'avatar_id' => null
]);
$coverId = null;
if (!$user->uses_gravatar) {
try {
$coverFile = $this->getIdDirectory('users', $user->id) . '/' . $user->id . '_.png';
$coverId = Image::upload(new UploadedFile($coverFile,
$user->id . '_.png'), $user->id)->id;
DB::table('users')->where('id', $user->id)->update(['avatar_id' => $coverId]);
} catch (\Exception $e) {
$this->error('Could copy user avatar ' . $user->id . ' because ' . $e->getMessage());
DB::table('users')->where('id', $user->id)->update(['uses_gravatar' => true]);
}
}
}
$this->info('Syncing Genres');
$oldGenres = $oldDb->table('genres')->get();
foreach ($oldGenres as $genre) {
DB::table('genres')->insert([
'id' => $genre->id,
'name' => $genre->title,
'slug' => $genre->slug
]);
}
$this->info('Syncing Albums');
$oldAlbums = $oldDb->table('albums')->get();
foreach ($oldAlbums as $playlist) {
$logViews = $oldDb->table('album_log_views')->whereAlbumId($playlist->id)->get();
$logDownload = $oldDb->table('album_log_downloads')->whereAlbumId($playlist->id)->get();
DB::table('albums')->insert([
'title' => $playlist->title,
'description' => $playlist->description,
'created_at' => $playlist->created_at,
'updated_at' => $playlist->updated_at,
'deleted_at' => $playlist->deleted_at,
'slug' => $playlist->slug,
'id' => $playlist->id,
'user_id' => $playlist->user_id,
'view_count' => 0,
'download_count' => 0
]);
foreach ($logViews as $logItem) {
try {
DB::table('resource_log_items')->insert([
'user_id' => $logItem->user_id,
'log_type' => ResourceLogItem::VIEW,
'album_id' => $logItem->album_id,
'created_at' => $logItem->created_at,
'ip_address' => $logItem->ip_address,
]);
} catch (\Exception $e) {
$this->error('Could insert log item for album ' . $playlist->id . ' because ' . $e->getMessage());
}
}
foreach ($logDownload as $logItem) {
try {
DB::table('resource_log_items')->insert([
'user_id' => $logItem->user_id,
'log_type' => ResourceLogItem::DOWNLOAD,
'album_id' => $logItem->album_id,
'created_at' => $logItem->created_at,
'ip_address' => $logItem->ip_address,
'track_format_id' => $logItem->track_file_format_id - 1
]);
} catch (\Exception $e) {
$this->error('Could insert log item for album ' . $playlist->id . ' because ' . $e->getMessage());
}
}
}
$this->info('Syncing Tracks');
$oldTracks = $oldDb->table('tracks')->get();
foreach ($oldTracks as $track) {
$coverId = null;
if ($track->cover) {
try {
$coverFile = $this->getIdDirectory('tracks',
$track->id) . '/' . $track->id . '_' . $track->cover . '.png';
$coverId = Image::upload(new UploadedFile($coverFile,
$track->id . '_' . $track->cover . '.png'), $track->user_id)->id;
} catch (\Exception $e) {
$this->error('Could copy track cover ' . $track->id . ' because ' . $e->getMessage());
}
}
$trackLogViews = $oldDb->table('track_log_views')->whereTrackId($track->id)->get();
$trackLogPlays = $oldDb->table('track_log_plays')->whereTrackId($track->id)->get();
$trackLogDownload = $oldDb->table('track_log_downloads')->whereTrackId($track->id)->get();
DB::table('tracks')->insert([
'id' => $track->id,
'title' => $track->title,
'slug' => $track->slug,
'description' => $track->description,
'lyrics' => $track->lyrics,
'created_at' => $track->created_at,
'deleted_at' => $track->deleted_at,
'updated_at' => $track->updated_at,
'released_at' => $track->released_at,
'published_at' => $track->published_at,
'genre_id' => $track->genre_id,
'is_explicit' => $track->explicit,
'is_downloadable' => $track->downloadable,
'is_vocal' => $track->is_vocal,
'track_type_id' => $track->track_type_id,
'track_number' => $track->track_number,
'user_id' => $track->user_id,
'album_id' => $track->album_id,
'cover_id' => $coverId,
'license_id' => $track->license_id,
'duration' => $track->duration,
'view_count' => 0,
'play_count' => 0,
'download_count' => 0
]);
foreach ($trackLogViews as $logItem) {
try {
DB::table('resource_log_items')->insert([
'user_id' => $logItem->user_id,
'log_type' => ResourceLogItem::VIEW,
'track_id' => $logItem->track_id,
'created_at' => $logItem->created_at,
'ip_address' => $logItem->ip_address
]);
} catch (\Exception $e) {
$this->error('Could insert log item for track ' . $track->id . ' because ' . $e->getMessage());
}
}
foreach ($trackLogPlays as $logItem) {
try {
DB::table('resource_log_items')->insert([
'user_id' => $logItem->user_id,
'log_type' => ResourceLogItem::PLAY,
'track_id' => $logItem->track_id,
'created_at' => $logItem->created_at,
'ip_address' => $logItem->ip_address
]);
} catch (\Exception $e) {
$this->error('Could insert log item for track ' . $track->id . ' because ' . $e->getMessage());
}
}
foreach ($trackLogDownload as $logItem) {
try {
DB::table('resource_log_items')->insert([
'user_id' => $logItem->user_id,
'log_type' => ResourceLogItem::DOWNLOAD,
'track_id' => $logItem->track_id,
'created_at' => $logItem->created_at,
'ip_address' => $logItem->ip_address,
'track_format_id' => $logItem->track_file_format_id - 1
]);
} catch (\Exception $e) {
$this->error('Could insert log item for track ' . $track->id . ' because ' . $e->getMessage());
}
}
}
$oldShowSongs = $oldDb->table('song_track')->get();
foreach ($oldShowSongs as $song) {
try {
DB::table('show_song_track')->insert([
'id' => $song->id,
'show_song_id' => $song->song_id,
'track_id' => $song->track_id
]);
} catch (\Exception $e) {
$this->error('Could insert show track item for ' . $song->track_id . ' because ' . $e->getMessage());
}
}
$this->info('Syncing Playlists');
$oldPlaylists = $oldDb->table('playlists')->get();
foreach ($oldPlaylists as $playlist) {
$logViews = $oldDb->table('playlist_log_views')->wherePlaylistId($playlist->id)->get();
$logDownload = $oldDb->table('playlist_log_downloads')->wherePlaylistId($playlist->id)->get();
DB::table('playlists')->insert([
'title' => $playlist->title,
'description' => $playlist->description,
'created_at' => $playlist->created_at,
'updated_at' => $playlist->updated_at,
'deleted_at' => $playlist->deleted_at,
'slug' => $playlist->slug,
'id' => $playlist->id,
'user_id' => $playlist->user_id,
'is_public' => true,
'view_count' => 0,
'download_count' => 0,
]);
foreach ($logViews as $logItem) {
try {
DB::table('resource_log_items')->insert([
'user_id' => $logItem->user_id,
'log_type' => ResourceLogItem::VIEW,
'playlist_id' => $logItem->playlist_id,
'created_at' => $logItem->created_at,
'ip_address' => $logItem->ip_address,
]);
} catch (\Exception $e) {
$this->error('Could insert log item for playlist ' . $playlist->id . ' because ' . $e->getMessage());
}
}
foreach ($logDownload as $logItem) {
try {
DB::table('resource_log_items')->insert([
'user_id' => $logItem->user_id,
'log_type' => ResourceLogItem::DOWNLOAD,
'playlist_id' => $logItem->playlist_id,
'created_at' => $logItem->created_at,
'ip_address' => $logItem->ip_address,
'track_format_id' => $logItem->track_file_format_id - 1
]);
} catch (\Exception $e) {
$this->error('Could insert log item for playlist ' . $playlist->id . ' because ' . $e->getMessage());
}
}
}
$this->info('Syncing Playlist Tracks');
$oldPlaylistTracks = $oldDb->table('playlist_track')->get();
foreach ($oldPlaylistTracks as $playlistTrack) {
DB::table('playlist_track')->insert([
'id' => $playlistTrack->id,
'created_at' => $playlistTrack->created_at,
'updated_at' => $playlistTrack->updated_at,
'position' => $playlistTrack->position,
'playlist_id' => $playlistTrack->playlist_id,
'track_id' => $playlistTrack->track_id
]);
}
$this->info('Syncing Comments');
$oldComments = $oldDb->table('comments')->get();
foreach ($oldComments as $comment) {
try {
DB::table('comments')->insert([
'id' => $comment->id,
'user_id' => $comment->user_id,
'created_at' => $comment->created_at,
'deleted_at' => $comment->deleted_at,
'updated_at' => $comment->updated_at,
'content' => $comment->content,
'track_id' => $comment->track_id,
'album_id' => $comment->album_id,
'playlist_id' => $comment->playlist_id,
'profile_id' => $comment->profile_id
]);
} catch (Exception $e) {
$this->error('Could not sync comment ' . $comment->id . ' because ' . $e->getMessage());
}
}
$this->info('Syncing Favourites');
$oldFavs = $oldDb->table('favourites')->get();
foreach ($oldFavs as $fav) {
try {
DB::table('favourites')->insert([
'id' => $fav->id,
'user_id' => $fav->user_id,
'created_at' => $fav->created_at,
'track_id' => $fav->track_id,
'album_id' => $fav->album_id,
'playlist_id' => $fav->playlist_id,
]);
} catch (Exception $e) {
$this->error('Could not sync favourite ' . $fav->id . ' because ' . $e->getMessage());
}
}
$this->info('Syncing Followers');
$oldFollowers = $oldDb->table('user_follower')->get();
foreach ($oldFollowers as $follower) {
try {
DB::table('followers')->insert([
'id' => $follower->id,
'user_id' => $follower->follower_id,
'artist_id' => $follower->user_id,
'created_at' => $follower->created_at,
]);
} catch (Exception $e) {
$this->error('Could not sync follower ' . $follower->id . ' because ' . $e->getMessage());
}
}
}
private function getIdDirectory($type, $id)
{
$dir = (string)(floor($id / 100) * 100);
return \Config::get('ponyfm.files_directory') . '/' . $type . '/' . $dir;
}
}

View file

@ -0,0 +1,53 @@
<?php
namespace App\Console\Commands;
use App\Track;
use Illuminate\Console\Command;
class RebuildTags extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'rebuild:tags
{trackId? : ID of the track to rebuild tags for}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Rewrites tags in track files, ensuring they\'re up to date.';
/**
* Create a new command instance.
*
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
if ($this->argument('trackId')) {
$track = Track::findOrFail($this->argument('trackId'));
$tracks = [$track];
} else {
$tracks = Track::whereNotNull('published_at')->get();
}
foreach($tracks as $track) {
$this->comment('Rewriting tags for track #'.$track->id.'...');
$track->updateTags();
}
}
}

View file

@ -0,0 +1,232 @@
<?php
namespace App\Console\Commands;
use App\ResourceLogItem;
use DB;
use Illuminate\Console\Command;
class RefreshCache extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'refresh-cache';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Refreshes cache tables for views and downloads';
/**
* Create a new command instance.
*
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
DB::connection()->disableQueryLog();
DB::table('tracks')->update(['comment_count' => DB::raw('(SELECT COUNT(id) FROM comments WHERE comments.track_id = tracks.id AND deleted_at IS NULL)')]);
DB::table('albums')->update([
'comment_count' => DB::raw('(SELECT COUNT(id) FROM comments WHERE comments.album_id = albums.id AND deleted_at IS NULL)'),
'track_count' => DB::raw('(SELECT COUNT(id) FROM tracks WHERE album_id = albums.id)')
]);
DB::table('playlists')->update([
'comment_count' => DB::raw('(SELECT COUNT(id) FROM comments WHERE comments.playlist_id = playlists.id AND deleted_at IS NULL)'),
'track_count' => DB::raw('(SELECT COUNT(id) FROM playlist_track WHERE playlist_id = playlists.id)')
]);
DB::table('users')->update([
'comment_count' => DB::raw('(SELECT COUNT(id) FROM comments WHERE comments.profile_id = users.id AND deleted_at IS NULL)'),
'track_count' => DB::raw('(SELECT COUNT(id) FROM tracks WHERE deleted_at IS NULL AND published_at IS NOT NULL AND user_id = users.id)')
]);
$users = DB::table('users')->get();
$cacheItems = [];
$resources = [
'album' => [],
'playlist' => [],
'track' => []
];
foreach ($users as $user) {
$cacheItems[$user->id] = [
'album' => [],
'playlist' => [],
'track' => [],
];
}
$logItems = DB::table('resource_log_items')->get();
foreach ($logItems as $item) {
$type = '';
$id = 0;
if ($item->album_id) {
$type = 'album';
$id = $item->album_id;
} else {
if ($item->playlist_id) {
$type = 'playlist';
$id = $item->playlist_id;
} else {
if ($item->track_id) {
$type = 'track';
$id = $item->track_id;
}
}
}
$resource = $this->getCacheItem($resources, $type, $id);
if ($item->user_id != null) {
$userResource = $this->getUserCacheItem($cacheItems, $item->user_id, $type, $id);
if ($item->log_type == ResourceLogItem::DOWNLOAD) {
$userResource['download_count']++;
} else {
if ($item->log_type == ResourceLogItem::VIEW) {
$userResource['view_count']++;
} else {
if ($item->log_type == ResourceLogItem::PLAY) {
$userResource['play_count']++;
}
}
}
$cacheItems[$item->user_id][$type][$id] = $userResource;
}
if ($item->log_type == ResourceLogItem::DOWNLOAD) {
$resource['download_count']++;
} else {
if ($item->log_type == ResourceLogItem::VIEW) {
$resource['view_count']++;
} else {
if ($item->log_type == ResourceLogItem::PLAY) {
$resource['play_count']++;
}
}
}
$resources[$type][$id] = $resource;
}
$pins = DB::table('pinned_playlists')->get();
foreach ($pins as $pin) {
$userResource = $this->getUserCacheItem($cacheItems, $pin->user_id, 'playlist', $pin->playlist_id);
$userResource['is_pinned'] = true;
$cacheItems[$pin->user_id]['playlist'][$pin->playlist_id] = $userResource;
}
$favs = DB::table('favourites')->get();
foreach ($favs as $fav) {
$type = '';
$id = 0;
if ($fav->album_id) {
$type = 'album';
$id = $fav->album_id;
} else {
if ($fav->playlist_id) {
$type = 'playlist';
$id = $fav->playlist_id;
} else {
if ($fav->track_id) {
$type = 'track';
$id = $fav->track_id;
}
}
}
$userResource = $this->getUserCacheItem($cacheItems, $fav->user_id, $type, $id);
$userResource['is_favourited'] = true;
$cacheItems[$fav->user_id][$type][$id] = $userResource;
$resource = $this->getCacheItem($resources, $type, $id);
$resource['favourite_count']++;
$resources[$type][$id] = $resource;
}
foreach (DB::table('followers')->get() as $follower) {
$userResource = $this->getUserCacheItem($cacheItems, $follower->user_id, 'artist', $follower->artist_id);
$userResource['is_followed'] = true;
$cacheItems[$follower->user_id]['artist'][$follower->artist_id] = $userResource;
}
foreach ($resources as $name => $resourceArray) {
foreach ($resourceArray as $id => $resource) {
DB::table($name . 's')->whereId($id)->update($resource);
}
}
DB::table('resource_users')->delete();
foreach ($cacheItems as $cacheItem) {
foreach ($cacheItem as $resources) {
foreach ($resources as $resource) {
DB::table('resource_users')->insert($resource);
}
}
}
}
private function getCacheItem(&$resources, $type, $id)
{
if (!isset($resources[$type][$id])) {
$item = [
'view_count' => 0,
'download_count' => 0,
'favourite_count' => 0,
];
if ($type == 'track') {
$item['play_count'] = 0;
}
$resources[$type][$id] = $item;
return $item;
}
return $resources[$type][$id];
}
private function getUserCacheItem(&$items, $userId, $type, $id)
{
if (!isset($items[$userId][$type][$id])) {
$item = [
'is_followed' => false,
'is_favourited' => false,
'is_pinned' => false,
'view_count' => 0,
'play_count' => 0,
'download_count' => 0,
'user_id' => $userId
];
$item[$type . '_id'] = $id;
$items[$userId][$type][$id] = $item;
return $item;
}
return $items[$userId][$type][$id];
}
}

32
app/Console/Kernel.php Normal file
View file

@ -0,0 +1,32 @@
<?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\MigrateOldData::class,
\App\Console\Commands\RefreshCache::class,
\App\Console\Commands\ImportMLPMA::class,
\App\Console\Commands\ClassifyMLPMA::class,
\App\Console\Commands\RebuildTags::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
View file

@ -0,0 +1,8 @@
<?php
namespace App\Events;
abstract class Event
{
//
}

View file

@ -0,0 +1,44 @@
<?php
namespace App\Exceptions;
use Exception;
use Symfony\Component\HttpKernel\Exception\HttpException;
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,
];
/**
* 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)
{
return parent::render($request, $e);
}
}

65
app/Favourite.php Normal file
View file

@ -0,0 +1,65 @@
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Favourite extends Model
{
protected $table = 'favourites';
public $timestamps = false;
/*
|--------------------------------------------------------------------------
| Relationships
|--------------------------------------------------------------------------
*/
public function user()
{
return $this->belongsTo('App\User');
}
public function track()
{
return $this->belongsTo('App\Track');
}
public function album()
{
return $this->belongsTo('App\Album');
}
public function playlist()
{
return $this->belongsTo('App\Playlist');
}
/**
* Return the resource associated with this favourite.
*
* @return Resource|NULL
*/
public function getResourceAttribute()
{
if ($this->track_id) {
return $this->track;
} else {
if ($this->album_id) {
return $this->album;
} else {
if ($this->playlist_id) {
return $this->playlist;
} // no resource - this should never happen under real circumstances
else {
return null;
}
}
}
}
public function getTypeAttribute()
{
return get_class($this->resource);
}
}

12
app/Follower.php Normal file
View file

@ -0,0 +1,12 @@
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Follower extends Model
{
protected $table = 'followers';
public $timestamps = false;
}

15
app/Genre.php Normal file
View file

@ -0,0 +1,15 @@
<?php
namespace App;
use App\Traits\SlugTrait;
use Illuminate\Database\Eloquent\Model;
class Genre extends Model
{
protected $table = 'genres';
protected $fillable = ['name', 'slug'];
public $timestamps = false;
use SlugTrait;
}

View file

@ -0,0 +1,20 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Redirect;
use View;
class AccountController extends Controller
{
public function getIndex()
{
return View::make('shared.null');
}
public function getRegister()
{
return Redirect::to(Config::get('poniverse.urls')['register']);
}
}

View file

@ -0,0 +1,70 @@
<?php
namespace App\Http\Controllers;
use AlbumDownloader;
use App;
use App\Album;
use App\ResourceLogItem;
use App\Track;
use Illuminate\Support\Facades\Redirect;
use View;
class AlbumsController extends Controller
{
public function getIndex()
{
return View::make('albums.index');
}
public function getShow($id, $slug)
{
$album = Album::find($id);
if (!$album) {
App::abort(404);
}
if ($album->slug != $slug) {
return Redirect::action('AlbumsController@getAlbum', [$id, $album->slug]);
}
return View::make('albums.show');
}
public function getShortlink($id)
{
$album = Album::find($id);
if (!$album) {
App::abort(404);
}
return Redirect::action('AlbumsController@getTrack', [$id, $album->slug]);
}
public function getDownload($id, $extension)
{
$album = Album::with('tracks', 'user')->find($id);
if (!$album) {
App::abort(404);
}
$format = null;
$formatName = null;
foreach (Track::$Formats as $name => $item) {
if ($item['extension'] == $extension) {
$format = $item;
$formatName = $name;
break;
}
}
if ($format == null) {
App::abort(404);
}
ResourceLogItem::logItem('album', $id, ResourceLogItem::DOWNLOAD, $format['index']);
$downloader = new AlbumDownloader($album, $formatName);
$downloader->download();
}
}

View file

@ -1,6 +1,10 @@
<?php namespace Api\Mobile;
<?php
use Entities\Track;
namespace App\Http\Controllers\Api\Mobile;
use App\Http\Controllers\Controller;
use App\Track;
use Response;
class TracksController extends Controller
{

View file

@ -0,0 +1,88 @@
<?php
namespace App\Http\Controllers\Api\V1;
use App\Image;
use App\Track;
use Cover;
use Illuminate\Support\Facades\Response;
class TracksController extends \ApiControllerBase
{
public function getTrackRadioDetails($hash)
{
$track = Track
::with('user', 'album', 'user.avatar', 'cover', 'comments', 'genre')
->published()
->whereHash($hash)->first();
if (!$track) {
return Response::json(['message' => 'Track not found.'], 403);
}
$comments = [];
foreach ($track->comments as $comment) {
$comments[] = [
'id' => $comment->id,
'created_at' => $comment->created_at,
'content' => $comment->content,
'user' => [
'name' => $comment->user->display_name,
'id' => $comment->user->id,
'url' => $comment->user->url,
'avatars' => [
'normal' => $comment->user->getAvatarUrl(Image::NORMAL),
'thumbnail' => $comment->user->getAvatarUrl(Image::THUMBNAIL),
'small' => $comment->user->getAvatarUrl(Image::SMALL),
]
]
];
}
return Response::json([
'id' => $track->id,
'title' => $track->title,
'description' => $track->description,
'lyrics' => $track->lyrics,
'user' => [
'id' => $track->user->id,
'name' => $track->user->display_name,
'url' => $track->user->url,
'avatars' => [
'thumbnail' => $track->user->getAvatarUrl(Image::THUMBNAIL),
'small' => $track->user->getAvatarUrl(Image::SMALL),
'normal' => $track->user->getAvatarUrl(Image::NORMAL)
]
],
'stats' => [
'views' => $track->view_count,
'plays' => $track->play_count,
'downloads' => $track->download_count,
'comments' => $track->comment_count,
'favourites' => $track->favourite_count
],
'url' => $track->url,
'is_vocal' => !!$track->is_vocal,
'is_explicit' => !!$track->is_explicit,
'is_downloadable' => !!$track->is_downloadable,
'published_at' => $track->published_at,
'duration' => $track->duration,
'genre' => $track->genre != null
?
[
'id' => $track->genre->id,
'name' => $track->genre->name
] : null,
'type' => [
'id' => $track->track_type->id,
'name' => $track->track_type->title
],
'covers' => [
'thumbnail' => $track->getCoverUrl(Image::THUMBNAIL),
'small' => $track->getCoverUrl(Image::SMALL),
'normal' => $track->getCoverUrl(Image::NORMAL)
],
'comments' => $comments
], 200);
}
}

View file

@ -0,0 +1,34 @@
<?php
namespace App\Http\Controllers\Api\Web;
use App\Http\Controllers\ApiControllerBase;
use App\Commands\SaveAccountSettingsCommand;
use Cover;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Input;
use Illuminate\Support\Facades\Response;
class AccountController extends ApiControllerBase
{
public function getSettings()
{
$user = Auth::user();
return Response::json([
'bio' => $user->bio,
'can_see_explicit_content' => $user->can_see_explicit_content == 1,
'display_name' => $user->display_name,
'sync_names' => $user->sync_names == 1,
'mlpforums_name' => $user->mlpforums_name,
'gravatar' => $user->gravatar ? $user->gravatar : $user->email,
'avatar_url' => !$user->uses_gravatar ? $user->getAvatarUrl() : null,
'uses_gravatar' => $user->uses_gravatar == 1
], 200);
}
public function postSave()
{
return $this->execute(new SaveAccountSettingsCommand(Input::all()));
}
}

View file

@ -0,0 +1,147 @@
<?php
namespace App\Http\Controllers\Api\Web;
use App\Album;
use App\Commands\CreateAlbumCommand;
use App\Commands\DeleteAlbumCommand;
use App\Commands\EditAlbumCommand;
use App\Http\Controllers\ApiControllerBase;
use App\Image;
use App\ResourceLogItem;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Input;
use Illuminate\Support\Facades\Response;
class AlbumsController extends ApiControllerBase
{
public function postCreate()
{
return $this->execute(new CreateAlbumCommand(Input::all()));
}
public function postEdit($id)
{
return $this->execute(new EditAlbumCommand($id, Input::all()));
}
public function postDelete($id)
{
return $this->execute(new DeleteAlbumCommand($id));
}
public function getShow($id)
{
$album = Album::with([
'tracks' => function ($query) {
$query->userDetails();
},
'tracks.cover',
'tracks.genre',
'tracks.user',
'user',
'comments',
'comments.user'
])
->userDetails()
->find($id);
if (!$album) {
App::abort(404);
}
if (Input::get('log')) {
ResourceLogItem::logItem('album', $id, ResourceLogItem::VIEW);
$album->view_count++;
}
$returned_album = Album::mapPublicAlbumShow($album);
if ($returned_album['is_downloadable'] == 0) {
unset($returned_album['formats']);
}
return Response::json([
'album' => $returned_album
], 200);
}
public function getIndex()
{
$page = 1;
if (Input::has('page')) {
$page = Input::get('page');
}
$query = Album::summary()
->with('user', 'user.avatar', 'cover')
->userDetails()
->orderBy('created_at', 'desc')
->where('track_count', '>', 0);
$count = $query->count();
$perPage = 40;
$query->skip(($page - 1) * $perPage)->take($perPage);
$albums = [];
foreach ($query->get() as $album) {
$albums[] = Album::mapPublicAlbumSummary($album);
}
return Response::json(["albums" => $albums, "current_page" => $page, "total_pages" => ceil($count / $perPage)],
200);
}
public function getOwned()
{
$query = Album::summary()->where('user_id', \Auth::user()->id)->orderBy('created_at', 'desc')->get();
$albums = [];
foreach ($query as $album) {
$albums[] = [
'id' => $album->id,
'title' => $album->title,
'slug' => $album->slug,
'created_at' => $album->created_at,
'covers' => [
'small' => $album->getCoverUrl(Image::SMALL),
'normal' => $album->getCoverUrl(Image::NORMAL)
]
];
}
return Response::json($albums, 200);
}
public function getEdit($id)
{
$album = Album::with('tracks')->find($id);
if (!$album) {
return $this->notFound('Album ' . $id . ' not found!');
}
if ($album->user_id != Auth::user()->id) {
return $this->notAuthorized();
}
$tracks = [];
foreach ($album->tracks as $track) {
$tracks[] = [
'id' => $track->id,
'title' => $track->title
];
}
return Response::json([
'id' => $album->id,
'title' => $album->title,
'user_id' => $album->user_id,
'slug' => $album->slug,
'created_at' => $album->created_at,
'published_at' => $album->published_at,
'description' => $album->description,
'cover_url' => $album->hasCover() ? $album->getCoverUrl(Image::NORMAL) : null,
'real_cover_url' => $album->getCoverUrl(Image::NORMAL),
'tracks' => $tracks
], 200);
}
}

View file

@ -0,0 +1,197 @@
<?php
namespace App\Http\Controllers\Api\Web;
use App\Album;
use App\Comment;
use App\Favourite;
use App\Http\Controllers\ApiControllerBase;
use App\Image;
use App\Track;
use App\User;
use Cover;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Input;
use Illuminate\Support\Facades\Response;
class ArtistsController extends ApiControllerBase
{
public function getFavourites($slug)
{
$user = User::whereSlug($slug)->first();
if (!$user) {
App::abort(404);
}
$favs = Favourite::whereUserId($user->id)->with([
'track.genre',
'track.cover',
'track.user',
'album.cover',
'album.user',
'track' => function ($query) {
$query->userDetails();
},
'album' => function ($query) {
$query->userDetails();
}
])->get();
$tracks = [];
$albums = [];
foreach ($favs as $fav) {
if ($fav->type == 'App\Track') {
$tracks[] = Track::mapPublicTrackSummary($fav->track);
} else {
if ($fav->type == 'App\Album') {
$albums[] = Album::mapPublicAlbumSummary($fav->album);
}
}
}
return Response::json([
'tracks' => $tracks,
'albums' => $albums
], 200);
}
public function getContent($slug)
{
$user = User::whereSlug($slug)->first();
if (!$user) {
App::abort(404);
}
$query = Track::summary()->published()->listed()->explicitFilter()->with('genre', 'cover',
'user')->userDetails()->whereUserId($user->id)->whereNotNull('published_at');
$tracks = [];
$singles = [];
foreach ($query->get() as $track) {
if ($track->album_id != null) {
$tracks[] = Track::mapPublicTrackSummary($track);
} else {
$singles[] = Track::mapPublicTrackSummary($track);
}
}
$query = Album::summary()
->with('user')
->orderBy('created_at', 'desc')
->where('track_count', '>', 0)
->whereUserId($user->id);
$albums = [];
foreach ($query->get() as $album) {
$albums[] = Album::mapPublicAlbumSummary($album);
}
return Response::json(['singles' => $singles, 'albumTracks' => $tracks, 'albums' => $albums], 200);
}
public function getShow($slug)
{
$user = User::whereSlug($slug)
->userDetails()
->with([
'comments' => function ($query) {
$query->with('user');
}
])
->first();
if (!$user) {
App::abort(404);
}
$trackQuery = Track::summary()
->published()
->explicitFilter()
->listed()
->with('genre', 'cover', 'user')
->userDetails()
->whereUserId($user->id)
->whereNotNull('published_at')
->orderBy('created_at', 'desc')
->take(20);
$latestTracks = [];
foreach ($trackQuery->get() as $track) {
$latestTracks[] = Track::mapPublicTrackSummary($track);
}
$comments = [];
foreach ($user->comments as $comment) {
$comments[] = Comment::mapPublic($comment);
}
$userData = [
'is_following' => false
];
if ($user->users->count()) {
$userRow = $user->users[0];
$userData = [
'is_following' => (bool)$userRow->is_followed
];
}
return Response::json([
'artist' => [
'id' => (int)$user->id,
'name' => $user->display_name,
'slug' => $user->slug,
'is_archived' => (bool)$user->is_archived,
'avatars' => [
'small' => $user->getAvatarUrl(Image::SMALL),
'normal' => $user->getAvatarUrl(Image::NORMAL)
],
'created_at' => $user->created_at,
'followers' => [],
'following' => [],
'latest_tracks' => $latestTracks,
'comments' => $comments,
'bio' => $user->bio,
'mlpforums_username' => $user->mlpforums_name,
'message_url' => $user->message_url,
'user_data' => $userData
]
], 200);
}
public function getIndex()
{
$page = 1;
if (Input::has('page')) {
$page = Input::get('page');
}
$query = User::orderBy('created_at', 'desc')
->where('track_count', '>', 0);
$count = $query->count();
$perPage = 40;
$query->skip(($page - 1) * $perPage)->take($perPage);
$users = [];
foreach ($query->get() as $user) {
$users[] = [
'id' => $user->id,
'name' => $user->display_name,
'slug' => $user->slug,
'url' => $user->url,
'is_archived' => $user->is_archived,
'avatars' => [
'small' => $user->getAvatarUrl(Image::SMALL),
'normal' => $user->getAvatarUrl(Image::NORMAL)
],
'created_at' => $user->created_at
];
}
return Response::json(["artists" => $users, "current_page" => $page, "total_pages" => ceil($count / $perPage)],
200);
}
}

View file

@ -0,0 +1,13 @@
<?php
namespace App\Http\Controllers\Api\Web;
use App\Http\Controllers\Controller;
class AuthController extends Controller
{
public function postLogout()
{
\Auth::logout();
}
}

View file

@ -0,0 +1,50 @@
<?php
namespace App\Http\Controllers\Api\Web;
use App;
use App\Commands\CreateCommentCommand;
use App\Comment;
use App\Http\Controllers\ApiControllerBase;
use Illuminate\Support\Facades\Input;
use Illuminate\Support\Facades\Response;
class CommentsController extends ApiControllerBase
{
public function postCreate($type, $id)
{
return $this->execute(new CreateCommentCommand($type, $id, Input::all()));
}
public function getIndex($type, $id)
{
$column = '';
if ($type == 'track') {
$column = 'track_id';
} else {
if ($type == 'user') {
$column = 'profile_id';
} else {
if ($type == 'album') {
$column = 'album_id';
} else {
if ($type == 'playlist') {
$column = 'playlist_id';
} else {
App::abort(500);
}
}
}
}
$query = Comment::where($column, '=', $id)->orderBy('created_at', 'desc')->with('user');
$comments = [];
foreach ($query->get() as $comment) {
$comments[] = Comment::mapPublic($comment);
}
return Response::json(['list' => $comments, 'count' => count($comments)]);
}
}

View file

@ -0,0 +1,47 @@
<?php
namespace App\Http\Controllers\Api\Web;
use App\Http\Controllers\ApiControllerBase;
use App\News;
use App\Track;
use Cover;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Input;
use Illuminate\Support\Facades\Response;
class DashboardController extends ApiControllerBase
{
public function getIndex()
{
$recentQuery = Track::summary()
->with(['genre', 'user', 'cover', 'user.avatar'])
->whereIsLatest(true)
->listed()
->userDetails()
->explicitFilter()
->published()
->orderBy('published_at', 'desc')
->take(30);
$recentTracks = [];
foreach ($recentQuery->get() as $track) {
$recentTracks[] = Track::mapPublicTrackSummary($track);
}
return Response::json([
'recent_tracks' => $recentTracks,
'popular_tracks' => Track::popular(30, Auth::check() && Auth::user()->can_see_explicit_content),
'news' => News::getNews(0, 10)
], 200);
}
public function postReadNews()
{
News::markPostAsRead(Input::get('url'));
return Response::json([
]);
}
}

View file

@ -0,0 +1,110 @@
<?php
namespace App\Http\Controllers\Api\Web;
use App\Album;
use App\Commands\ToggleFavouriteCommand;
use App\Favourite;
use App\Http\Controllers\ApiControllerBase;
use App\Playlist;
use App\Track;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Input;
use Illuminate\Support\Facades\Response;
class FavouritesController extends ApiControllerBase
{
public function postToggle()
{
return $this->execute(new ToggleFavouriteCommand(Input::get('type'), Input::get('id')));
}
public function getTracks()
{
$query = Favourite
::whereUserId(Auth::user()->id)
->whereNotNull('track_id')
->with([
'track' => function ($query) {
$query
->userDetails()
->published();
},
'track.user',
'track.genre',
'track.cover',
'track.album',
'track.album.user'
]);
$tracks = [];
foreach ($query->get() as $fav) {
if ($fav->track == null) // deleted track
{
continue;
}
$tracks[] = Track::mapPublicTrackSummary($fav->track);
}
return Response::json(["tracks" => $tracks], 200);
}
public function getAlbums()
{
$query = Favourite
::whereUserId(Auth::user()->id)
->whereNotNull('album_id')
->with([
'album' => function ($query) {
$query->userDetails();
},
'album.user',
'album.user.avatar',
'album.cover'
]);
$albums = [];
foreach ($query->get() as $fav) {
if ($fav->album == null) // deleted album
{
continue;
}
$albums[] = Album::mapPublicAlbumSummary($fav->album);
}
return Response::json(["albums" => $albums], 200);
}
public function getPlaylists()
{
$query = Favourite
::whereUserId(Auth::user()->id)
->whereNotNull('playlist_id')
->with([
'playlist' => function ($query) {
$query->userDetails();
},
'playlist.user',
'playlist.user.avatar',
'playlist.tracks',
'playlist.tracks.cover'
]);
$playlists = [];
foreach ($query->get() as $fav) {
if ($fav->playlist == null) // deleted playlist
{
continue;
}
$playlists[] = Playlist::mapPublicPlaylistSummary($fav->playlist);
}
return Response::json(["playlists" => $playlists], 200);
}
}

View file

@ -0,0 +1,15 @@
<?php
namespace App\Http\Controllers\Api\Web;
use App\Commands\ToggleFollowingCommand;
use App\Http\Controllers\ApiControllerBase;
use Illuminate\Support\Facades\Input;
class FollowController extends ApiControllerBase
{
public function postToggle()
{
return $this->execute(new ToggleFollowingCommand(Input::get('type'), Input::get('id')));
}
}

View file

@ -0,0 +1,31 @@
<?php
namespace App\Http\Controllers\Api\Web;
use App\Http\Controllers\ApiControllerBase;
use App\Image;
use Cover;
use Illuminate\Support\Facades\Response;
class ImagesController extends ApiControllerBase
{
public function getOwned()
{
$query = Image::where('uploaded_by', \Auth::user()->id);
$images = [];
foreach ($query->get() as $image) {
$images[] = [
'id' => $image->id,
'urls' => [
'small' => $image->getUrl(Image::SMALL),
'normal' => $image->getUrl(Image::NORMAL),
'thumbnail' => $image->getUrl(Image::THUMBNAIL),
'original' => $image->getUrl(Image::ORIGINAL)
],
'filename' => $image->filename
];
}
return Response::json($images, 200);
}
}

View file

@ -0,0 +1,143 @@
<?php
namespace App\Http\Controllers\Api\Web;
use App\Commands\AddTrackToPlaylistCommand;
use App\Commands\CreatePlaylistCommand;
use App\Commands\DeletePlaylistCommand;
use App\Commands\EditPlaylistCommand;
use App\Http\Controllers\ApiControllerBase;
use App\Image;
use App\Playlist;
use App\ResourceLogItem;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Input;
use Illuminate\Support\Facades\Response;
class PlaylistsController extends ApiControllerBase
{
public function postCreate()
{
return $this->execute(new CreatePlaylistCommand(Input::all()));
}
public function postEdit($id)
{
return $this->execute(new EditPlaylistCommand($id, Input::all()));
}
public function postDelete($id)
{
return $this->execute(new DeletePlaylistCommand($id, Input::all()));
}
public function postAddTrack($id)
{
return $this->execute(new AddTrackToPlaylistCommand($id, Input::get('track_id')));
}
public function getIndex()
{
$page = 1;
if (Input::has('page')) {
$page = Input::get('page');
}
$query = Playlist::summary()
->with('user', 'user.avatar', 'tracks', 'tracks.cover', 'tracks.user', 'tracks.album', 'tracks.album.user')
->userDetails()
->orderBy('created_at', 'desc')
->where('track_count', '>', 0)
->whereIsPublic(true);
$count = $query->count();
$perPage = 40;
$query->skip(($page - 1) * $perPage)->take($perPage);
$playlists = [];
foreach ($query->get() as $playlist) {
$playlists[] = Playlist::mapPublicPlaylistSummary($playlist);
}
return Response::json([
"playlists" => $playlists,
"current_page" => $page,
"total_pages" => ceil($count / $perPage)
], 200);
}
public function getShow($id)
{
$playlist = Playlist::with([
'tracks.user',
'tracks.genre',
'tracks.cover',
'tracks.album',
'tracks' => function ($query) {
$query->userDetails();
},
'comments',
'comments.user'
])->userDetails()->find($id);
if (!$playlist || !$playlist->canView(Auth::user())) {
App::abort('404');
}
if (Input::get('log')) {
ResourceLogItem::logItem('playlist', $id, ResourceLogItem::VIEW);
$playlist->view_count++;
}
return Response::json(Playlist::mapPublicPlaylistShow($playlist), 200);
}
public function getPinned()
{
$query = Playlist
::userDetails()
->with('tracks', 'tracks.cover', 'tracks.user', 'user')
->join('pinned_playlists', function ($join) {
$join->on('playlist_id', '=', 'playlists.id');
})
->where('pinned_playlists.user_id', '=', Auth::user()->id)
->orderBy('title', 'asc')
->select('playlists.*')
->get();
$playlists = [];
foreach ($query as $playlist) {
$mapped = Playlist::mapPublicPlaylistSummary($playlist);
$mapped['description'] = $playlist->description;
$mapped['is_pinned'] = true;
$playlists[] = $mapped;
}
return Response::json($playlists, 200);
}
public function getOwned()
{
$query = Playlist::summary()->with('pins', 'tracks', 'tracks.cover')->where('user_id',
\Auth::user()->id)->orderBy('title', 'asc')->get();
$playlists = [];
foreach ($query as $playlist) {
$playlists[] = [
'id' => $playlist->id,
'title' => $playlist->title,
'slug' => $playlist->slug,
'created_at' => $playlist->created_at,
'description' => $playlist->description,
'url' => $playlist->url,
'covers' => [
'small' => $playlist->getCoverUrl(Image::SMALL),
'normal' => $playlist->getCoverUrl(Image::NORMAL)
],
'is_pinned' => $playlist->hasPinFor(Auth::user()->id),
'is_public' => $playlist->is_public == 1
];
}
return Response::json($playlists, 200);
}
}

View file

@ -0,0 +1,29 @@
<?php
namespace App\Http\Controllers\Api\Web;
use App\Http\Controllers\Controller;
use App\ProfileRequest;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Response;
class ProfilerController extends Controller
{
public function getRequest($id)
{
if (!Config::get('app.debug')) {
return;
}
$key = 'profiler-request-' . $id;
$request = Cache::get($key);
if (!$request) {
exit();
}
Cache::forget($key);
return Response::json(['request' => ProfileRequest::load($request)->toArray()], 200);
}
}

View file

@ -0,0 +1,26 @@
<?php
namespace App\Http\Controllers\Api\Web;
use App\Genre;
use App\Http\Controllers\ApiControllerBase;
use App\License;
use App\ShowSong;
use App\TrackType;
use Illuminate\Support\Facades\DB;
class TaxonomiesController extends ApiControllerBase
{
public function getAll()
{
return \Response::json([
'licenses' => License::all()->toArray(),
'genres' => Genre::select('genres.*',
DB::raw('(SELECT COUNT(id) FROM tracks WHERE tracks.genre_id = genres.id AND tracks.published_at IS NOT NULL) AS track_count'))->orderBy('name')->get()->toArray(),
'track_types' => TrackType::select('track_types.*',
DB::raw('(SELECT COUNT(id) FROM tracks WHERE tracks.track_type_id = track_types.id AND tracks.published_at IS NOT NULL) AS track_count'))->get()->toArray(),
'show_songs' => ShowSong::select('title', 'id', 'slug',
DB::raw('(SELECT COUNT(tracks.id) FROM show_song_track INNER JOIN tracks ON tracks.id = show_song_track.track_id WHERE show_song_track.show_song_id = show_songs.id AND tracks.published_at IS NOT NULL) AS track_count'))->get()->toArray()
], 200);
}
}

View file

@ -0,0 +1,159 @@
<?php
namespace App\Http\Controllers\Api\Web;
use App\Commands\DeleteTrackCommand;
use App\Commands\EditTrackCommand;
use App\Commands\UploadTrackCommand;
use App\Http\Controllers\ApiControllerBase;
use App\ResourceLogItem;
use App\Track;
use Cover;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Input;
use Illuminate\Support\Facades\Response;
class TracksController extends ApiControllerBase
{
public function postUpload()
{
session_write_close();
return $this->execute(new UploadTrackCommand());
}
public function postDelete($id)
{
return $this->execute(new DeleteTrackCommand($id));
}
public function postEdit($id)
{
return $this->execute(new EditTrackCommand($id, Input::all()));
}
public function getShow($id)
{
$track = Track::userDetails()->withComments()->find($id);
if (!$track || !$track->canView(Auth::user())) {
return $this->notFound('Track not found!');
}
if (Input::get('log')) {
ResourceLogItem::logItem('track', $id, ResourceLogItem::VIEW);
$track->view_count++;
}
$returned_track = Track::mapPublicTrackShow($track);
if ($returned_track['is_downloadable'] != 1) {
unset($returned_track['formats']);
}
return Response::json(['track' => $returned_track], 200);
}
public function getIndex()
{
$page = 1;
$perPage = 45;
if (Input::has('page')) {
$page = Input::get('page');
}
$query = Track::summary()
->userDetails()
->listed()
->explicitFilter()
->published()
->with('user', 'genre', 'cover', 'album', 'album.user');
$this->applyFilters($query);
$totalCount = $query->count();
$query->take($perPage)->skip($perPage * ($page - 1));
$tracks = [];
$ids = [];
foreach ($query->get(['tracks.*']) as $track) {
$tracks[] = Track::mapPublicTrackSummary($track);
$ids[] = $track->id;
}
return Response::json([
"tracks" => $tracks,
"current_page" => $page,
"total_pages" => ceil($totalCount / $perPage)
], 200);
}
public function getOwned()
{
$query = Track::summary()->where('user_id', \Auth::user()->id)->orderBy('created_at', 'desc');
$tracks = [];
foreach ($query->get() as $track) {
$tracks[] = Track::mapPrivateTrackSummary($track);
}
return Response::json($tracks, 200);
}
public function getEdit($id)
{
$track = Track::with('showSongs')->find($id);
if (!$track) {
return $this->notFound('Track ' . $id . ' not found!');
}
if ($track->user_id != Auth::user()->id) {
return $this->notAuthorized();
}
return Response::json(Track::mapPrivateTrackShow($track), 200);
}
private function applyFilters($query)
{
if (Input::has('order')) {
$order = \Input::get('order');
$parts = explode(',', $order);
$query->orderBy($parts[0], $parts[1]);
}
if (Input::has('is_vocal')) {
$isVocal = \Input::get('is_vocal');
if ($isVocal == 'true') {
$query->whereIsVocal(true);
} else {
$query->whereIsVocal(false);
}
}
if (Input::has('in_album')) {
if (Input::get('in_album') == 'true') {
$query->whereNotNull('album_id');
} else {
$query->whereNull('album_id');
}
}
if (Input::has('genres')) {
$query->whereIn('genre_id', Input::get('genres'));
}
if (Input::has('types')) {
$query->whereIn('track_type_id', Input::get('types'));
}
if (Input::has('songs')) {
$query->join('show_song_track', function ($join) {
$join->on('tracks.id', '=', 'show_song_track.track_id');
});
$query->whereIn('show_song_track.show_song_id', Input::get('songs'));
}
return $query;
}
}

View file

@ -0,0 +1,35 @@
<?php
namespace App\Http\Controllers;
use Response;
abstract class ApiControllerBase extends Controller
{
protected function execute($command)
{
if (!$command->authorize()) {
return $this->notAuthorized();
}
$result = $command->execute();
if ($result->didFail()) {
return Response::json([
'message' => 'Validation failed',
'errors' => $result->getValidator()->messages()->getMessages()
], 400);
}
return Response::json($result->getResponse(), 200);
}
public function notAuthorized()
{
return Response::json(['message' => 'You may not do this!'], 403);
}
public function notFound($message)
{
return Response::json(['message' => $message], 403);
}
}

View file

@ -0,0 +1,35 @@
<?php
namespace App\Http\Controllers;
use App;
use App\User;
use Illuminate\Contracts\View\View;
class ArtistsController extends Controller
{
public function getIndex()
{
return View::make('artists.index');
}
public function getProfile($slug)
{
$user = User::whereSlug($slug)->first();
if (!$user) {
App::abort('404');
}
return View::make('artists.profile');
}
public function getShortlink($id)
{
$user = User::find($id);
if (!$user) {
App::abort('404');
}
return Redirect::action('ArtistsController@getProfile', [$id]);
}
}

View file

@ -0,0 +1,115 @@
<?php
namespace App\Http\Controllers;
use App\User;
use Auth;
use Config;
use DB;
use Input;
use Poniverse;
use Redirect;
use URL;
class AuthController extends Controller
{
protected $poniverse;
public function __construct()
{
$this->poniverse = new Poniverse(Config::get('poniverse.client_id'), Config::get('poniverse.secret'));
$this->poniverse->setRedirectUri(URL::to('/auth/oauth'));
}
public function getLogin()
{
if (Auth::guest()) {
return Redirect::to($this->poniverse->getAuthenticationUrl('login'));
}
return Redirect::to('/');
}
public function postLogout()
{
Auth::logout();
return Redirect::to('/');
}
public function getOAuth()
{
$code = $this->poniverse->getClient()->getAccessToken(
Config::get('poniverse.urls')['token'],
'authorization_code',
[
'code' => Input::query('code'),
'redirect_uri' => URL::to('/auth/oauth')
]);
if ($code['code'] != 200) {
if ($code['code'] == 400 && $code['result']['error_description'] == 'The authorization code has expired' && !isset($this->request['login_attempt'])) {
return Redirect::to($this->poniverse->getAuthenticationUrl('login_attempt'));
}
return Redirect::to('/')->with('message',
'Unfortunately we are having problems attempting to log you in at the moment. Please try again at a later time.');
}
$this->poniverse->setAccessToken($code['result']['access_token']);
$poniverseUser = $this->poniverse->getUser();
$token = DB::table('oauth2_tokens')->where('external_user_id', '=', $poniverseUser['id'])->where('service', '=',
'poniverse')->first();
$setData = [
'access_token' => $code['result']['access_token'],
'expires' => date('Y-m-d H:i:s', strtotime("+" . $code['result']['expires_in'] . " Seconds", time())),
'type' => $code['result']['token_type'],
];
if (isset($code['result']['refresh_token']) && !empty($code['result']['refresh_token'])) {
$setData['refresh_token'] = $code['result']['refresh_token'];
}
if ($token) {
//User already exists, update access token and refresh token if provided.
DB::table('oauth2_tokens')->where('id', '=', $token->id)->update($setData);
return $this->loginRedirect(User::find($token->user_id));
}
//Check by email to see if they already have an account
$localMember = User::where('email', '=', $poniverseUser['email'])->first();
if ($localMember) {
return $this->loginRedirect($localMember);
}
$user = new User;
$user->mlpforums_name = $poniverseUser['username'];
$user->display_name = $poniverseUser['display_name'];
$user->email = $poniverseUser['email'];
$user->created_at = gmdate("Y-m-d H:i:s", time());
$user->uses_gravatar = 1;
$user->save();
//We need to insert a new token row :O
$setData['user_id'] = $user->id;
$setData['external_user_id'] = $poniverseUser['id'];
$setData['service'] = 'poniverse';
DB::table('oauth2_tokens')->insert($setData);
return $this->loginRedirect($user);
}
protected function loginRedirect($user, $rememberMe = true)
{
Auth::login($user, $rememberMe);
return Redirect::to('/');
}
}

View file

@ -0,0 +1,23 @@
<?php
namespace App\Http\Controllers;
use View;
class ContentController extends Controller
{
public function getTracks()
{
return View::make('shared.null');
}
public function getAlbums()
{
return View::make('shared.null');
}
public function getPlaylists()
{
return View::make('shared.null');
}
}

View file

@ -0,0 +1,12 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Foundation\Validation\ValidatesRequests;
abstract class Controller extends BaseController
{
use DispatchesJobs, ValidatesRequests;
}

View file

@ -0,0 +1,23 @@
<?php
namespace App\Http\Controllers;
use View;
class FavouritesController extends Controller
{
public function getTracks()
{
return View::make('shared.null');
}
public function getAlbums()
{
return View::make('shared.null');
}
public function getPlaylists()
{
return View::make('shared.null');
}
}

View file

@ -0,0 +1,13 @@
<?php
namespace App\Http\Controllers;
use View;
class HomeController extends Controller
{
public function getIndex()
{
return View::make('home.index');
}
}

View file

@ -0,0 +1,52 @@
<?php
namespace App\Http\Controllers;
use App\Image;
use Config;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Redirect;
use Response;
use URL;
class ImagesController extends Controller
{
public function getImage($id, $type)
{
$coverType = Image::getImageTypeFromName($type);
if ($coverType == null) {
App::abort(404);
}
$image = Image::find($id);
if (!$image) {
App::abort(404);
}
$response = Response::make('', 200);
$filename = $image->getFile($coverType['id']);
if (!is_file($filename)) {
$redirect = URL::to('/images/icons/profile_' . Image::$ImageTypes[$coverType['id']]['name'] . '.png');
return Redirect::to($redirect);
}
if (Config::get('app.sendfile')) {
$response->header('X-Sendfile', $filename);
} else {
$response->header('X-Accel-Redirect', $filename);
}
$response->header('Content-Disposition', 'filename="' . $filename . '"');
$response->header('Content-Type', 'image/png');
$lastModified = filemtime($filename);
$response->header('Last-Modified', $lastModified);
$response->header('Cache-Control', 'max-age=' . (60 * 60 * 24 * 7));
return $response;
}
}

View file

@ -0,0 +1,70 @@
<?php
namespace App\Http\Controllers;
use App;
use App\Playlist;
use App\ResourceLogItem;
use App\Track;
use Auth;
use Illuminate\Support\Facades\Redirect;
use View;
class PlaylistsController extends Controller
{
public function getIndex()
{
return View::make('playlists.index');
}
public function getPlaylist($id, $slug)
{
$playlist = Playlist::find($id);
if (!$playlist || !$playlist->canView(Auth::user())) {
App::abort(404);
}
if ($playlist->slug != $slug) {
return Redirect::action('PlaylistsController@getPlaylist', [$id, $playlist->slug]);
}
return View::make('playlists.show');
}
public function getShortlink($id)
{
$playlist = Playlist::find($id);
if (!$playlist || !$playlist->canView(Auth::user())) {
App::abort(404);
}
return Redirect::action('PlaylistsController@getPlaylist', [$id, $playlist->slug]);
}
public function getDownload($id, $extension)
{
$playlist = Playlist::with('tracks', 'user', 'tracks.album')->find($id);
if (!$playlist || !$playlist->is_public) {
App::abort(404);
}
$format = null;
$formatName = null;
foreach (Track::$Formats as $name => $item) {
if ($item['extension'] == $extension) {
$format = $item;
$formatName = $name;
break;
}
}
if ($format == null) {
App::abort(404);
}
ResourceLogItem::logItem('playlist', $id, ResourceLogItem::DOWNLOAD, $format['index']);
$downloader = new PlaylistDownloader($playlist, $formatName);
$downloader->download();
}
}

View file

@ -0,0 +1,153 @@
<?php
namespace App\Http\Controllers;
use App\ResourceLogItem;
use App\Track;
use App\TrackFile;
use Auth;
use Config;
use Illuminate\Support\Facades\App;
use Redirect;
use Response;
use View;
class TracksController extends Controller
{
public function getIndex()
{
return View::make('tracks.index');
}
public function getEmbed($id)
{
$track = Track
::whereId($id)
->published()
->userDetails()
->with(
'user',
'user.avatar',
'genre'
)->first();
if (!$track || !$track->canView(Auth::user())) {
App::abort(404);
}
$userData = [
'stats' => [
'views' => 0,
'plays' => 0,
'downloads' => 0
],
'is_favourited' => false
];
if ($track->users->count()) {
$userRow = $track->users[0];
$userData = [
'stats' => [
'views' => $userRow->view_count,
'plays' => $userRow->play_count,
'downloads' => $userRow->download_count,
],
'is_favourited' => $userRow->is_favourited
];
}
return View::make('tracks.embed', ['track' => $track, 'user' => $userData]);
}
public function getTrack($id, $slug)
{
$track = Track::find($id);
if (!$track || !$track->canView(Auth::user())) {
App::abort(404);
}
if ($track->slug != $slug) {
return Redirect::action('TracksController@getTrack', [$id, $track->slug]);
}
return View::make('tracks.show');
}
public function getShortlink($id)
{
$track = Track::find($id);
if (!$track || !$track->canView(Auth::user())) {
App::abort(404);
}
return Redirect::action('TracksController@getTrack', [$id, $track->slug]);
}
public function getStream($id, $extension)
{
$track = Track::find($id);
if (!$track || !$track->canView(Auth::user())) {
App::abort(404);
}
$trackFile = TrackFile::findOrFailByExtension($track->id, $extension);
ResourceLogItem::logItem('track', $id, ResourceLogItem::PLAY, $trackFile->getFormat()['index']);
$response = Response::make('', 200);
$filename = $trackFile->getFile();
if (Config::get('app.sendfile')) {
$response->header('X-Sendfile', $filename);
} else {
$response->header('X-Accel-Redirect', $filename);
}
$time = gmdate(filemtime($filename));
if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && $time == $_SERVER['HTTP_IF_MODIFIED_SINCE']) {
header('HTTP/1.0 304 Not Modified');
exit();
}
$response->header('Last-Modified', $time);
$response->header('Content-Type', $trackFile->getFormat()['mime_type']);
return $response;
}
public function getDownload($id, $extension)
{
$track = Track::find($id);
if (!$track || !$track->canView(Auth::user())) {
App::abort(404);
}
$trackFile = TrackFile::findOrFailByExtension($track->id, $extension);
ResourceLogItem::logItem('track', $id, ResourceLogItem::DOWNLOAD, $trackFile->getFormat()['index']);
$response = Response::make('', 200);
$filename = $trackFile->getFile();
if (Config::get('app.sendfile')) {
$response->header('X-Sendfile', $filename);
$response->header('Content-Disposition',
'attachment; filename="' . $trackFile->getDownloadFilename() . '"');
} else {
$response->header('X-Accel-Redirect', $filename);
$response->header('Content-Disposition',
'attachment; filename="' . $trackFile->getDownloadFilename() . '"');
}
$time = gmdate(filemtime($filename));
if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && $time == $_SERVER['HTTP_IF_MODIFIED_SINCE']) {
header('HTTP/1.0 304 Not Modified');
exit();
}
$response->header('Last-Modified', $time);
$response->header('Content-Type', $trackFile->getFormat()['mime_type']);
return $response;
}
}

View file

@ -0,0 +1,13 @@
<?php
namespace App\Http\Controllers;
use View;
class UploaderController extends Controller
{
public function getIndex()
{
return View::make('shared.null');
}
}

View file

@ -0,0 +1,26 @@
<?php
namespace App\Http\Controllers;
use App\User;
use File;
use Illuminate\Support\Facades\App;
class UsersController extends Controller
{
public function getAvatar($id, $type)
{
$coverType = Cover::getCoverFromName($type);
if ($coverType == null) {
App::abort(404);
}
$user = User::find($id);
if (!$user) {
App::abort(404);
}
return File::inline($user->getAvatarFile($coverType['id']), 'image/png', 'cover.png');
}
}

34
app/Http/Kernel.php Normal file
View file

@ -0,0 +1,34 @@
<?php
namespace App\Http;
use Illuminate\Foundation\Http\Kernel as HttpKernel;
class Kernel extends HttpKernel
{
/**
* The application's global HTTP middleware stack.
*
* @var array
*/
protected $middleware = [
\Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
];
/**
* The application's route middleware.
*
* @var array
*/
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'csrf' => \App\Http\Middleware\VerifyCsrfHeader::class,
];
}

View file

@ -0,0 +1,42 @@
<?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()) {
return redirect()->guest('login');
}
return $next($request);
}
}

View 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 = [
//
];
}

View 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
* @return void
*/
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('/home');
}
return $next($request);
}
}

View file

@ -0,0 +1,28 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Session\TokenMismatchException;
use Session;
class VerifyCsrfHeader
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
* @throws TokenMismatchException
*/
public function handle($request, Closure $next)
{
if (Session::token() != $request->input('_token') && Session::token() != $request->header('X-Token')) {
throw new TokenMismatchException;
}
return $next($request);
}
}

View 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 = [
//
];
}

View file

@ -0,0 +1,10 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
abstract class Request extends FormRequest
{
//
}

161
app/Http/routes.php Normal file
View file

@ -0,0 +1,161 @@
<?php
/*
|--------------------------------------------------------------------------
| Application Routes
|--------------------------------------------------------------------------
|
| Here is where you can register all of the routes for an application.
| It's a breeze. Simply tell Laravel the URIs it should respond to
| and give it the controller to call when that URI is requested.
|
*/
if (Config::get('app.debug')) {
Route::get('/api/web/profiler/{id}', 'Api\Web\ProfilerController@getRequest');
}
Route::get('/dashboard', 'TracksController@getIndex');
Route::get('/tracks', 'TracksController@getIndex');
Route::get('/tracks/popular', 'TracksController@getIndex');
Route::get('/tracks/random', 'TracksController@getIndex');
Route::get('tracks/{id}-{slug}', 'TracksController@getTrack');
Route::get('t{id}', 'TracksController@getShortlink' );
Route::get('t{id}/embed', 'TracksController@getEmbed' );
Route::get('t{id}/stream.{extension}', 'TracksController@getStream' );
Route::get('t{id}/dl.{extension}', 'TracksController@getDownload' );
Route::get('albums', 'AlbumsController@getIndex');
Route::get('albums/{id}-{slug}', 'AlbumsController@getShow');
Route::get('a{id}', 'AlbumsController@getShortlink')->where('id', '\d+');
Route::get('a{id}/dl.{extension}', 'AlbumsController@getDownload' );
Route::get('artists', 'ArtistsController@getIndex');
Route::get('playlists', 'PlaylistsController@getIndex');
Route::get('/register', 'AccountController@getRegister');
Route::get('/login', 'AuthController@getLogin');
Route::get('/auth/oauth', 'AuthController@getOAuth');
Route::get('/about', function() { return View::make('pages.about'); });
Route::get('/faq', function() { return View::make('pages.faq'); });
Route::get('i{id}/{type}.png', 'ImagesController@getImage')->where('id', '\d+');
Route::get('u{id}/avatar_{type}.png', 'UsersController@getAvatar')->where('id', '\d+');
Route::get('playlist/{id}-{slug}', 'PlaylistsController@getPlaylist');
Route::get('p{id}', 'PlaylistsController@getShortlink')->where('id', '\d+');
Route::get('p{id}/dl.{extension}', 'PlaylistsController@getDownload' );
Route::group(['prefix' => 'api/v1'], function() {
Route::get('/tracks/radio-details/{hash}', 'Api\V1\TracksController@getTrackRadioDetails');
Route::post('/tracks/radio-details/{hash}', 'Api\V1\TracksController@getTrackRadioDetails');
});
Route::group(['prefix' => 'api/web'], function() {
Route::get('/taxonomies/all', 'Api\Web\TaxonomiesController@getAll');
Route::get('/playlists/show/{id}', 'Api\Web\PlaylistsController@getShow');
Route::get('/tracks', 'Api\Web\TracksController@getIndex');
Route::get('/tracks/{id}', 'Api\Web\TracksController@getShow')->where('id', '\d+');
Route::get('/albums', 'Api\Web\AlbumsController@getIndex');
Route::get('/albums/{id}', 'Api\Web\AlbumsController@getShow')->where('id', '\d+');
Route::get('/playlists', 'Api\Web\PlaylistsController@getIndex');
Route::get('/playlists/{id}', 'Api\Web\PlaylistsController@getShow')->where('id', '\d+');
Route::get('/comments/{type}/{id}', 'Api\Web\CommentsController@getIndex')->where('id', '\d+');
Route::get('/artists', 'Api\Web\ArtistsController@getIndex');
Route::get('/artists/{slug}', 'Api\Web\ArtistsController@getShow');
Route::get('/artists/{slug}/content', 'Api\Web\ArtistsController@getContent');
Route::get('/artists/{slug}/favourites', 'Api\Web\ArtistsController@getFavourites');
Route::get('/dashboard', 'Api\Web\DashboardController@getIndex');
Route::group(['before' => 'auth|csrf'], function() {
Route::post('/tracks/upload', 'Api\Web\TracksController@postUpload');
Route::post('/tracks/delete/{id}', 'Api\Web\TracksController@postDelete');
Route::post('/tracks/edit/{id}', 'Api\Web\TracksController@postEdit');
Route::post('/albums/create', 'Api\Web\AlbumsController@postCreate');
Route::post('/albums/delete/{id}', 'Api\Web\AlbumsController@postDelete');
Route::post('/albums/edit/{id}', 'Api\Web\AlbumsController@postEdit');
Route::post('/playlists/create', 'Api\Web\PlaylistsController@postCreate');
Route::post('/playlists/delete/{id}', 'Api\Web\PlaylistsController@postDelete');
Route::post('/playlists/edit/{id}', 'Api\Web\PlaylistsController@postEdit');
Route::post('/playlists/{id}/add-track', 'Api\Web\PlaylistsController@postAddTrack');
Route::post('/comments/{type}/{id}', 'Api\Web\CommentsController@postCreate')->where('id', '\d+');
Route::post('/account/settings/save', 'Api\Web\AccountController@postSave');
Route::post('/favourites/toggle', 'Api\Web\FavouritesController@postToggle');
Route::post('/follow/toggle', 'Api\Web\FollowController@postToggle');
Route::post('/dashboard/read-news', 'Api\Web\DashboardController@postReadNews');
});
Route::group(['before' => 'auth'], function() {
Route::get('/account/settings', 'Api\Web\AccountController@getSettings');
Route::get('/images/owned', 'Api\Web\ImagesController@getOwned');
Route::get('/tracks/owned', 'Api\Web\TracksController@getOwned');
Route::get('/tracks/edit/{id}', 'Api\Web\TracksController@getEdit');
Route::get('/albums/owned', 'Api\Web\AlbumsController@getOwned');
Route::get('/albums/edit/{id}', 'Api\Web\AlbumsController@getEdit');
Route::get('/playlists/owned', 'Api\Web\PlaylistsController@getOwned');
Route::get('/playlists/pinned', 'Api\Web\PlaylistsController@getPinned');
Route::get('/favourites/tracks', 'Api\Web\FavouritesController@getTracks');
Route::get('/favourites/albums', 'Api\Web\FavouritesController@getAlbums');
Route::get('/favourites/playlists', 'Api\Web\FavouritesController@getPlaylists');
});
Route::group(['before' => 'csrf'], function(){
Route::post('/auth/logout', 'Api\Web\AuthController@postLogout');
});
});
Route::group(['prefix' => 'account'], function() {
Route::group(['before' => 'auth'], function(){
Route::get('/favourites/tracks', 'FavouritesController@getTracks');
Route::get('/favourites/albums', 'FavouritesController@getAlbums');
Route::get('/favourites/playlists', 'FavouritesController@getPlaylists');
Route::get('/tracks', 'ContentController@getTracks');
Route::get('/tracks/edit/{id}', 'ContentController@getTracks');
Route::get('/albums', 'ContentController@getAlbums');
Route::get('/albums/edit/{id}', 'ContentController@getAlbums');
Route::get('/albums/create', 'ContentController@getAlbums');
Route::get('/playlists', 'ContentController@getPlaylists');
Route::get('/uploader', 'UploaderController@getIndex');
Route::get('/', 'AccountController@getIndex');
});
});
Route::get('u{id}', 'ArtistsController@getShortlink')->where('id', '\d+');
Route::get('users/{id}-{slug}', 'ArtistsController@getShortlink')->where('id', '\d+');
Route::get('{slug}', 'ArtistsController@getProfile');
Route::get('{slug}/content', 'ArtistsController@getProfile');
Route::get('{slug}/favourites', 'ArtistsController@getProfile');
Route::get('/', 'HomeController@getIndex');
Route::group(['domain' => 'api.pony.fm'], function() {
Route::get('tracks/latest', ['uses' => 'Api\Mobile\TracksController@latest']);
Route::get('tracks/popular', [ 'uses' => 'Api\Mobile\TracksController@popular']);
/* Route::get('tracks/id', [ 'uses' => 'Api\Mobile\TracksController@track']);
Route::get('user', ['uses' => 'Api\Mobile\UserController@user']);*/
});

117
app/Image.php Normal file
View file

@ -0,0 +1,117 @@
<?php
namespace App;
use External;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\URL;
use Symfony\Component\HttpFoundation\File\UploadedFile;
class Image extends Model
{
const NORMAL = 1;
const ORIGINAL = 2;
const THUMBNAIL = 3;
const SMALL = 4;
public static $ImageTypes = [
self::NORMAL => ['id' => self::NORMAL, 'name' => 'normal', 'width' => 350, 'height' => 350],
self::ORIGINAL => ['id' => self::ORIGINAL, 'name' => 'original', 'width' => null, 'height' => null],
self::SMALL => ['id' => self::SMALL, 'name' => 'small', 'width' => 100, 'height' => 100],
self::THUMBNAIL => ['id' => self::THUMBNAIL, 'name' => 'thumbnail', 'width' => 50, 'height' => 50]
];
public static function getImageTypeFromName($name)
{
foreach (self::$ImageTypes as $cover) {
if ($cover['name'] != $name) {
continue;
}
return $cover;
}
return null;
}
public static function upload(UploadedFile $file, $user)
{
$userId = $user;
if ($user instanceof User) {
$userId = $user->id;
}
$hash = md5_file($file->getPathname());
$image = Image::whereHash($hash)->whereUploadedBy($userId)->first();
if ($image) {
return $image;
}
$image = new Image();
try {
$image->uploaded_by = $userId;
$image->size = $file->getSize();
$image->filename = $file->getClientOriginalName();
$image->extension = $file->getClientOriginalExtension();
$image->mime = $file->getMimeType();
$image->hash = $hash;
$image->save();
$image->ensureDirectoryExists();
foreach (self::$ImageTypes as $coverType) {
$command = 'convert 2>&1 "' . $file->getPathname() . '" -background transparent -flatten +matte -strip -quality 95 -format png ';
if (isset($coverType['width']) && isset($coverType['height'])) {
$command .= '-thumbnail ' . $coverType['width'] . 'x' . $coverType['height'] . '^ -gravity center -extent ' . $coverType['width'] . 'x' . $coverType['height'] . ' ';
}
$command .= '"' . $image->getFile($coverType['id']) . '"';
External::execute($command);
}
return $image;
} catch (\Exception $e) {
$image->delete();
throw $e;
}
}
protected $table = 'images';
public function getUrl($type = self::NORMAL)
{
$type = self::$ImageTypes[$type];
return URL::to('i' . $this->id . '/' . $type['name'] . '.png');
}
public function getFile($type = self::NORMAL)
{
return $this->getDirectory() . '/' . $this->getFilename($type);
}
public function getFilename($type = self::NORMAL)
{
$typeInfo = self::$ImageTypes[$type];
return $this->id . '_' . $typeInfo['name'] . '.png';
}
public function getDirectory()
{
$dir = (string)(floor($this->id / 100) * 100);
return Config::get('ponyfm.files_directory') . '/images/' . $dir;
}
public function ensureDirectoryExists()
{
$destination = $this->getDirectory();
umask(0);
if (!is_dir($destination)) {
mkdir($destination, 0777, true);
}
}
}

21
app/Jobs/Job.php Normal file
View 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 "queueOn" and "delay" queue helper methods.
|
*/
use Queueable;
}

118
app/Library/Assets.php Normal file
View file

@ -0,0 +1,118 @@
<?php
class Assets
{
public static function scriptIncludes($area = 'app')
{
if (!Config::get("app.debug")) {
return '<script src="/build/scripts/' . $area . '.js?' . filemtime("./build/scripts/" . $area . ".js") . '"></script>';
}
$scripts = self::mergeGlobs(self::getScriptsForArea($area));
$retVal = "";
foreach ($scripts as $script) {
$filename = self::replaceExtensionWith($script, ".coffee", ".js");
$retVal .= "<script src='/build/$filename?" . filemtime('./build/' . $filename) . "'></script>";
}
return $retVal;
}
public static function styleIncludes($area = 'app')
{
if (!Config::get("app.debug")) {
return '<script>document.write(\'<link rel="stylesheet" href="build/styles/' . $area . '.css?' . filemtime("build/styles/" . $area . ".css") . '" />\');</script>';
}
$styles = self::mergeGlobs(self::getStylesForArea($area));
$retVal = "";
foreach ($styles as $style) {
$filename = self::replaceExtensionWith($style, ".less", ".css");
$retVal .= "<link rel='stylesheet' href='/build/$filename?" . filemtime('./build/' . $filename) . "' />";
}
return $retVal;
}
private static function replaceExtensionWith($filename, $fromExtension, $toExtension)
{
$fromLength = strlen($fromExtension);
return substr($filename, -$fromLength) == $fromExtension
? substr($filename, 0, strlen($filename) - $fromLength) . $toExtension
: $filename;
}
/** Merges an array of paths that are passed into "glob" into a list of unique filenames.
* Note that this method assumes the globs should be relative to the "app" folder of this project */
private static function mergeGlobs($globs)
{
$files = [];
$filesFound = [];
foreach ($globs as $glob) {
foreach (glob("../resources/assets/" . $glob, GLOB_BRACE) as $file) {
if (isset($filesFound[$file])) {
continue;
}
$filesFound[$file] = true;
$files[] = substr($file, 20); // chop off ../app/
}
}
return $files;
}
private static function getScriptsForArea($area)
{
if ($area == 'app') {
return [
"scripts/base/jquery-2.0.2.js",
"scripts/base/angular.js",
"scripts/base/*.{coffee,js}",
"scripts/shared/*.{coffee,js}",
"scripts/app/*.{coffee,js}",
"scripts/app/services/*.{coffee,js}",
"scripts/app/filters/*.{coffee,js}",
"scripts/app/directives/*.{coffee,js}",
"scripts/app/controllers/*.{coffee,js}",
"scripts/**/*.{coffee,js}"
];
} else {
if ($area == 'embed') {
return [
"scripts/base/jquery-2.0.2.js",
"scripts/base/jquery.viewport.js",
"scripts/base/underscore.js",
"scripts/base/moment.js",
"scripts/base/jquery.timeago.js",
"scripts/base/soundmanager2-nodebug.js",
"scripts/embed/*.coffee"
];
}
}
throw new Exception();
}
private static function getStylesForArea($area)
{
if ($area == 'app') {
return [
"styles/base/jquery-ui.css",
"styles/base/colorbox.css",
"styles/app.less",
"styles/profiler.less"
];
} else {
if ($area == 'embed') {
return [
"styles/embed.less"
];
}
}
throw new Exception();
}
}

View file

@ -0,0 +1,19 @@
<?php
class AudioCache
{
private static $_movieCache = array();
/**
* @param $filename
* @return FFmpegMovie
*/
public static function get($filename)
{
if (isset(self::$_movieCache[$filename])) {
return self::$_movieCache[$filename];
}
return self::$_movieCache[$filename] = new FFmpegMovie($filename);
}
}

View file

@ -0,0 +1,36 @@
<?php
use Assetic\Asset\BaseAsset;
use Assetic\Filter\FilterInterface;
/**
* Class CacheBusterAsset
* OH GOD IT BUUUUUUURNS
*
* Well, I may as well tell you why this awful class exists. So... Assetic doesn't quite support less's import
* directive. I mean; it supports it insofar as Less itself supports it - but it doesn't take into account the
* last modified time for imported assets. Since we only have one less file that imports everything else... well
* you can see where this is going. This asset will let us override the last modified time for an entire collection
* which allows me to write a custom mechanism for cache busting.
*/
class CacheBusterAsset extends BaseAsset
{
private $_lastModified;
/**
* @param int $lastModified
*/
public function __construct($lastModified)
{
$this->_lastModified = $lastModified;
parent::__construct([], '', '', []);
}
public function load(FilterInterface $additionalFilter = null)
{
}
public function getLastModified()
{
return $this->_lastModified;
}
}

16
app/Library/External.php Normal file
View file

@ -0,0 +1,16 @@
<?php
use Illuminate\Support\Facades\Log;
class External
{
public static function execute($command)
{
$output = [];
$error = exec($command, $output);
if ($error != null) {
Log::error('"' . $command . '" failed with "' . $error . '"');
}
}
}

32
app/Library/File.php Normal file
View file

@ -0,0 +1,32 @@
<?php
/**
* File
*
* Note: Remember to remove the "File" alias in APP_DIR/config/application.php
*
* @author Phill Sparks <me@phills.me.uk>
*/
class File extends \Illuminate\Support\Facades\File
{
public static function inline($path, $mime, $name = null)
{
if (is_null($name)) {
$name = basename($path);
}
$response = Response::make(static::get($path));
$response->header('Content-Type', $mime);
$response->header('Content-Disposition', 'inline; filename="' . $name . '"');
$response->header('Content-Transfer-Encoding', 'binary');
$response->header('Expires', 0);
$response->header('Cache-Control', 'must-revalidate, post-check=0, pre-check=0');
$response->header('Pragma', 'public');
$response->header('Content-Length', filesize($path));
return $response;
}
}

30
app/Library/Gravatar.php Normal file
View file

@ -0,0 +1,30 @@
<?php
use Illuminate\Support\Facades\URL;
class Gravatar
{
public static function getUrl($email, $size = 80, $default = null, $rating = 'g')
{
$url = 'https://www.gravatar.com/avatar/';
$url .= md5(strtolower(trim($email)));
$url .= "?s=$size&r=$rating";
if ($default != null) {
$url .= "&d=" . $default;
} else {
$size = 'normal';
if ($size == 50) {
$size = 'thumbnail';
} else {
if ($size == 100) {
$size = 'small';
}
}
$url .= "&d=" . urlencode(URL::to('/images/icons/profile_' . $size . '.png'));
}
return $url;
}
}

60
app/Library/Helpers.php Normal file
View file

@ -0,0 +1,60 @@
<?php
class Helpers
{
/**
* Removes whitespace and special characters from a string
* and sets all characters to lower case.
*/
public static function sanitizeInputForHashing($value)
{
$value = preg_replace('/[^A-Za-z0-9]/', '', $value);
return strtolower($value);
}
public static function template($template)
{
echo file_get_contents('templates/' . $template);
}
public static function angular($expression)
{
return '{{' . $expression . '}}';
}
public static function formatBytes($bytes, $precision = 2)
{
if ($bytes == 0) {
return '0 MB';
}
$units = array('B', 'KB', 'MB', 'GB', 'TB');
$bytes = max($bytes, 0);
$pow = floor(($bytes ? log($bytes) : 0) / log(1024));
$pow = min($pow, count($units) - 1);
$bytes /= pow(1024, $pow);
return round($bytes, $precision) . ' ' . $units[$pow];
}
/**
* timeago-style timestamp generator macro.
*
* @param string $timestamp A timestamp in SQL DATETIME syntax
* @return string
*/
public static function timestamp($timestamp)
{
if (gettype($timestamp) !== 'string' && get_class($timestamp) === 'DateTime') {
$timestamp = $timestamp->format('c');
}
$title = date('c', strtotime($timestamp));
$content = date('F d, o \@ g:i:s a', strtotime($timestamp));
return '<abbr class="timeago" title="' . $title . '">' . $content . '</abbr>';
}
}

35
app/Library/IpsHasher.php Normal file
View file

@ -0,0 +1,35 @@
<?php
use Illuminate\Contracts\Hashing\Hasher;
class IpsHasher implements Hasher
{
public function make($value, array $options = array())
{
return md5(md5($options['salt']) . md5(static::ips_sanitize($value)));
}
public function check($value, $hashedValue, array $options = array())
{
return static::make($value, ['salt' => $options['salt']]) === $hashedValue;
}
public function needsRehash($hashedValue, array $options = array())
{
return false;
}
static public function ips_sanitize($value)
{
$value = str_replace('&', '&amp;', $value);
$value = str_replace('\\', '&#092;', $value);
$value = str_replace('!', '&#33;', $value);
$value = str_replace('$', '&#036;', $value);
$value = str_replace('"', '&quot;', $value);
$value = str_replace('<', '&lt;', $value);
$value = str_replace('>', '&gt;', $value);
$value = str_replace('\'', '&#39;', $value);
return $value;
}
}

26
app/Library/PFMAuth.php Normal file
View file

@ -0,0 +1,26 @@
<?php
use Illuminate\Auth\EloquentUserProvider;
use Illuminate\Contracts\Hashing\Hasher;
class NullHasher implements Hasher
{
public function make($value, array $options = array())
{
}
public function check($value, $hashedValue, array $options = array())
{
}
public function needsRehash($hashedValue, array $options = array())
{
}
}
class PFMAuth extends EloquentUserProvider
{
function __construct()
{
parent::__construct(new NullHasher(), 'App\User');
}
}

View file

@ -0,0 +1,158 @@
<?php
class PfmValidator extends Illuminate\Validation\Validator
{
/**
* Determine if a given rule implies that the attribute is required.
*
* @param string $rule
* @return bool
*/
protected function implicit($rule)
{
return $rule == 'required' or $rule == 'accepted' or $rule == 'required_with' or $rule == 'required_when';
}
/**
* Validate the audio format of the file.
*
* @param string $attribute
* @param array $value
* @param array $parameters
* @return bool
*/
public function validateAudioFormat($attribute, $value, $parameters)
{
// attribute is the file field
// value is the file array itself
// parameters is a list of formats the file can be, verified via ffmpeg
$file = AudioCache::get($value->getPathname());
return in_array($file->getAudioCodec(), $parameters);
}
/**
* Validate the sample rate of the audio file.
*
* @param string $attribute
* @param array $value
* @param array $parameters
* @return bool
*/
public function validateSampleRate($attribute, $value, $parameters)
{
// attribute is the file field
// value is the file array itself
// parameters is a list of sample rates the file can be, verified via ffmpeg
$file = AudioCache::get($value->getPathname());
return in_array($file->getAudioSampleRate(), $parameters);
}
/**
* Validate the number of channels in the audio file.
*
* @param string $attribute
* @param array $value
* @param array $parameters
* @return bool
*/
public function validateAudioChannels($attribute, $value, $parameters)
{
// attribute is the file field
// value is the file array itself
// parameters is a list of sample rates the file can be, verified via ffmpeg
$file = AudioCache::get($value->getPathname());
return in_array($file->getAudioChannels(), $parameters);
}
/**
* Validate the bit rate of the audio file.
*
* @param string $attribute
* @param array $value
* @param array $parameters
* @return bool
*/
public function validateAudioBitrate($attribute, $value, $parameters)
{
// attribute is the file field
// value is the file array itself
// parameters is a list of sample rates the file can be, verified via ffmpeg
$file = AudioCache::get($value->getPathname());
return in_array($file->getAudioBitRate(), $parameters);
}
/**
* Validate the duration of the audio file, in seconds.
*
* @param string $attribute
* @param array $value
* @param array $parameters
* @return bool
*/
public function validateMinDuration($attribute, $value, $parameters)
{
// attribute is the file field
// value is the file array itself
// parameters is an array containing one value: the minimum duration
$file = AudioCache::get($value->getPathname());
return $file->getDuration() >= (float)$parameters[0];
}
/**
* Require a field when the value of another field matches a certain value.
*
* @param string $attribute
* @param array $value
* @param array $parameters
* @return bool
*/
/** OLD CODE
* public function validate_required_when($attribute, $value, $parameters)
* {
* if ( Input::get($parameters[0]) === $parameters[1] && static::required($attribute, $value) ){
* return true;
*
* } else {
* return false;
* }
* }
**/
// custom required_when validator
public function validateRequiredWhen($attribute, $value, $parameters)
{
if (Input::get($parameters[0]) == $parameters[1]) {
return $this->validate_required($attribute, $value);
}
return true;
}
// custom image width validator
public function validateMinWidth($attribute, $value, $parameters)
{
return getimagesize($value->getPathname())[0] >= $parameters[0];
}
// custom image height validator
public function validateMinHeight($attribute, $value, $parameters)
{
return getimagesize($value->getPathname())[1] >= $parameters[0];
}
public function validateTextareaLength($attribute, $value, $parameters)
{
return strlen(str_replace("\r\n", "\n", $value)) <= $parameters[0];
}
}

Some files were not shown because too many files have changed in this diff Show more