Many things

This commit is contained in:
nelsonlaquet 2013-08-01 03:57:08 -05:00
parent 1aac8a8f64
commit b71efac59f
79 changed files with 4297 additions and 233 deletions

View file

@ -0,0 +1,64 @@
<?php
use Illuminate\Console\Command;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
class MigrateOldData extends Command {
protected $name = 'migrate-old-data';
protected $description = 'Migrates data from the old pfm site.';
public function __construct()
{
parent::__construct();
}
public function fire() {
$this->call('migrate:refresh');
$oldDb = DB::connection('old');
$oldUsers = $oldDb->table('users')->get();
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,
'password_hash' => $user->password_hash,
'password_salt' => $user->password_salt,
'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
]);
}
}
protected function getArguments()
{
return [
['example', InputArgument::REQUIRED, 'An example argument.'],
];
}
protected function getOptions() {
return [
['example', null, InputOption::VALUE_OPTIONAL, 'An example option.', null],
];
}
}

View file

@ -4,11 +4,9 @@
use Commands\CreateAlbumCommand;
use Commands\DeleteAlbumCommand;
use Commands\DeleteTrackCommand;
use Commands\EditAlbumCommand;
use Commands\EditTrackCommand;
use Cover;
use Entities\Album;
use Entities\Comment;
use Entities\Image;
use Entities\Track;
use Illuminate\Support\Facades\Auth;
@ -29,7 +27,7 @@
}
public function getShow($id) {
$album = Album::with('tracks', 'user')->find($id);
$album = Album::with(['tracks', 'user', 'comments' => function($query) { $query->with('user'); }])->details()->find($id);
if (!$album)
App::abort(404);
@ -47,6 +45,11 @@
];
}
$comments = [];
foreach ($album->comments as $comment) {
$comments[] = Comment::mapPublic($comment);
}
return Response::json([
'album' => [
'id' => $album->id,
@ -71,7 +74,8 @@
'views' => 0,
'downloads' => 0
],
'comments' => ['count' => 0, 'list' => []]
'comments' => ['count' => count($comments), 'list' => $comments],
'is_favourited' => $album->favourites->count() > 0
]
], 200);
}
@ -82,7 +86,8 @@
$page = Input::get('page');
$query = Album::summary()
->with('tracks', 'user')
->with(['tracks' => function($query) { $query->details(); }, 'user'])
->details()
->orderBy('created_at', 'desc')
->whereRaw('(SELECT COUNT(id) FROM tracks WHERE tracks.album_id = albums.id) > 0');

View file

@ -9,6 +9,8 @@
use Commands\EditTrackCommand;
use Cover;
use Entities\Album;
use Entities\Comment;
use Entities\Favourite;
use Entities\Image;
use Entities\Track;
use Entities\User;
@ -17,6 +19,33 @@
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' => function($query) { $query->details(); },
'album' => function($query) { $query->details(); }])->get();
$tracks = [];
$albums = [];
foreach ($favs as $fav) {
if ($fav->type == 'Entities\Track') {
$tracks[] = Track::mapPublicTrackSummary($fav->track);
}
else if ($fav->type == 'Entities\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)
@ -49,17 +78,22 @@
}
public function getShow($slug) {
$user = User::whereSlug($slug)->first();
$user = User::whereSlug($slug)->with(['comments' => function ($query) { $query->with('user'); }])->first();
if (!$user)
App::abort(404);
$trackQuery = Track::summary()->whereUserId($user->id)->whereNotNull('published_at')->orderBy('created_at', 'desc')->take(10);
$trackQuery = Track::summary()->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);
}
return Response::json([
'artist' => [
'id' => $user->id,
@ -73,7 +107,7 @@
'followers' => [],
'following' => [],
'latest_tracks' => $latestTracks,
'comments' => ['count' => 0, 'list' => []],
'comments' => ['count' => count($comments), 'list' => $comments],
'bio' => $user->bio,
'mlpforums_username' => $user->mlpforums_name
]

View file

@ -0,0 +1,42 @@
<?php
namespace Api\Web;
use Commands\CreateCommentCommand;
use Entities\Album;
use Entities\Comment;
use Entities\Image;
use Entities\Playlist;
use Entities\Track;
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

@ -14,41 +14,14 @@
class DashboardController extends \ApiControllerBase {
public function getIndex() {
$query = Track::summary()->with(['genre', 'user', 'cover'])->whereNotNull('published_at')->orderBy('published_at', 'desc')->take(15);
$query = Track::summary()->with(['genre', 'user', 'cover'])->details()->whereNotNull('published_at')->orderBy('published_at', 'desc')->take(30);
if (!Auth::check() || !Auth::user()->can_see_explicit_content)
$query->whereIsExplicit(false);
$tracks = [];
foreach ($query->get() as $track) {
$tracks[] = [
'id' => $track->id,
'title' => $track->title,
'user' => [
'id' => $track->user->id,
'name' => $track->user->display_name,
'url' => $track->user->url
],
'url' => $track->url,
'slug' => $track->slug,
'is_vocal' => $track->is_vocal,
'is_explicit' => $track->is_explicit,
'is_downloadable' => $track->is_downloadable,
'is_published' => $track->isPublished(),
'published_at' => $track->published_at,
'duration' => $track->duration,
'genre' => [
'id' => $track->genre->id,
'slug' => $track->genre->slug,
'name' => $track->genre->name
],
'track_type_id' => $track->track_type_id,
'covers' => [
'thumbnail' => $track->getCoverUrl(Image::THUMBNAIL),
'small' => $track->getCoverUrl(Image::SMALL),
'normal' => $track->getCoverUrl(Image::NORMAL)
]
];
$tracks[] = Track::mapPublicTrackSummary($track);
}
return Response::json([

View file

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

View file

@ -2,18 +2,13 @@
namespace Api\Web;
use Commands\CreateAlbumCommand;
use Commands\AddTrackToPlaylistCommand;
use Commands\CreatePlaylistCommand;
use Commands\DeleteAlbumCommand;
use Commands\DeletePlaylistCommand;
use Commands\DeleteTrackCommand;
use Commands\EditAlbumCommand;
use Commands\EditPlaylistCommand;
use Commands\EditTrackCommand;
use Cover;
use Entities\Album;
use Entities\Comment;
use Entities\Image;
use Entities\PinnedPlaylist;
use Entities\Playlist;
use Entities\Track;
use Illuminate\Support\Facades\Auth;
@ -33,11 +28,25 @@
return $this->execute(new DeletePlaylistCommand($id, Input::all()));
}
public function postAddTrack($id) {
return $this->execute(new AddTrackToPlaylistCommand($id, Input::get('track_id')));
}
public function getShow($id) {
$playlist = Playlist::find($id);
$playlist = Playlist::with(['tracks' => function($query) { $query->details(); }, 'comments' => function($query) { $query->with('user'); }])->find($id);
if (!$playlist || !$playlist->canView(Auth::user()))
App::abort('404');
$tracks = [];
foreach ($playlist->tracks as $track) {
$tracks[] = Track::mapPublicTrackSummary($track);
}
$comments = [];
foreach ($playlist->comments as $comment) {
$comments[] = Comment::mapPublic($comment);
}
return Response::json([
'id' => $playlist->id,
'title' => $playlist->title,
@ -50,7 +59,9 @@
'normal' => $playlist->getCoverUrl(Image::NORMAL)
],
'is_pinned' => true,
'is_public' => $playlist->is_public == 1
'is_public' => $playlist->is_public == 1,
'tracks' => $tracks,
'comments' => ['count' => count($comments), 'list' => $comments],
], 200);
}

View file

@ -6,6 +6,7 @@
use Commands\EditTrackCommand;
use Commands\UploadTrackCommand;
use Cover;
use Entities\Favourite;
use Entities\Image;
use Entities\Track;
use Illuminate\Support\Facades\Auth;
@ -27,7 +28,7 @@
}
public function getShow($id) {
$track = Track::find($id);
$track = Track::details()->withComments()->find($id);
if (!$track || !$track->canView(Auth::user()))
return $this->notFound('Track not found!');
@ -35,7 +36,7 @@
}
public function getRecent() {
$query = Track::summary()->with(['genre', 'user', 'cover'])->whereNotNull('published_at')->orderBy('published_at', 'desc')->take(15);
$query = Track::summary()->details()->with(['genre', 'user', 'cover'])->whereNotNull('published_at')->orderBy('published_at', 'desc')->take(30);
if (!Auth::check() || !Auth::user()->can_see_explicit_content)
$query->whereIsExplicit(false);
@ -54,15 +55,23 @@
if (Input::has('page'))
$page = Input::get('page');
$query = Track::summary()->whereNotNull('published_at');
$query = Track::summary()
->details()
->whereNotNull('published_at')
->with('user', 'genre');
$this->applyFilters($query);
$totalCount = $query->count();
$query->take(30)->skip(30 * ($page - 1));
$tracks = [];
foreach ($query->get() as $track)
$ids = [];
foreach ($query->get() as $track) {
$tracks[] = Track::mapPublicTrackSummary($track);
$ids[] = $track->id;
}
return Response::json(["tracks" => $tracks, "current_page" => $page, "total_pages" => ceil($totalCount / 30)], 200);
}

View file

@ -26,4 +26,19 @@
return Redirect::action('TracksController@getTrack', [$id, $track->slug]);
}
public function getStream($id) {
$track = Track::find($id);
if (!$track || !$track->canView(Auth::user()))
App::abort(404);
$format = Track::$Formats['MP3'];
$response = Response::make('', 200);
$response->header('X-Sendfile', $track->getFileFor('MP3'));
$response->header('Content-Disposition', 'filename=' . $track->getFilenameFor('MP3'));
$response->header('Content-Type', $format['mime_type']);
return $response;
}
}

View file

@ -12,7 +12,7 @@ class CreateUsersTable extends Migration {
$table->boolean('sync_names')->default(true);
$table->string('password_hash', 32);
$table->string('password_salt', 5);
$table->string('email', 150)->unique();
$table->string('email', 150)->indexed();
$table->string('gravatar')->nullable();
$table->string('slug');
$table->boolean('uses_gravatar')->default(true);

View file

@ -2040,8 +2040,8 @@ Twilight Sparkle: Yes! Everything’s going to be just fine!",
public function down() {
Schema::table('show_song_track', function($table){
$table->drop_foreign('show_song_track_track_id_foreign');
$table->drop_foreign('show_song_track_show_song_id_foreign');
$table->dropForeign('show_song_track_track_id_foreign');
$table->dropForeign('show_song_track_show_song_id_foreign');
});
Schema::drop('show_song_track');

View file

@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
class CreateFavourites extends Migration {
public function up() {
Schema::create('favourites', function($table){
$table->increments('id');
$table->integer('user_id')->unsigned()->index();
$table->integer('track_id')->unsigned()->nullable()->index();
$table->integer('album_id')->unsigned()->nullable()->index();
$table->integer('playlist_id')->unsigned()->nullable()->index();
$table->timestamps();
$table->foreign('user_id')->references('id')->on('users')->on_delete('cascade');
$table->foreign('track_id')->references('id')->on('tracks');
$table->foreign('album_id')->references('id')->on('albums');
$table->foreign('playlist_id')->references('id')->on('playlists');
});
}
public function down() {
Schema::table('favourites', function($table){
$table->dropForeign('favourites_user_id_foreign');
$table->dropForeign('favourites_track_id_foreign');
$table->dropForeign('favourites_album_id_foreign');
$table->dropForeign('favourites_playlist_id_foreign');
});
Schema::drop('favourites');
}
}

View file

@ -0,0 +1,37 @@
<?php
use Illuminate\Database\Migrations\Migration;
class CreateComments extends Migration {
public function up() {
Schema::create('comments', function($table){
$table->increments('id');
$table->integer('user_id')->unsigned();
$table->timestamps();
$table->string('ip_address', 46);
$table->text('content');
$table->timestamp('deleted_at')->nullable()->index();
$table->integer('profile_id')->unsigned()->nullable()->index();
$table->integer('track_id')->unsigned()->nullable()->index();
$table->integer('album_id')->unsigned()->nullable()->index();
$table->integer('playlist_id')->unsigned()->nullable()->index();
$table->foreign('profile_id')->references('id')->on('users');
$table->foreign('user_id')->references('id')->on('users');
$table->foreign('track_id')->references('id')->on('tracks');
$table->foreign('album_id')->references('id')->on('albums');
$table->foreign('playlist_id')->references('id')->on('playlists');
});
}
public function down() {
Schema::table('comments', function($table){
$table->dropForeign('comments_user_id_foreign');
$table->dropForeign('comments_track_id_foreign');
$table->dropForeign('comments_album_id_foreign');
$table->dropForeign('comments_playlist_id_foreign');
});
Schema::drop('comments');
}
}

View file

@ -45,9 +45,11 @@
return new AssetCollection([
new FileAsset('scripts/base/jquery-2.0.2.js'),
new FileAsset('scripts/base/jquery-ui.js'),
new FileAsset('scripts/base/jquery.cookie.js'),
new FileAsset('scripts/base/jquery.colorbox.js'),
new FileAsset('scripts/base/underscore.js'),
new FileAsset('scripts/base/moment.js'),
new FileAsset('scripts/base/soundmanager2-nodebug.js'),
new FileAsset('scripts/base/angular.js'),
new FileAsset('scripts/base/ui-bootstrap-tpls-0.4.0.js'),
new FileAsset('scripts/base/angular-ui-sortable.js'),

View file

@ -0,0 +1,37 @@
<?php
namespace Commands;
use Entities\Album;
use Entities\Favourite;
use Entities\Playlist;
use Entities\Track;
use Illuminate\Support\Facades\Auth;
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]);
return CommandResponse::succeed(['message' => 'Track added!']);
}
}

View file

@ -0,0 +1,71 @@
<?php
namespace Commands;
use Entities\Album;
use Entities\Comment;
use Entities\Image;
use Entities\Track;
use External;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
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,49 @@
<?php
namespace Commands;
use Entities\Album;
use Entities\Favourite;
use Entities\Playlist;
use Entities\Track;
use Illuminate\Support\Facades\Auth;
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)->first();
$isFavourited = false;
if ($existing) {
$existing->delete();
} else {
$fav = new Favourite();
$fav->$typeId = $this->_resourceId;
$fav->user_id = Auth::user()->id;
$fav->save();
$isFavourited = true;
}
return CommandResponse::succeed(['is_favourited' => $isFavourited]);
}
}

View file

@ -3,6 +3,7 @@
namespace Entities;
use Cover;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\URL;
use Whoops\Example\Exception;
use Traits\SlugTrait;
@ -16,12 +17,26 @@
return self::select('id', 'title', 'user_id', 'slug', 'created_at', 'cover_id');
}
public function scopeDetails($query) {
if (Auth::check()) {
$query->with(['favourites' => function($query) {
$query->whereUserId(Auth::user()->id);
}]);
}
return !$query;
}
protected $table = 'albums';
public function user() {
return $this->belongsTo('Entities\User');
}
public function favourites() {
return $this->hasMany('Entities\Favourite');
}
public function cover() {
return $this->belongsTo('Entities\Image');
}
@ -30,6 +45,10 @@
return $this->hasMany('Entities\Track')->orderBy('track_number', 'asc');
}
public function comments(){
return $this->hasMany('Entities\Comment');
}
public static function mapPublicAlbumSummary($album) {
return [
'id' => $album->id,
@ -46,7 +65,8 @@
'id' => $album->user->id,
'name' => $album->user->display_name,
'url' => $album->user->url,
]
],
'is_favourited' => $album->favourites->count() > 0
];
}

View file

@ -0,0 +1,62 @@
<?php
namespace Entities;
class Comment extends \Eloquent {
protected $table = 'comments';
protected $softDelete = true;
public function user(){
return $this->belongsTo('Entities\User');
}
public function track(){
return $this->belongsTo('Entities\Track');
}
public function album(){
return $this->belongsTo('Entities\Album');
}
public function playlist(){
return $this->belongsTo('Entities\Playlist');
}
public function profile(){
return $this->belongsTo('Entities\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,53 @@
<?php
namespace Entities;
class Favourite extends \Eloquent {
protected $table = 'favourites';
/*
|--------------------------------------------------------------------------
| Relationships
|--------------------------------------------------------------------------
*/
public function user(){
return $this->belongsTo('Entities\User');
}
public function track(){
return $this->belongsTo('Entities\Track');
}
public function album(){
return $this->belongsTo('Entities\Album');
}
public function playlist(){
return $this->belongsTo('Entities\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);
}
}

View file

@ -14,7 +14,15 @@
}
public function tracks() {
return $this->belongsToMany('Entities\Track')->orderBy('position', 'asc');
return $this
->belongsToMany('Entities\Track')
->withPivot('position')
->withTimestamps()
->orderBy('position', 'asc');
}
public function comments(){
return $this->hasMany('Entities\Comment');
}
public function pins() {

View file

@ -6,6 +6,7 @@
use External;
use getid3_writetags;
use Helpers;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\URL;
use Illuminate\Support\Str;
@ -29,6 +30,20 @@
return self::select('id', 'title', 'user_id', 'slug', 'is_vocal', 'is_explicit', 'created_at', 'published_at', 'duration', 'is_downloadable', 'genre_id', 'track_type_id', 'cover_id', 'album_id');
}
public function scopeDetails($query) {
if (Auth::check()) {
$query->with(['favourites' => function($query) {
$query->whereUserId(Auth::user()->id);
}]);
}
return !$query;
}
public function scopeWithComments($query) {
$query->with(['comments' => function($query) { $query->with('user'); }]);
}
public static function mapPublicTrackShow($track) {
$returnValue = self::mapPublicTrackSummary($track);
$returnValue['description'] = $track->description;
@ -38,7 +53,14 @@
'plays' => 0,
'downloads' => 0
];
$returnValue['comments'] = ['count' => 0, 'list' => []];
$comments = [];
foreach ($track->comments as $comment) {
$comments[] = Comment::mapPublic($comment);
}
$returnValue['comments'] = ['count' => count($comments), 'list' => $comments];
if ($track->album_id != null) {
$returnValue['album'] = [
@ -94,7 +116,9 @@
'thumbnail' => $track->getCoverUrl(Image::THUMBNAIL),
'small' => $track->getCoverUrl(Image::SMALL),
'normal' => $track->getCoverUrl(Image::NORMAL)
]
],
'is_favourited' => $track->favourites->count() > 0,
'duration' => $track->duration
];
}
@ -142,6 +166,14 @@
return $this->belongsTo('Entities\Genre');
}
public function comments(){
return $this->hasMany('Entities\Comment');
}
public function favourites() {
return $this->hasMany('Entities\Favourite');
}
public function cover() {
return $this->belongsTo('Entities\Image');
}

View file

@ -18,6 +18,10 @@
return $this->belongsTo('Entities\Image');
}
public function comments(){
return $this->hasMany('Entities\Comment', 'profile_id');
}
public function getUrlAttribute() {
return URL::to('/' . $this->slug);
}

View file

@ -18,6 +18,7 @@
Route::get('tracks/{id}-{slug}', 'TracksController@getTrack');
Route::get('t{id}', 'TracksController@getShortlink' );
Route::get('t{id}/stream', 'TracksController@getStream' );
Route::get('t{id}/dl.{extension}', 'TracksController@getDownload' );
Route::get('albums', 'AlbumsController@getIndex');
@ -52,6 +53,10 @@
Route::get('/albums', 'Api\Web\AlbumsController@getIndex');
Route::get('/albums/{id}', 'Api\Web\AlbumsController@getShow')->where('id', '\d+');
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');
@ -71,8 +76,13 @@
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::group(['before' => 'auth'], function() {

View file

@ -11,3 +11,4 @@
|
*/
Artisan::add(new MigrateOldData);

View file

@ -5,22 +5,7 @@
<div>
<h1><a href="/">Pony.fm</a></h1>
<div class="now-playing">
<div class="current-track">
<div class="transport">
<div class="bar" style="width: 44%;"></div>
</div>
<div class="image"></div>
<ul class="buttons">
<li><a class="previous" href="#"><i class="icon-fast-backward"></i></a></li>
<li><a class="play" href="#"><i class="icon-pause"></i></a></li>
<li><a class="next" href="#"><i class="icon-fast-forward"></i></a></li>
<li><a class="volume" href="#"><i class="icon-volume-up"></i></a></li>
</ul>
<div class="title">
<span class="song"><a href="#">Love Me Cheerilee</a></span>
<span class="artist"><a href="#">MandoPony</a></span>
</div>
</div>
<pfm-player></pfm-player>
</div>
</div>
</header>
@ -65,7 +50,7 @@
Account
</h3>
</li>
<li ng-class="{selected: $state.includes('account-favourites')}"><a href="/account/favourites">Favourites</a></li>
{{-- <li ng-class="{selected: $state.includes('account-favourites')}"><a href="/account/favourites">Favourites</a></li> --}}
<li ng-class="{selected: $state.includes('account-content')}"><a href="/account/tracks">Your Content</a></li>
<li ng-class="{selected: isActive('/account')}"><a href="/account">Settings</a></li>
@endif
@ -74,7 +59,7 @@
@if (!Auth::check())
<li ng-class="{selected: isActive('/login')}"><a href="/login">Login</a></li>
<li ng-class="{selected: isActive('/register')}"><a href="/register">Register</a></li>
{{-- <li ng-class="{selected: isActive('/register')}"><a href="/register">Register</a></li> --}}
@endif
<li ng-class="{selected: isActive('/about')}"><a href="/about">About</a></li>

View file

@ -7,3 +7,7 @@
RewriteRule ^(.*\.(?:coffee))$ /asset.php?type=coffee&file=/$1 [L,QSA,NC]
RewriteRule ^(.*\.(?:less))$ /asset.php?type=less&file=/$1 [L,QSA,NC]
</IfModule>
<IfModule xsendfile_module>
XSendFile On
</IfModule>

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -1,11 +1,19 @@
angular.module('ponyfm').controller "application", [
'$scope', 'auth', '$location', 'upload', '$state', '$stateParams', '$injector'
($scope, auth, $location, upload, $state, $stateParams, $injector) ->
'$scope', 'auth', '$location', 'upload', '$state', '$stateParams', '$injector', '$rootScope'
($scope, auth, $location, upload, $state, $stateParams, $injector, $rootScope) ->
$scope.auth = auth.data
$scope.$state = $state
$scope.$stateParams = $stateParams
$loadingElement = null
$rootScope.safeApply = (fn) ->
phase = $rootScope.$$phase
if (phase == '$apply' || phase == 'digest')
fn()
return
$rootScope.$apply fn
$scope.logout = () ->
auth.logout().done -> location.reload()

View file

@ -1,12 +1,13 @@
window.pfm.preloaders['artist-favourites'] = [
'artists', '$state'
(artists, $state) ->
artists.fetch $state.params.slug, true
artists.fetchFavourites $state.params.slug, true
]
angular.module('ponyfm').controller "artist-favourites", [
'$scope', 'artists', '$state'
($scope, artists, $state) ->
artists.fetch($state.params.slug).done (artistResponse) ->
$scope.artist = artistResponse.artist
artists.fetchFavourites($state.params.slug).done (artistResponse) ->
$scope.favourites = artistResponse
console.log artistResponse
]

View file

@ -16,12 +16,12 @@ angular.module('ponyfm').controller "playlist-form", [
playlists.editPlaylist($scope.form)
def
.done ->
dialog.close()
.done (res) ->
dialog.close(res)
.fail (errors)->
$scope.errors = errors
$scope.isLoading = false
$scope.close = () -> dialog.close()
$scope.close = () -> dialog.close(null)
]

View file

@ -1,11 +1,12 @@
angular.module('ponyfm').controller 'playlist', [
'$scope', '$state'
($scope, $state) ->
console.log $state.params.id
$scope.refresh = () ->
$.getJSON('/api/web/playlists/show/' + $state.params.id)
.done (playlist) -> $scope.$apply ->
$scope.playlist = playlist
$scope.refresh()
window.pfm.preloaders['playlist'] = [
'$state', 'playlists'
($state, playlists) ->
playlists.fetch $state.params.id, true
]
angular.module('ponyfm').controller 'playlist', [
'$scope', '$state', 'playlists'
($scope, $state, playlists) ->
playlists.fetch($state.params.id).done (playlist) ->
$scope.playlist = playlist
]

View file

@ -5,14 +5,47 @@ window.pfm.preloaders['track'] = [
]
angular.module('ponyfm').controller "track", [
'$scope', 'tracks', '$state', 'playlists', 'auth'
($scope, tracks, $state, playlists, auth) ->
'$scope', 'tracks', '$state', 'playlists', 'auth', 'favourites', '$dialog'
($scope, tracks, $state, playlists, auth, favourites, $dialog) ->
tracks.fetch($state.params.id).done (trackResponse) ->
$scope.track = trackResponse.track
$scope.trackArray = [$scope.track]
$scope.playlists = []
if auth.data.isLogged
playlists.refreshOwned().done (lists) ->
$scope.playlists.push list for list in lists
$scope.favouriteWorking = false
$scope.toggleFavourite = (track) ->
$scope.favouriteWorking = true
favourites.toggle('track', track.id).done (res) ->
track.is_favourited = res.is_favourited
$scope.favouriteWorking = false
$scope.addToNewPlaylist = () ->
dialog = $dialog.dialog
templateUrl: '/templates/partials/playlist-dialog.html'
controller: 'playlist-form'
resolve: {
playlist: () ->
is_public: true
is_pinned: true
name: ''
description: ''
}
dialog.open().then (playlist) ->
return if !playlist
playlists.addTrackToPlaylist playlist.id, $scope.track.id
$state.transitionTo 'playlist', {id: playlist.id}
$scope.addToPlaylist = (playlist) ->
return if playlist.message
playlists.addTrackToPlaylist(playlist.id, $scope.track.id).done (res) ->
playlist.message = res.message
]

View file

@ -6,6 +6,7 @@ angular.module('ponyfm').directive 'pfmAlbumsList', () ->
class: '@class'
controller: [
'$scope'
($scope) ->
'$scope', 'auth'
($scope, auth) ->
$scope.auth = auth.data
]

View file

@ -0,0 +1,29 @@
angular.module('ponyfm').directive 'pfmComments', () ->
restrict: 'E'
templateUrl: '/templates/directives/comments.html'
scope:
resource: '=resource',
type: '@type'
controller: [
'$scope', 'comments'
($scope, comments, auth) ->
$scope.isWorking = false
$scope.content = ''
$scope.auth = auth.data
refresh = () ->
comments.fetchList($scope.type, $scope.resource.id, true).done (comments) ->
$scope.resource.comments.count = comments.count
$scope.resource.comments.list.length = 0
$scope.resource.comments.list.push comment for comment in comments.list
$scope.isWorking = false
$scope.addComment = () ->
content = $scope.content
$scope.content = ''
$scope.isWorking = true
comments.addComment($scope.type, $scope.resource.id, content).done () ->
refresh()
]

View file

@ -0,0 +1,20 @@
angular.module('ponyfm').directive 'pfmFavouriteButton', () ->
restrict: 'E'
templateUrl: '/templates/directives/favourite-button.html'
scope:
resource: '=resource',
class: '@class',
type: '@type'
controller: [
'$scope', 'favourites', 'auth'
($scope, favourites, auth) ->
$scope.auth = auth.data
$scope.isWorking = false
$scope.toggleFavourite = () ->
$scope.isWorking = true
favourites.toggle($scope.type, $scope.resource.id).done (res) ->
$scope.isWorking = false
$scope.resource.is_favourited = res.is_favourited
]

View file

@ -0,0 +1,73 @@
angular.module('ponyfm').directive 'pfmPlayer', () ->
$element = null
restrict: 'E'
templateUrl: '/templates/directives/player.html'
scope: {}
compile: (element) ->
$element = element
controller: [
'$scope', 'player', 'auth'
($scope, player, auth) ->
$scope.player = player
$scope.auth = auth.data
$scope.playPause = () ->
$scope.player.playPause()
$scope.playNext = () ->
$scope.player.playNext()
$scope.playPrev = () ->
$scope.player.playPrev()
$scope.seek = (e) ->
$transport = $ '.transport'
percent = ((e.pageX - $transport.offset().left) / $transport.width())
duration = parseFloat($scope.player.currentTrack.duration)
$scope.player.seek percent * duration * 1000
isSliding = false
$slider = $element.find('.volume-slider')
$knob = $element.find('.volume-slider .knob')
$bar = $element.find('.volume-slider .bar')
player.readyDef.done ->
initialY = (180 - (180 * (player.volume / 100))) - 7.5
$knob.css {top: initialY}
moveVolumeSlider = (absoluteY) ->
newY = absoluteY - $bar.offset().top;
maxY = $bar.height() - ($knob.height() / 2)
newY = 0 if newY < 0
newY = maxY if newY > maxY
percent = 100 - ((newY / maxY) * 100)
$scope.player.setVolume percent
$knob.css {top: newY}
$knob.click (e) ->
e.preventDefault()
e.stopPropagation()
$slider.click (e) -> $scope.$apply -> moveVolumeSlider(e.pageY)
$(document).mousemove (e) ->
return if !isSliding
moveVolumeSlider(e.pageY)
$knob.mousedown (e) ->
e.preventDefault()
e.stopPropagation()
isSliding = true
$slider.parent().addClass('keep-open')
$(document).mouseup (e) ->
e.preventDefault()
e.stopPropagation()
isSliding = false
$slider.parent().removeClass('keep-open')
]

View file

@ -2,4 +2,4 @@ angular.module('ponyfm').directive 'pfmProgressBar', () ->
(scope, element, attrs) ->
scope.$watch attrs.pfmProgressBar, (val) ->
return if !val?
$(element).css 'width', Math.floor(val) + '%'
$(element).css 'width', val + '%'

View file

@ -6,6 +6,15 @@ angular.module('ponyfm').directive 'pfmTracksList', () ->
class: '@class'
controller: [
'$scope'
($scope) ->
'$scope', 'favourites', 'player', 'auth'
($scope, favourites, player, auth) ->
$scope.auth = auth.data
$scope.toggleFavourite = (track) ->
favourites.toggle('track', track.id).done (res) ->
track.is_favourited = res.is_favourited
$scope.play = (track) ->
index = _.indexOf $scope.tracks, (t) -> t.id == track.id
player.playTracks $scope.tracks, index
]

View file

@ -1,2 +1,4 @@
angular.module('ponyfm').filter 'newlines', () ->
(input) -> input.replace(/\n/g, '<br/>')
(input) ->
return '' if !input
input.replace(/\n/g, '<br/>')

View file

@ -1,5 +1,7 @@
angular.module('ponyfm').filter 'noHTML', () ->
(input) ->
return '' if !input
input.replace(/&/g, '&amp;')
.replace(/>/g, '&gt;')
.replace(/</g, '&lt;')

View file

@ -4,6 +4,7 @@ angular.module('ponyfm').factory('artists', [
artistPage = []
artists = {}
artistContent = {}
artistFavourites = {}
self =
filters: {}
@ -39,5 +40,15 @@ angular.module('ponyfm').factory('artists', [
artistContent[slug] = artistsDef.promise()
fetchFavourites: (slug, force) ->
force = force || false
slug = 1 if !slug
return artistFavourites[slug] if !force && artistFavourites[slug]
artistsDef = new $.Deferred()
$http.get('/api/web/artists/' + slug + '/favourites').success (albums) ->
artistsDef.resolve albums
artistFavourites[slug] = artistsDef.promise()
self
])

View file

@ -0,0 +1,27 @@
angular.module('ponyfm').factory('comments', [
'$rootScope', '$http'
($rootScope, $http) ->
commentCache = []
self =
filters: {}
addComment: (resourceType, resourceId, content) ->
commentDef = new $.Deferred()
$http.post('/api/web/comments/' + resourceType + '/' + resourceId, {content: content, _token: pfm.token}).success (comment) ->
commentDef.resolve comment
commentDef.promise()
fetchList: (resourceType, resourceId, force) ->
key = resourceType + '-' + resourceId
force = force || false
return commentCache[key] if !force && commentCache[key]
commentDef = new $.Deferred()
$http.get('/api/web/comments/' + resourceType + '/' + resourceId).success (comments) ->
commentDef.resolve comments
commentCache[key] = commentDef.promise()
self
])

View file

@ -0,0 +1,13 @@
angular.module('ponyfm').factory('favourites', [
'$rootScope', '$http'
($rootScope, $http) ->
self =
toggle: (type, id) ->
def = new $.Deferred()
$http.post('/api/web/favourites/toggle', {type: type, id: id, _token: pfm.token}).success (res) ->
def.resolve res
def.promise()
self
])

View file

@ -0,0 +1,117 @@
angular.module('ponyfm').factory('player', [
'$rootScope'
($rootScope) ->
readyDef = new $.Deferred()
play = (track) ->
self.currentTrack = track
$rootScope.$broadcast 'player-starting-track', track
self.currentSound = soundManager.createSound
url: '/t' + track.id + '/stream',
volume: self.volume
whileloading: () -> $rootScope.safeApply ->
track.loadingProgress = (self.currentSound.bytesLoaded / self.currentSound.bytesTotal) * 100
whileplaying: () -> $rootScope.safeApply ->
track.progress = (self.currentSound.position / (track.duration * 1000)) * 100
onfinish: () -> $rootScope.safeApply ->
track.isPlaying = false
self.playNext()
onstop: () -> $rootScope.safeApply ->
track.isPlaying = false
self.isPlaying = false
onplay: () -> $rootScope.safeApply ->
track.isPlaying = true
onresume: () -> $rootScope.safeApply ->
track.isPlaying = true
onpause: () -> $rootScope.safeApply ->
track.isPlaying = false
track.isPlaying = true
self.isPlaying = true
self.currentSound.play()
self =
ready: false
isPlaying: false
currentTrack: null
currentSound: null
playlist: []
playlistIndex: 0
volume: 0
readyDef: readyDef.promise()
playPause: () ->
return if !self.ready
return if !self.isPlaying
if self.currentSound.paused
self.currentSound.play()
else
self.currentSound.pause()
playNext: () ->
self.currentSound.stop() if self.currentSound != null
self.playlistIndex++
if self.playlistIndex >= self.playlist.length
self.playlist.length = 0
self.currentTrack = null
self.currentSong = null
self.isPlaying = false
return
play self.playlist[self.playlistIndex]
playPrev: () ->
self.currentSound.stop() if self.currentSound != null
self.playlistIndex--
if self.playlistIndex <= 0
self.playlist.length = 0
self.currentTrack = null
self.currentSong = null
self.isPlaying = false
return
play self.playlist[self.playlistIndex]
seek: (progress) ->
return if !self.currentSound
self.currentSound.setPosition(progress)
setVolume: (theVolume) ->
theVolume = 100 if theVolume > 100
self.currentSound.setVolume(theVolume) if self.currentSound
$.cookie('pfm-volume', theVolume)
self.volume = theVolume
playTracks: (tracks, index) ->
return if !self.ready
return if tracks.length == 0
if tracks[index].isPlaying
self.playPause()
return
self.currentSound.stop() if self.currentSound != null
$rootScope.$broadcast 'player-stopping-playlist'
self.playlist.length = 0
self.playlist.push track for track in tracks
self.playlistIndex = index
$rootScope.$broadcast 'player-starting-playlist', tracks
play tracks[index]
pfm.soundManager.done () ->
self.ready = true
self.setVolume($.cookie('pfm-volume') || 100)
readyDef.resolve()
self
])

View file

@ -2,9 +2,20 @@ angular.module('ponyfm').factory('playlists', [
'$rootScope', '$state', '$http'
($rootScope, $state, $http) ->
playlistDef = null
playlists = {}
self =
pinnedPlaylists: []
fetch: (id, force) ->
force = force || false
return playlists[id] if !force && playlists[id]
def = new $.Deferred()
$http.get('/api/web/playlists/' + id).success (playlist) ->
def.resolve playlist
playlists[id] = def.promise()
refreshOwned: (force) ->
force = force || false
return playlistDef if !force && playlistDef
@ -15,6 +26,13 @@ angular.module('ponyfm').factory('playlists', [
playlistDef
addTrackToPlaylist: (playlistId, trackId) ->
def = new $.Deferred()
$http.post('/api/web/playlists/' + playlistId + '/add-track', {track_id: trackId, _token: pfm.token}).success (res) ->
def.resolve(res)
def
refresh: () ->
$.getJSON('/api/web/playlists/pinned')
.done (playlists) -> $rootScope.$apply ->

View file

@ -0,0 +1,95 @@
/*!
* jQuery Cookie Plugin v1.3.1
* https://github.com/carhartl/jquery-cookie
*
* Copyright 2013 Klaus Hartl
* Released under the MIT license
*/
(function (factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as anonymous module.
define(['jquery'], factory);
} else {
// Browser globals.
factory(jQuery);
}
}(function ($) {
var pluses = /\+/g;
function raw(s) {
return s;
}
function decoded(s) {
return decodeURIComponent(s.replace(pluses, ' '));
}
function converted(s) {
if (s.indexOf('"') === 0) {
// This is a quoted cookie as according to RFC2068, unescape
s = s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\');
}
try {
return config.json ? JSON.parse(s) : s;
} catch(er) {}
}
var config = $.cookie = function (key, value, options) {
// write
if (value !== undefined) {
options = $.extend({}, config.defaults, options);
if (typeof options.expires === 'number') {
var days = options.expires, t = options.expires = new Date();
t.setDate(t.getDate() + days);
}
value = config.json ? JSON.stringify(value) : String(value);
return (document.cookie = [
config.raw ? key : encodeURIComponent(key),
'=',
config.raw ? value : encodeURIComponent(value),
options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
options.path ? '; path=' + options.path : '',
options.domain ? '; domain=' + options.domain : '',
options.secure ? '; secure' : ''
].join(''));
}
// read
var decode = config.raw ? raw : decoded;
var cookies = document.cookie.split('; ');
var result = key ? undefined : {};
for (var i = 0, l = cookies.length; i < l; i++) {
var parts = cookies[i].split('=');
var name = decode(parts.shift());
var cookie = decode(parts.join('='));
if (key && key === name) {
result = converted(cookie);
break;
}
if (!key) {
result[name] = converted(cookie);
}
}
return result;
};
config.defaults = {};
$.removeCookie = function (key, options) {
if ($.cookie(key) !== undefined) {
// Must not alter options, thus extending a fresh object...
$.cookie(key, '', $.extend({}, options, { expires: -1 }));
return true;
}
return false;
};
}));

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,9 @@
def = new $.Deferred()
pfm.soundManager = def.promise()
soundManager.setup
url: '/flash/soundmanager/'
flashVersion: 9
onready: () ->
def.resolve()

View file

@ -18,9 +18,11 @@
&.empty {
.alert();
.border-radius(0px);
float: none !important;
width: auto !important;
display: block;
margin-top: 5px;
padding: 5px;
font-size: 9pt;
}

View file

@ -200,7 +200,7 @@ html {
.pagination {
border: none;
background: #ccc;
background: #ddd;
margin: 5px 0px;
padding: 0px;
overflow: hidden;

View file

@ -13,7 +13,6 @@ html body {
.background-color {
background: rgba(42, 42, 42, 1);
background-image: url('/images/pattern4.jpg');
position: fixed;
left: 0px;
top: 0px;
@ -97,27 +96,46 @@ header {
.image {
float: left;
width: 35px;
height: 35px;
width: 40px;
height: 40px;
background: #aaa;
img {
display: block;
width: 100%;
height: 100%;
}
}
.transport {
display: none;
margin-top: 5px;
margin-left: 6px;
margin-top: -3px;
margin-bottom: 3px;
margin-left: 46px;
height: 5px;
background: #555;
position: relative;
margin-right: 6px;
cursor: pointer;
.bar {
background: #fd8500;
&:hover {
}
.bar, .loader-bar {
position: absolute;
height: 100%;
top: 0px;
left: 0px;
}
.loader-bar {
background: #888;
z-index: 200;
}
.bar {
background: #fd8500;
z-index: 500;
}
}
.title {
@ -149,13 +167,14 @@ header {
margin: 0px;
padding: 0px;
list-style: none;
margin-top: 6px;
margin-right: 6px;
margin-top: 3px;
margin-right: 5px;
li {
float: left;
margin: 0px;
padding: 0px;
position: relative;
a {
display: block;
@ -165,9 +184,52 @@ header {
color: #ddd;
}
&:hover {
.volume-slider {
.box-shadow(0px 5px 9px rgba(50, 50, 50, 0.5));
position: absolute;
border-bottom: 1px solid #fff;
border-top: 3px solid #3366CC;
top: 30px;
background: #eee;
list-style: none;
margin: 0px;
display: none;
width: 100%;
padding: 10px 0px;
z-index: 300;
.bar {
width: 5px;
height: 180px;
background: #aaa;
margin: auto;
position: relative;
cursor: pointer;
.knob {
.border-radius(20px);
height: 15px;
width: 15px;
display: block;
background: #1F3D7B;
border: 2px solid #1F3D7B;
position: absolute;
top: 0px;
left: -7px;
font-size: 1px;
padding: 0px;
}
}
}
&:hover, &.keep-open {
#gradient>.vertical(#fff, #ddd);
.volume-slider {
display: block;
}
a {
text-decoration: none;
color: #000;

View file

@ -1,8 +1,44 @@
@import-once 'base/bootstrap/bootstrap';
@import-once 'mixins';
.comments {
ul {
margin: 0px;
padding: 0px;
list-style: none;
margin-bottom: 5px;
li {
.clearfix();
margin: 0px;
padding: 3px;
border-bottom: 1px solid #ddd;
.meta {
font-size: 8pt;
float: right;
}
.content {
margin-left: 45px;
}
img {
.img-polaroid();
float: left;
width: 32px;
height: 32px;
padding: 2px;
}
}
}
}
.dashboard {
section {
/*
.box-sizing(border-box);
width: 50%;
@ -15,6 +51,7 @@
&:last-child {
padding-left: 10px;
}
*/
}
}
@ -60,8 +97,24 @@
overflow: hidden;
margin: 10px 0px;
padding: 0px;
line-height: normal;
&.empty {
.alert();
.border-radius(0px);
float: none !important;
width: auto !important;
display: block;
margin-top: 5px;
padding: 5px;
font-size: 9pt;
&:hover {
background-color: @warningBackground;
}
}
&:hover, &.is-playing {
background: #eee;
.image {
@ -71,6 +124,22 @@
}
}
&.is-favourited {
.icon-favourite {
color: @orange;
text-decoration: none;
i {
color: #FFD76E;
text-shadow: 0px 0px 2px rgba(0,0,0,0.8);
&:before {
content: "\f005"
}
}
}
}
.image {
height: 40px;
width: 40px;
@ -109,13 +178,18 @@
.icons {
float: right;
font-size: 13px;
font-size: 14px;
margin-right: 2px;
a {
text-decoration: none;
}
a, span {
display: block;
float: left;
margin: 0px 3px;
line-height: 0px;
}
.icon-microphone-off {
@ -166,7 +240,7 @@
}
}
.user-details, .track-details, .album-details {
.user-details, .track-details, .album-details, .playlist-details {
.comments {
.alert {
.border-radius(0px);
@ -212,11 +286,8 @@
}
}
.track-details, .album-details {
.track-details, .album-details, .playlist-details {
h1 {
.box-shadow(0px 2px 3px rgba(0, 0, 0, .3));
background: #eee;
padding: 5px;
}
.lyrics {
@ -263,6 +334,45 @@
}
}
.track-details {
h1 {
margin-left: 55px;
}
.tracks-listing li .image .play-button {
display: block;
}
.tracks-listing {
float: left;
margin-top: 5px;
}
.tracks-listing li {
margin: 0px;
margin-bottom: 5px;
float: left;
.image {
height: 50px;
width: 50px;
.play-button {
line-height: 48px;
}
img {
width: 48px;
height: 48px;
}
}
.icons, .info {
display: none;
}
}
}
html {
.track-toolbar {
padding: 5px;

View file

@ -5,10 +5,7 @@
</ul>
<div class="track-toolbar btn-group pull-right">
<a href="#" class="btn btn-small">
Favourite This!
<i class="icon-star-empty"></i>
</a>
<pfm-favourite-button resource="album" type="album"></pfm-favourite-button>
<div class="dropdown">
<a href="#" class="btn btn-small btn-info dropdown-toggle">
Downloads <i class="caret"></i>
@ -34,20 +31,10 @@
</div>
<h2>Tracks</h2>
<pfm-tracks-list tracks="album.tracks" />
<pfm-tracks-list tracks="album.tracks"></pfm-tracks-list>
<h2>Comments</h2>
<div class="comments">
<div class="alert alert-info" ng-show="album.comments.count == 0">
There are no comments yet!
</div>
<form class="pfm-form">
<div class="form-row">
<textarea></textarea>
</div>
<button type="submit" class="btn disabled">Post Comment</button>
</form>
</div>
<pfm-comments type="album" resource="album"></pfm-comments>
</div>
<div class="span4 cover-image">
<img ng-src="{{album.covers.normal}}" />

View file

@ -5,7 +5,7 @@
</ul>
<div class="track-toolbar btn-group pull-right">
<a href="#" class="btn btn-small">
<a href="#" class="btn btn-small" ng-show="auth.isLogged">
Follow
</a>
<a href="#" class="btn btn-small">

View file

@ -5,10 +5,10 @@
<div ng-show="content.singles.length">
<h2>Singles</h2>
<pfm-tracks-list tracks="content.singles" class="four-column no-artist" />
<pfm-tracks-list tracks="content.singles" class="four-column no-artist"></pfm-tracks-list>
</div>
<div ng-show="content.albumTracks.length">
<h2>Part of an Album</h2>
<pfm-tracks-list tracks="content.albumTracks" class="four-column no-artist" />
<pfm-tracks-list tracks="content.albumTracks" class="four-column no-artist"></pfm-tracks-list>
</div>

View file

@ -1 +1,10 @@
favourites!
<div class="row-fluid">
<div class="span6">
<h2>Tracks</h2>
<pfm-tracks-list tracks="favourites.tracks"></pfm-tracks-list>
</div>
<div class="span6">
<h2>Albums</h2>
<pfm-albums-list albums="favourites.albums"></pfm-albums-list>
</div>
</div>

View file

@ -8,17 +8,7 @@
</div>
<h2>Comments</h2>
<div class="comments">
<div class="alert alert-info" ng-show="artist.comments.count == 0">
There are no comments yet!
</div>
<form class="pfm-form">
<div class="form-row">
<textarea></textarea>
</div>
<button type="submit" class="btn disabled">Post Comment</button>
</form>
</div>
<pfm-comments type="user" resource="artist"></pfm-comments>
</div>
<div class="span6 cover-image">
<h2>Recent Tracks</h2>

View file

@ -1,4 +1,7 @@
<div>
<h1>Login!</h1>
<h1>Login</h1>
<div class="alert">
Only user accounts that were created as of the launch of the pre-release will be available.
</div>
<ng-include src="'/templates/partials/auth/login.html'" />
</div>

View file

@ -1,20 +1,11 @@
<div class="dashboard">
<section class="recent-tracks">
<section class="recent-tracks stretch-to-bottom">
<div>
<h1>
<a href="#"><i class="icon-music"></i> see more</a>
The Newest Tunes
</h1>
<pfm-tracks-list tracks="recentTracks" />
</div>
</section>
<section class="popular">
<div>
<h1>
<a href="#"><i class="icon-star"></i> see more</a>
What's Popular Today
</h1>
<pfm-tracks-list tracks="recentTracks" />
<pfm-tracks-list tracks="recentTracks" class="two-column"></pfm-tracks-list>
</div>
</section>
</div>

View file

@ -0,0 +1,21 @@
<div class="comments">
<div class="alert alert-info" ng-show="resource.comments.count == 0">
There are no comments yet!
</div>
<ul ng-show="resource.comments.count > 0">
<li ng-repeat="comment in resource.comments.list">
<div class="meta">
<a href="{{comment.user.url}}">{{comment.user.name}}</a> posted
{{comment.created_at.date | momentFromNow}}
</div>
<img ng-src="{{comment.user.avatars.thumbnail}}" />
<div class="content">{{comment.content}}</div>
</li>
</ul>
<form class="pfm-form" ng-submit="addComment()" ng-show="auth.isLogged">
<div class="form-row">
<textarea ng-model="content"></textarea>
</div>
<button type="submit" class="btn" ng-class="{disabled: content.length == 0}">Post Comment</button>
</form>
</div>

View file

@ -0,0 +1,10 @@
<a href="#" class="btn btn-small favourite-button" ng-class="{'is-favourited': resource.is_favourited, disabled: isWorking}" pfm-eat-click ng-click="toggleFavourite()" ng-show="auth.isLogged">
<span ng-hide="resource.is_favourited">
Favourite This!
<i class="icon-star-empty"></i>
</span>
<span ng-show="resource.is_favourited">
In Your Favourites
<i class="icon-star"></i>
</span>
</a>

View file

@ -0,0 +1,31 @@
<div class="current-track" ng-show="player.isPlaying">
<div class="image">
<img ng-src="{{player.currentTrack.covers.thumbnail}}" />
</div>
<div class="transport" ng-click="seek($event)">
<div class="bar" pfm-progress-bar="player.currentTrack.progress"></div>
<div class="loader-bar" pfm-progress-bar="player.currentTrack.loadingProgress"></div>
</div>
<ul class="buttons">
<li><a pfm-eat-click ng-click="playPrev()" class="previous" href="#"><i class="icon-fast-backward"></i></a></li>
<li>
<a pfm-eat-click ng-click="playPause()" class="play" href="#">
<i class="icon-pause" ng-show="player.currentTrack.isPlaying"></i>
<i class="icon-play" ng-hide="player.currentTrack.isPlaying"></i>
</a>
</li>
<li><a pfm-eat-click ng-click="playNext()" class="next" href="#"><i class="icon-fast-forward"></i></a></li>
<li>
<a pfm-eat-click ng-click="" class="volume" href="#">
<i class="icon-volume-up"></i>
</a>
<div class="volume-slider">
<div class="bar"><a href="#" class="knob">&nbsp;</a></div>
</div>
</li>
</ul>
<div class="title">
<span class="song"><a href="{{player.currentTrack.url}}">{{player.currentTrack.title}}</a></span>
<span class="artist"><a href="{{player.currentTrack.user.url}}">{{player.currentTrack.user.name}}</a></span>
</div>
</div>

View file

@ -1,12 +1,15 @@
<ul class="tracks-listing {{class}}">
<li ng-repeat="track in tracks">
<li ng-repeat="track in tracks" ng-class="{'can-favourite': auth.isLogged, 'is-favourited': track.is_favourited, 'is-playing': track.isPlaying}">
<div class="image">
<a href="#" class="play-button"><i class="icon-play"></i></a>
<a href="#" class="play-button" pfm-eat-click ng-click="play(track)">
<i class="icon-play" ng-show="!track.isPlaying"></i>
<i class="icon-pause" ng-hide="!track.isPlaying"></i>
</a>
<img ng-src="{{track.covers.thumbnail}}" />
</div>
<div class="icons">
<span><i ng-class="{'icon-microphone-off': !track.is_vocal, 'icon-microphone': track.is_vocal}"></i></span>
<a href="#"><i class="icon-star-empty"></i></a>
<a pfm-eat-click class="icon-favourite" href="#" ng-click="toggleFavourite(track)" ng-show="auth.isLogged"><i class="icon-star-empty"></i></a>
</div>
<a class="info" href="{{track.url}}">
<span class="title">{{track.title}}</span>
@ -17,4 +20,7 @@
</span>
</a>
</li>
<li ng-show="!tracks.length" class="empty">
No tracks found...
</li>
</ul>

View file

@ -1,13 +1,37 @@
<div>
<h1>Look, it's a website!</h1>
<h1>Pony.fm BETA 2 Preview</h1>
<p>
This page should basically be a big advert for PFM. But that comes later. For now, you may do two things:
Welcome to Pony.fm's BETA 2 preview! If you were looking for the functional website, head on over to <a href="http://pony.fm" target="_blank">Pony.fm</a>.
</p>
<div class="row">
<div class="span6 panel">
<h2>Login</h2>
<ng-include src="'/templates/partials/auth/login.html'" ng-controller="login" />
</div>
</div>
<p>
This is an early look at the next Pony.fm release. <a href="http://pony.fm" target="_blank">Pony.fm</a> is a music hosting website
for Bronies - and you can learn more about it <a href="http://pony.fm/about" target="_blank">here</a>.
</p>
<p>
This web application is under <strong>heavy development</strong>. While you are free to browse around, please note
that although the track and user data on this site is a copy of Pony.fm - all data will be <strong>erased</strong> when
the preview ends, or whenever we feel like. As such, please do not use this application for hosting your new music.
</p>
<p>
At this time, many features are notably missing - such as:
</p>
<ul>
<li>Mobile friendly design</li>
<li>Artist following</li>
<li>Notifications</li>
<li>Public playlists</li>
<li>Music Downloads</li>
</ul>
<p>
And more. The layout and design is also subject to change in the future.
</p>
<p>
However, we welcome any and all feedback - so if you have a suggestion or bug report, you can hop on over to
<a href="http://mlpforums.com/forum/62-ponyfm/" target="_blank">Pony.fm's Forum</a>.
</p>
<p>We hope you enjoy taking a look at the future of Pony.fm as much as we did building it!</p>
</div>

View file

@ -1,11 +1,37 @@
<div>
<h1>About!</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Assumenda id, illum in incidunt nemo nisi perspiciatis
quibusdam quos repellat veniam? Accusamus animi ducimus id iure repellendus tempore veniam voluptates
voluptatibus!</p>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aut beatae commodi corporis doloremque dolorum ex facilis
impedit perferendis praesentium quasi quis quisquam repellat sint temporibus, totam. Excepturi magni tempora
voluptates.</p>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Assumenda atque corporis debitis doloremque est expedita
illo ipsam itaque maiores modi nam placeat qui sit suscipit, totam unde vel veniam. Temporibus?</p>
<h1>What exactly <em>is</em> Pony.fm, anyway?</h1>
<div class="stretch-to-bottom">
<div class="row-fluid">
<div class="span12 columns">
<p>Some <em>My Little Pony: Friendship is Magic</em> fans - typically referred to as &quot;bronies&quot; are the musical type, and show their appreciation for the show by pouring their talent into <strong>fan music</strong>.
<p>The brony fan music community is diverse, spanning genres from symphonic metal to trance and everything in between. But most importantly, the community creates music.</p>
<p>A <em>lot</em> of music.</p>
<p>All this music has to go somewhere. YouTube, SoundCloud, and Bandcamp are popular outlets that many brony musicians use to host their tunes. But no mainstream sites are specifically designed for our fandom&#39;s needs, and they&#39;re not particularly helpful if, as a listener, you&#39;re looking for pony fan music.</p>
<p>That&#39;s where Pony.fm comes in. <strong>Pony.fm is a community, hosting service, and music database rolled into one, with a generous dash of pony on top.</strong></p>
</div>
</div>
<div class="row-fluid">
<div class="span12">
<h2>So it&#39;s SoundCloud with ponies?</h2>
<p>Eenope!</p>
<p>Pony.fm is an original project. Although it takes inspiration from a number of well-known services for the general public, Pony.fm is not specifically modeled after any one of them. As a fan site itself, Pony.fm is an experience all its own.</p>
<p>Simply put, <strong>&quot;Pony.fm is Pony.fm.&quot;</strong></p>
<h2>What makes Pony.fm special?</h2>
<p>Pony.fm is a service created by a brony, for bronies. Every inch of the Pony.fm experience is crafted with ponies and bronies in mind. Some of the features necessarily resemble what you may find on other sites - lossless uploads, for example - but some features are specific to the pony fan music community.</p>
<p>Created as a service for the fandom, Pony.fm aims to be the one-stop shop for all things pony music, for artists and listeners alike.</p>
</div>
</div>
<div class="row-fluid">
<div class="span12">
<h2>What does MLP Forums have to do with Pony.fm?</h2>
<p>MLP Forums and Pony.fm share an owner, and each encompasses a different segment of the global <em>My Little Pony: Friendship is Magic</em> community. Put together, both sites are able to offer a richer &quot;supercommunity&quot; experience than either site could offer on its own.</p>
<h2>Who is behind Pony.fm?</h2>
<p>Pony.fm is built and owned by Feld0, a pony-loving teenager who heard the call of code. Recognizing the need for a true pony-specific music hosting site, and realizing that MLP Forums provided him with the resources he needed to make it happen, he created a blank text file and started pumping code into his computer.</p>
</div>
</div>
</div>
</div>

View file

@ -1,23 +1,77 @@
<div>
<h1>FAQ!</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ab asperiores autem beatae blanditiis earum enim illo nisi
nulla numquam, odio saepe sed sunt tenetur! Ab aperiam enim error harum soluta!</p>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aliquid animi blanditiis corporis debitis dolore dolorum
expedita facere facilis fuga ipsa labore libero non odit praesentium quidem quo, repudiandae veniam voluptatem!</p>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Corporis doloribus hic ipsam, laudantium minima
necessitatibus, officia perferendis provident ratione sunt unde vitae. Consectetur, ducimus harum maiores molestias
possimus ratione vero!</p>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ab at beatae, blanditiis culpa dicta dolor ducimus ex harum
ipsam iure laboriosam, mollitia nesciunt obcaecati pariatur quibusdam quod recusandae, sapiente veritatis!</p>
<h1>Pony.fm FAQ</h1>
<div class="stretch-to-bottom">
<ul class="faq-tos">
<li><a href="#mp3">Why doesn't Pony.fm support MP3 files?</a></li>
<li><a href="#not-accepted">Why isn't my file being accepted for Upload?</a></li>
<li><a href="#upload">How do I Upload a song?</a></li>
<li><a href="#avatar">How do you set an avatar?</a></li>
<li><a href="#mlp-forums">Why the connection to MLP Forums?</a></li>
<li><a href="#feedback">How do I send Feedback to the Developers?</a></li>
<li><a href="#poniverse">What is the "Poniverse" and what does Pony FM have to do with it?</a></li>
<li><a href="#stats">Can I view any site statistics?</a></li>
<li><a href="#contact">How do I get in contact with other Musicians on Pony.fm?</a></li>
<li><a href="#report">How do I report someone?</a></li>
<li><a href="#download">How do I download an artist' song?</a></li>
</ul>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Blanditiis dignissimos esse nihil non odio possimus
ratione sapiente ullam! Assumenda corporis doloremque et ipsum laudantium nihil quae quidem sapiente temporibus,
veritatis!</p>
<a name="mp3"></a>
<h2>Why doesn't Pony.fm support MP3 files?</h2>
<p>MP3 encoding is "lossy." Lossy means that, during the encoding process, quality gets sacrificed for a decrease in size.</p>
<p>Pony.fm does not provide only MP3's; it also provides OGG's and lossless FLAC's. Uploading a lossless file puts a "perfect" copy of your track in Pony.fm's file store, which can be offered up for download on its own for audiophiles who like CD or better-than-CD sound quality, but starting from a lossless original also allows Pony.fm to transcode a song to other lossy formats with only one degree of loss.</p>
<p>Adipisci aperiam cupiditate ea error et eum exercitationem facilis, hic minus molestiae nulla, omnis possimus
repudiandae temporibus voluptatem. Ab accusamus aspernatur assumenda dicta dolorum eos, inventore nam quo
recusandae tenetur.</p>
<p>Pony.fm accepts a lossless upload, which is converted to FLAC (if it isn't already FLAC) for storage. This leaves a "perfect," unblemished copy of the track in Pony.fm's file store.</p>
<p>An MP3 file can be created from the FLAC. Minimal quality is lost because creating an MP3 from the FLAC is as good as creating an MP3 directly from your DAW.</p>
<p>An OGG Vorbis file can be created from the FLAC. Minimal quality is lost because Pony.fm has a lossless copy of the track on file; thus, we don't have the "recompressing a compressed MP3" issue that is present if Pony.fm's "master file" was an MP3.</p>
<p>Accusamus ad alias aliquam architecto blanditiis dolor eos, est eveniet, impedit maxime nam officia quaerat
quibusdam, ratione sed. Aut eum ex illum ipsum, minus nobis nulla quibusdam quidem repellat unde?</p>
<a name="not-accepted"></a>
<h2>Why isn't my file being accepted for Upload?</h2>
<p>Pony.fm analyzes all uploads to determine their format and check it against a whitelist; the file extension is ignored. Unfortunately, slight variations in AIFF and WAV files exist that need to be individually whitelisted.</p>
<p>The alpha should have nailed most of these by now, but if there are some that still are not being accepted, contact an admin with the specific file details and they will try to add them to the White List.</p>
<a name="upload"></a>
<h2>How do I Upload a song?</h2>
<p>At the top right of your screen there should be a button titled "Upload" next to the "send feedback" one. Select the Upload button and a drop down menu will appear, select the first option titled "Track Uploader". You should now be on a screen displaying the uploader. Select the Green button titled "Add Files" and select your song from your computer. The track should now start its download.</p>
<p>Please be aware that Pony.fm doesn't support MP3 uploads.</p>
<a name="avatar"></a>
<h2>How do you set an avatar?</h2>
<p>Avatars in Pony.fm use a free service called Gravatar. To learn more about it, and setup your own Gravatar account, <a href="https://gravatar.com/" target="_blank">click here</a>!</p>
<a name="mlp-forums"></a>
<h2>Why the connection to MLP Forums?</h2>
<p>MLP Forums is one of the web&#39;s largest and most well known <em>My Little Pony: Friendship is Magic</em> forum communities. Formally linking the two sites together paves the way for a rich, cross-site community experience. Members of one site can easily jump into the other without the hassle of managing yet another account, and content can seamlessly be brought from MLP Forums to Pony.fm and vice versa in meaningful ways.</p>
<a name="feedback"></a>
<h2>How do I send Feedback to the Developers?</h2>
<p>At the top of your page should be a nifty little button to the left of the upload button that says "Send Feedback".</p>
<p>Click this and a form will pop up, just follow the two simple instructions and enter the information needed and click "submit".</p>
<p>All feedback is greatly appreciated on Pony FM and we do our hardest to keep this site functional and to keep all of you happy.</p>
<a name="poniverse"></a>
<h2>What is the "Poniverse" and what does Pony FM have to do with it?</h2>
<p>The Poniverse is a network that links together several Brony sites ,such as MLP Forums, together to form a super community of sorts that provides sites that satisfy everyone's needs.</p>
<p>Pony FM is just one of those sites and sets out to provide Brony Musicians with their own special corner to share their work with others and to receive feedback from other musicians, and in lots of cases to form collaborations that can end up in great partnerships.</p>
<a name="stats"></a>
<h2>Can I view any site statistics?</h2>
<p>You sure can!</p>
<p>At the bottom left of your screen there is a small button that says Site Stats that's nestled just below a button for Pony.fm Forum.</p>
<p>Click on the "Site Stats" button and you will be taken to a screen that shows you graphs depicting the number of Track Views, Track Downloads, Track Plays and User Registrations.</p>
<a name="contact"></a>
<h2>How do I get in contact with other Musicians on Pony.fm?</h2>
<p>On each user's screen there is a nifty little section where you can leave comments. This is used best for providing feedback and to show them your support, but if you plan on trying to start a collaboration and would prefer a more private means of communication, underneath the user Bio, there is a "send a message" which will utilise the "Personal Messenger" from MLP Forums to allow you to send a message to that artist.</p>
<a name="report"></a>
<h2>How do I report someone?</h2>
<p>At the current time a report feature isn't quite installed into the site, however, email feld0@pony.fm and he would be glad to handle any moderating issues that you have. But to reiterate what was said before, there IS a report function in the works.</p>
<a name="download"></a>
<h2>How do I download an artist' song?</h2>
<p>Click on the song that you are looking to download and you will notice to the right of the screen is a button titled "Downloads".</p>
<p>Click this button and you will be brought a drop down menu with FLAC, MP3, OGG, AAC and ALAC file types for you to download.</p>
<p>Select your preferred file type to start the download and it should all be smooth sailing from there.</p>
</div>
</div>
</div>

View file

@ -1,3 +1,44 @@
<div>
<h1>{{playlist.title}}</h1>
<div class="playlist-details">
<ul class="breadcrumb">
<li><a href="/account/playlists">Your Playlists</a> <span class="divider">/</span></li>
<li class="active">{{playlist.title}}</li>
</ul>
<div class="track-toolbar btn-group pull-right">
<div class="dropdown">
<a href="#" class="btn btn-small btn-info dropdown-toggle">
Downloads <i class="caret"></i>
</a>
<ul class="dropdown-menu">
<li ng-repeat="format in playlist.formats"><a href="{{format.url}}">{{format.name}}</a></li>
</ul>
</div>
</div>
<h1>
{{playlist.title}}
</h1>
<div class="stretch-to-bottom">
<div class="row-fluid">
<div class="span9">
<div class="description">
<p ng-bind-html-unsafe="playlist.description | noHTML | newlines"></p>
</div>
<h2>Tracks</h2>
<pfm-tracks-list tracks="playlist.tracks"></pfm-tracks-list>
<h2>Comments</h2>
<pfm-comments type="playlist" resource="playlist"></pfm-comments>
</div>
<div class="span3 cover-image">
<div class="fb-like" data-href="{{playlist.url}}" data-send="false" data-layout="button_count" data-width="20" data-show-faces="false"></div>
<a href="https://twitter.com/share" class="twitter-share-button" data-url="{{playlist.url}}" data-text="{{playlist.title + ' by ' + playlist.user.name + ' on Pony.fm'}}" data-via="ponyfm">Tweet</a>
<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>
<a href="#" class="btn btn-info">Share or Embed</a>
</div>
</div>
</div>
</div>

View file

@ -1,8 +1,10 @@
<div>
<ul class="tabs">
<li ng-class="{active: $state.includes('tracks.search')}"><a href="/tracks">Search</a></li>
<!--
<li ng-class="{active: $state.includes('tracks.popular')}"><a href="/tracks/popular">Popular</a></li>
<li ng-class="{active: $state.includes('tracks.random')}"><a href="/tracks/random">Random</a></li>
-->
</ul>
<ui-view></ui-view>

View file

@ -1,3 +1,3 @@
<div class="stretch-to-bottom">
<pfm-tracks-list tracks="tracks" class="two-column" />
<pfm-tracks-list tracks="tracks" class="two-column"></pfm-tracks-list>
</div>

View file

@ -1,6 +1,6 @@
<ul class="dropdowns">
<li class="dropdown">
<a class="dropdown-toggle btn">
<a class="dropdown-toggle btn" ng-class="{'btn-info': query.filters.trackTypes.selectedArray.length}">
Type: <strong>{{query.filters.trackTypes.title}}</strong>
</a>
<ul class="dropdown-menu">
@ -11,7 +11,7 @@
</ul>
</li>
<li class="dropdown">
<a class="dropdown-toggle btn">
<a class="dropdown-toggle btn" ng-class="{'btn-info': query.filters.showSongs.selectedArray.length}">
Show Songs: <strong>{{query.filters.showSongs.title}}</strong>
</a>
<ul class="dropdown-menu">
@ -22,7 +22,7 @@
</ul>
</li>
<li class="dropdown">
<a class="dropdown-toggle btn">
<a class="dropdown-toggle btn" ng-class="{'btn-info': query.filters.genres.selectedArray.length}">
Genre: <strong>{{query.filters.genres.title}}</strong>
</a>
<ul class="dropdown-menu">
@ -33,7 +33,7 @@
</ul>
</li>
<li class="dropdown">
<a class="dropdown-toggle btn">
<a class="dropdown-toggle btn" ng-class="{'btn-info': !query.filters.isVocal.isDefault}">
Is Vocal: <strong>{{query.filters.isVocal.title}}</strong>
</a>
<ul class="dropdown-menu">
@ -43,7 +43,7 @@
</ul>
</li>
<li class="dropdown">
<a class="dropdown-toggle btn">
<a class="dropdown-toggle btn" ng-class="{'btn-info': !query.filters.sort.isDefault}">
Order: <strong>{{query.filters.sort.title}}</strong>
</a>
<ul class="dropdown-menu">

View file

@ -1,4 +1,4 @@
<div class="track-details">
<div class="track-details" xmlns="http://www.w3.org/1999/html">
<ul class="breadcrumb">
<li><a href="/tracks">Tracks</a> <span class="divider">/</span></li>
<li><a href="/tracks?filter=genres-{{track.genre.id}}">{{track.genre.name}}</a> <span class="divider">/</span></li>
@ -6,10 +6,7 @@
</ul>
<div class="track-toolbar btn-group pull-right">
<a href="#" class="btn btn-small">
Favourite This!
<i class="icon-star-empty"></i>
</a>
<pfm-favourite-button resource="track" type="track"></pfm-favourite-button>
<div class="dropdown">
<a href="#" class="btn btn-small btn-info dropdown-toggle">
Downloads <i class="caret"></i>
@ -19,16 +16,22 @@
</ul>
</div>
<div class="dropdown">
<a href="#" class="btn btn-small dropdown-toggle">
<a href="#" class="btn btn-small dropdown-toggle" ng-show="auth.isLogged">
Add to Playlist <i class="caret"></i>
</a>
<ul class="dropdown-menu">
<li ng-repeat="playlist in playlists"><a href="{{playlist.url}}">{{playlist.title}}</a></li>
<li><a href="#" class="btn-primary">Add to New Playlist</a></li>
<li ng-repeat="playlist in playlists">
<a ng-class="{disabled: playlist.message, 'btn-success': playlist.message}" href="{{playlist.url}}" pfm-eat-click ng-click="addToPlaylist(playlist); $event.stopPropagation()">
<span ng-hide="playlist.message">{{playlist.title}}</span>
<span ng-show="playlist.message">{{playlist.message}}</span>
</a>
</li>
<li><a href="#" class="btn-primary" pfm-eat-click ng-click="addToNewPlaylist()">Add to New Playlist</a></li>
</ul>
</div>
</div>
<pfm-tracks-list tracks="trackArray"></pfm-tracks-list>
<h1>
{{track.title}}
<span class="subtitle">
@ -58,17 +61,7 @@
</div>
<h2>Comments</h2>
<div class="comments">
<div class="alert alert-info" ng-show="track.comments.count == 0">
There are no comments yet!
</div>
<form class="pfm-form">
<div class="form-row">
<textarea></textarea>
</div>
<button type="submit" class="btn disabled">Post Comment</button>
</form>
</div>
<pfm-comments type="track" resource="track"></pfm-comments>
</div>
<div class="span4 cover-image">
<img ng-src="{{track.covers.normal}}" />

2
vendor/autoload.php vendored
View file

@ -4,4 +4,4 @@
require_once __DIR__ . '/composer' . '/autoload_real.php';
return ComposerAutoloaderInit00affa5bd26689f95e075708487b8b91::getLoader();
return ComposerAutoloaderInit99e3f8067509f9d0b122eb51fad39842::getLoader();

View file

@ -10,8 +10,12 @@ return array(
'AccountController' => $baseDir . '/app/controllers/AccountController.php',
'AlbumsController' => $baseDir . '/app/controllers/AlbumsController.php',
'ApiControllerBase' => $baseDir . '/app/controllers/ApiControllerBase.php',
'Api\\Web\\AccountController' => $baseDir . '/app/controllers/Api/Web/AccountController.php',
'Api\\Web\\AlbumsController' => $baseDir . '/app/controllers/Api/Web/AlbumsController.php',
'Api\\Web\\ArtistsController' => $baseDir . '/app/controllers/Api/Web/ArtistsController.php',
'Api\\Web\\AuthController' => $baseDir . '/app/controllers/Api/Web/AuthController.php',
'Api\\Web\\DashboardController' => $baseDir . '/app/controllers/Api/Web/DashboardController.php',
'Api\\Web\\FavouritesController' => $baseDir . '/app/controllers/Api/Web/FavouritesController.php',
'Api\\Web\\ImagesController' => $baseDir . '/app/controllers/Api/Web/ImagesController.php',
'Api\\Web\\PlaylistsController' => $baseDir . '/app/controllers/Api/Web/PlaylistsController.php',
'Api\\Web\\TaxonomiesController' => $baseDir . '/app/controllers/Api/Web/TaxonomiesController.php',
@ -126,16 +130,24 @@ return array(
'ClassPreloader\\Parser\\DirVisitor' => $vendorDir . '/classpreloader/classpreloader/src/ClassPreloader/Parser/DirVisitor.php',
'ClassPreloader\\Parser\\FileVisitor' => $vendorDir . '/classpreloader/classpreloader/src/ClassPreloader/Parser/FileVisitor.php',
'ClassPreloader\\Parser\\NodeTraverser' => $vendorDir . '/classpreloader/classpreloader/src/ClassPreloader/Parser/NodeTraverser.php',
'Commands\\AddTrackToPlaylistCommand' => $baseDir . '/app/models/Commands/AddTrackToPlaylistCommand.php',
'Commands\\CommandBase' => $baseDir . '/app/models/Commands/CommandBase.php',
'Commands\\CommandResponse' => $baseDir . '/app/models/Commands/CommandResponse.php',
'Commands\\CreateAlbumCommand' => $baseDir . '/app/models/Commands/CreateAlbumCommand.php',
'Commands\\CreatePlaylistCommand' => $baseDir . '/app/models/Commands/CreatePlaylistCommand.php',
'Commands\\DeleteAlbumCommand' => $baseDir . '/app/models/Commands/DeleteAlbumCommand.php',
'Commands\\DeletePlaylistCommand' => $baseDir . '/app/models/Commands/DeletePlaylistCommand.php',
'Commands\\DeleteTrackCommand' => $baseDir . '/app/models/Commands/DeleteTrackCommand.php',
'Commands\\EditAlbumCommand' => $baseDir . '/app/models/Commands/EditAlbumCommand.php',
'Commands\\EditPlaylistCommand' => $baseDir . '/app/models/Commands/EditPlaylistCommand.php',
'Commands\\EditTrackCommand' => $baseDir . '/app/models/Commands/EditTrackCommand.php',
'Commands\\SaveAccountSettingsCommand' => $baseDir . '/app/models/Commands/SaveAccountSettingsCommand.php',
'Commands\\ToggleFavouriteCommand' => $baseDir . '/app/models/Commands/ToggleFavouriteCommand.php',
'Commands\\UploadTrackCommand' => $baseDir . '/app/models/Commands/UploadTrackCommand.php',
'ContentController' => $baseDir . '/app/controllers/ContentController.php',
'CreateAlbums' => $baseDir . '/app/database/migrations/2013_07_28_060804_create_albums.php',
'CreateComments' => $baseDir . '/app/database/migrations/2013_08_01_051337_create_comments.php',
'CreateFavourites' => $baseDir . '/app/database/migrations/2013_08_01_024827_create_favourites.php',
'CreateImagesTable' => $baseDir . '/app/database/migrations/2013_07_26_230827_create_images_table.php',
'CreatePlaylists' => $baseDir . '/app/database/migrations/2013_07_28_135136_create_playlists.php',
'CreateSongsTable' => $baseDir . '/app/database/migrations/2013_07_28_034328_create_songs_table.php',
@ -408,9 +420,12 @@ return array(
'Doctrine\\DBAL\\Types\\VarDateTimeType' => $vendorDir . '/doctrine/dbal/lib/Doctrine/DBAL/Types/VarDateTimeType.php',
'Doctrine\\DBAL\\Version' => $vendorDir . '/doctrine/dbal/lib/Doctrine/DBAL/Version.php',
'Entities\\Album' => $baseDir . '/app/models/Entities/Album.php',
'Entities\\Favourite' => $baseDir . '/app/models/Entities/Favourite.php',
'Entities\\Genre' => $baseDir . '/app/models/Entities/Genre.php',
'Entities\\Image' => $baseDir . '/app/models/Entities/Image.php',
'Entities\\License' => $baseDir . '/app/models/Entities/License.php',
'Entities\\PinnedPlaylist' => $baseDir . '/app/models/Entities/PinnedPlaylist.php',
'Entities\\Playlist' => $baseDir . '/app/models/Entities/Playlist.php',
'Entities\\ShowSong' => $baseDir . '/app/models/Entities/ShowSong.php',
'Entities\\Track' => $baseDir . '/app/models/Entities/Track.php',
'Entities\\TrackType' => $baseDir . '/app/models/Entities/TrackType.php',
@ -1834,6 +1849,7 @@ return array(
'Symfony\\Component\\Translation\\Writer\\TranslationWriter' => $vendorDir . '/symfony/translation/Symfony/Component/Translation/Writer/TranslationWriter.php',
'TestCase' => $baseDir . '/app/tests/TestCase.php',
'TracksController' => $baseDir . '/app/controllers/TracksController.php',
'Traits\\SlugTrait' => $baseDir . '/app/models/Traits/SlugTrait.php',
'UsersController' => $baseDir . '/app/controllers/UsersController.php',
'Whoops\\Exception\\ErrorException' => $vendorDir . '/filp/whoops/src/Whoops/Exception/ErrorException.php',
'Whoops\\Exception\\Frame' => $vendorDir . '/filp/whoops/src/Whoops/Exception/Frame.php',

View file

@ -2,7 +2,7 @@
// autoload_real.php generated by Composer
class ComposerAutoloaderInit00affa5bd26689f95e075708487b8b91
class ComposerAutoloaderInit99e3f8067509f9d0b122eb51fad39842
{
private static $loader;
@ -19,9 +19,9 @@ class ComposerAutoloaderInit00affa5bd26689f95e075708487b8b91
return self::$loader;
}
spl_autoload_register(array('ComposerAutoloaderInit00affa5bd26689f95e075708487b8b91', 'loadClassLoader'), true, true);
spl_autoload_register(array('ComposerAutoloaderInit99e3f8067509f9d0b122eb51fad39842', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
spl_autoload_unregister(array('ComposerAutoloaderInit00affa5bd26689f95e075708487b8b91', 'loadClassLoader'));
spl_autoload_unregister(array('ComposerAutoloaderInit99e3f8067509f9d0b122eb51fad39842', 'loadClassLoader'));
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);