mirror of
https://github.com/Poniverse/Pony.fm.git
synced 2024-11-25 06:27:59 +01:00
Albums and artists
This commit is contained in:
parent
21009713f3
commit
70dccafa9c
32 changed files with 860 additions and 28 deletions
|
@ -1,7 +1,28 @@
|
|||
<?php
|
||||
|
||||
use Entities\Album;
|
||||
|
||||
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]);
|
||||
}
|
||||
}
|
|
@ -28,6 +28,93 @@
|
|||
return $this->execute(new DeleteAlbumCommand($id));
|
||||
}
|
||||
|
||||
public function getShow($id) {
|
||||
$album = Album::with('tracks', 'user')->find($id);
|
||||
if (!$album)
|
||||
App::abort(404);
|
||||
|
||||
$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)
|
||||
];
|
||||
}
|
||||
|
||||
return Response::json([
|
||||
'album' => [
|
||||
'id' => $album->id,
|
||||
'formats' => $formats,
|
||||
'track_count' => $album->tracks->count(),
|
||||
'title' => $album->title,
|
||||
'description' => $album->description,
|
||||
'slug' => $album->slug,
|
||||
'created_at' => $album->created_at,
|
||||
'covers' => [
|
||||
'small' => $album->getCoverUrl(Image::SMALL),
|
||||
'normal' => $album->getCoverUrl(Image::NORMAL)
|
||||
],
|
||||
'url' => $album->url,
|
||||
'user' => [
|
||||
'id' => $album->user->id,
|
||||
'name' => $album->user->display_name,
|
||||
'url' => $album->user->url,
|
||||
],
|
||||
'tracks' => $tracks,
|
||||
'stats' => [
|
||||
'views' => 0,
|
||||
'downloads' => 0
|
||||
],
|
||||
'comments' => ['count' => 0, 'list' => []]
|
||||
]
|
||||
], 200);
|
||||
}
|
||||
|
||||
public function getIndex() {
|
||||
$page = 1;
|
||||
if (Input::has('page'))
|
||||
$page = Input::get('page');
|
||||
|
||||
$query = Album::summary()
|
||||
->with('tracks', 'user')
|
||||
->orderBy('created_at', 'desc')
|
||||
->whereRaw('(SELECT COUNT(id) FROM tracks WHERE tracks.album_id = albums.id) > 0');
|
||||
|
||||
$count = $query->count();
|
||||
$perPage = 15;
|
||||
|
||||
$query->skip(($page - 1) * $perPage)->take($perPage);
|
||||
$albums = [];
|
||||
|
||||
foreach ($query->get() as $album) {
|
||||
$albums[] = [
|
||||
'id' => $album->id,
|
||||
'track_count' => $album->tracks->count(),
|
||||
'title' => $album->title,
|
||||
'slug' => $album->slug,
|
||||
'created_at' => $album->created_at,
|
||||
'covers' => [
|
||||
'small' => $album->getCoverUrl(Image::SMALL),
|
||||
'normal' => $album->getCoverUrl(Image::NORMAL)
|
||||
],
|
||||
'url' => $album->url,
|
||||
'user' => [
|
||||
'id' => $album->user->id,
|
||||
'name' => $album->user->display_name,
|
||||
'url' => $album->user->url,
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
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 = [];
|
||||
|
|
69
app/controllers/Api/Web/ArtistsController.php
Normal file
69
app/controllers/Api/Web/ArtistsController.php
Normal file
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
|
||||
namespace Api\Web;
|
||||
|
||||
use Commands\CreateAlbumCommand;
|
||||
use Commands\DeleteAlbumCommand;
|
||||
use Commands\DeleteTrackCommand;
|
||||
use Commands\EditAlbumCommand;
|
||||
use Commands\EditTrackCommand;
|
||||
use Cover;
|
||||
use Entities\Album;
|
||||
use Entities\Image;
|
||||
use Entities\Track;
|
||||
use Entities\User;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Input;
|
||||
use Illuminate\Support\Facades\Response;
|
||||
|
||||
class ArtistsController extends \ApiControllerBase {
|
||||
public function getShow($slug) {
|
||||
$user = User::whereSlug($slug)->first();
|
||||
if (!$user)
|
||||
App::abort(404);
|
||||
|
||||
return Response::json([
|
||||
'artist' => [
|
||||
'id' => $user->id,
|
||||
'name' => $user->display_name,
|
||||
'slug' => $user->slug,
|
||||
'avatars' => [
|
||||
'small' => $user->getAvatarUrl(Image::SMALL),
|
||||
'normal' => $user->getAvatarUrl(Image::NORMAL)
|
||||
],
|
||||
'created_at' => $user->created_at
|
||||
]
|
||||
], 200);
|
||||
}
|
||||
|
||||
public function getIndex() {
|
||||
$page = 1;
|
||||
if (Input::has('page'))
|
||||
$page = Input::get('page');
|
||||
|
||||
$query = User::orderBy('created_at', 'desc')
|
||||
->whereRaw('(SELECT COUNT(id) FROM tracks WHERE tracks.user_id = users.id) > 0');
|
||||
|
||||
$count = $query->count();
|
||||
$perPage = 15;
|
||||
|
||||
$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,
|
||||
'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);
|
||||
}
|
||||
}
|
|
@ -1,7 +1,25 @@
|
|||
<?php
|
||||
|
||||
use Entities\User;
|
||||
|
||||
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]);
|
||||
}
|
||||
}
|
|
@ -8,4 +8,19 @@
|
|||
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];
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@
|
|||
namespace Entities;
|
||||
|
||||
use Cover;
|
||||
use Illuminate\Support\Facades\URL;
|
||||
use Whoops\Example\Exception;
|
||||
use Traits\SlugTrait;
|
||||
|
||||
|
@ -33,6 +34,14 @@
|
|||
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 getCoverUrl($type = Image::NORMAL) {
|
||||
if (!$this->hasCover())
|
||||
return $this->user->getAvatarUrl($type);
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
use Cover;
|
||||
use External;
|
||||
use getid3_writetags;
|
||||
use Helpers;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\URL;
|
||||
use Illuminate\Support\Str;
|
||||
|
@ -32,6 +33,35 @@
|
|||
$returnValue = self::mapPublicTrackSummary($track);
|
||||
$returnValue['description'] = $track->description;
|
||||
$returnValue['lyrics'] = $track->lyrics;
|
||||
$returnValue['stats'] = [
|
||||
'views' => 0,
|
||||
'plays' => 0,
|
||||
'downloads' => 0
|
||||
];
|
||||
$returnValue['comments'] = ['count' => 0, 'list' => []];
|
||||
|
||||
if ($track->album_id != null) {
|
||||
$returnValue['album'] = [
|
||||
'title' => $track->album->title,
|
||||
'url' => $track->album->url,
|
||||
];
|
||||
}
|
||||
|
||||
$formats = [];
|
||||
|
||||
foreach (self::$Formats as $name => $format) {
|
||||
$file = $track->getFileFor($name);
|
||||
$url = $track->getUrlFor($name);
|
||||
$size = 0;
|
||||
|
||||
if (is_file($file))
|
||||
$size = filesize($file);
|
||||
|
||||
$formats[] = ['name' => $name, 'extension' => $format['extension'], 'url' => $url, 'size' => Helpers::formatBytes($size)];
|
||||
}
|
||||
|
||||
$returnValue['formats'] = $formats;
|
||||
|
||||
return $returnValue;
|
||||
}
|
||||
|
||||
|
@ -204,6 +234,14 @@
|
|||
return "{$this->getDirectory()}/{$this->id}.{$format['extension']}";
|
||||
}
|
||||
|
||||
public function getUrlFor($format) {
|
||||
if (!isset(self::$Formats[$format]))
|
||||
throw new Exception("$format is not a valid format!");
|
||||
|
||||
$format = self::$Formats[$format];
|
||||
return URL::to('/t' . $this->id . '/dl.' . $format['extension']);
|
||||
}
|
||||
|
||||
public function updateTags() {
|
||||
foreach (self::$Formats as $format => $data) {
|
||||
$this->{$data['tag_method']}($format);
|
||||
|
|
|
@ -18,10 +18,14 @@
|
|||
|
||||
Route::get('tracks/{id}-{slug}', 'TracksController@getTrack');
|
||||
Route::get('t{id}', 'TracksController@getShortlink' );
|
||||
Route::get('t{id}/dl.{extension}', 'TracksController@getDownload' );
|
||||
|
||||
Route::get('/albums', 'AlbumsController@getIndex');
|
||||
Route::get('/artists', 'ArtistsController@getIndex');
|
||||
Route::get('/playlists', 'PlaylistsController@getIndex');
|
||||
Route::get('albums', 'AlbumsController@getIndex');
|
||||
Route::get('albums/{id}-{slug}', 'AlbumsController@getShow');
|
||||
Route::get('a{id}', 'AlbumsController@getShortlink')->where('id', '\d+');
|
||||
|
||||
Route::get('artists', 'ArtistsController@getIndex');
|
||||
Route::get('playlists', 'PlaylistsController@getIndex');
|
||||
|
||||
Route::get('/login', function() { return View::make('auth.login'); });
|
||||
Route::get('/register', function() { return View::make('auth.register'); });
|
||||
|
@ -29,12 +33,12 @@
|
|||
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');
|
||||
Route::get('i{id}/{type}.png', 'ImagesController@getImage')->where('id', '\d+');
|
||||
|
||||
Route::get('u{id}/avatar_{type}.png', 'UsersController@getAvatar');
|
||||
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');
|
||||
Route::get('p{id}', 'PlaylistsController@getShortlink')->where('id', '\d+');
|
||||
|
||||
Route::group(['prefix' => 'api/web'], function() {
|
||||
Route::get('/taxonomies/all', 'Api\Web\TaxonomiesController@getAll');
|
||||
|
@ -45,6 +49,12 @@
|
|||
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('/artists', 'Api\Web\ArtistsController@getIndex');
|
||||
Route::get('/artists/{slug}', 'Api\Web\ArtistsController@getShow')->where('id', '[-\w]');
|
||||
|
||||
Route::get('/dashboard', 'Api\Web\DashboardController@getIndex');
|
||||
|
||||
Route::group(['before' => 'auth|csrf'], function() {
|
||||
|
@ -101,4 +111,8 @@
|
|||
});
|
||||
});
|
||||
|
||||
Route::get('u{id}', 'ArtistsController@getShortlink')->where('id', '\d+');
|
||||
Route::get('users/{id}-{slug}', 'ArtistsController@getShortlink')->where('id', '\d+');
|
||||
Route::get('{slug}', 'ArtistsController@getProfile')->where('id', '[-\w]');
|
||||
|
||||
Route::get('/', 'HomeController@getIndex');
|
6
app/views/albums/show.blade.php
Normal file
6
app/views/albums/show.blade.php
Normal file
|
@ -0,0 +1,6 @@
|
|||
@extends('shared._app_layout')
|
||||
|
||||
@section('app_content')
|
||||
<h1>Album!</h1>
|
||||
<p>This page should be what search engines see</p>
|
||||
@endsection
|
6
app/views/artists/profile.blade.php
Normal file
6
app/views/artists/profile.blade.php
Normal file
|
@ -0,0 +1,6 @@
|
|||
@extends('shared._app_layout')
|
||||
|
||||
@section('app_content')
|
||||
<h1>Artist Profile!</h1>
|
||||
<p>This page should be what search engines see</p>
|
||||
@endsection
|
|
@ -36,9 +36,8 @@
|
|||
@endif
|
||||
<li><h3>Discover</h3></li>
|
||||
<li ng-class="{selected: $state.includes('tracks') || $state.includes('track')}"><a href="/tracks">Music <i class="icon-music"></i></a></li>
|
||||
<li ng-class="{selected: $state.includes('albums')}"><a href="/albums">Albums <i class="icon-music"></i></a></li>
|
||||
<li ng-class="{selected: $state.includes('playlists')}"><a href="/playlists">Playlists <i class="icon-music"></i></a></li>
|
||||
<li ng-class="{selected: $state.includes('artists')}"><a href="/artists">Artists <i class="icon-user"></i></a></li>
|
||||
<li ng-class="{selected: $state.includes('albums') || $state.includes('album')}"><a href="/albums">Albums <i class="icon-th-list"></i></a></li>
|
||||
<li ng-class="{selected: $state.includes('artists') || $state.includes('artist')}"><a href="/artists">Artists <i class="icon-user"></i></a></li>
|
||||
|
||||
@if (Auth::check())
|
||||
<li>
|
||||
|
@ -102,7 +101,17 @@
|
|||
|
||||
@section('scripts')
|
||||
|
||||
<div id="fb-root"></div>
|
||||
|
||||
<script>
|
||||
(function(d, s, id) {
|
||||
var js, fjs = d.getElementsByTagName(s)[0];
|
||||
if (d.getElementById(id)) return;
|
||||
js = d.createElement(s); js.id = id;
|
||||
js.src = "//connect.facebook.net/en_US/all.js#xfbml=1&appId=186765381447538";
|
||||
fjs.parentNode.insertBefore(js, fjs);
|
||||
}(document, 'script', 'facebook-jssdk'));
|
||||
|
||||
window.pfm = {
|
||||
token: "{{Session::token()}}",
|
||||
auth: {
|
||||
|
|
|
@ -101,6 +101,18 @@ angular.module 'ponyfm', ['ui.bootstrap', 'ui.state', 'ui.date', 'ui.sortable'],
|
|||
state.state 'albums',
|
||||
url: '/albums'
|
||||
templateUrl: '/templates/albums/index.html'
|
||||
controller: 'albums'
|
||||
abstract: true
|
||||
|
||||
state.state 'albums.list',
|
||||
url: '?page'
|
||||
templateUrl: '/templates/albums/list.html'
|
||||
controller: 'albums-list'
|
||||
|
||||
state.state 'album',
|
||||
url: '/albums/{id:[^\-]+}-{slug}'
|
||||
templateUrl: '/templates/albums/show.html'
|
||||
controller: 'album'
|
||||
|
||||
# Playlists
|
||||
|
||||
|
@ -118,6 +130,13 @@ angular.module 'ponyfm', ['ui.bootstrap', 'ui.state', 'ui.date', 'ui.sortable'],
|
|||
state.state 'artists',
|
||||
url: '/artists'
|
||||
templateUrl: '/templates/artists/index.html'
|
||||
controller: 'artists'
|
||||
abstract: true
|
||||
|
||||
state.state 'artists.list',
|
||||
url: '?page'
|
||||
templateUrl: '/templates/artists/list.html'
|
||||
controller: 'artists-list'
|
||||
|
||||
# Pages
|
||||
|
||||
|
@ -152,6 +171,12 @@ angular.module 'ponyfm', ['ui.bootstrap', 'ui.state', 'ui.date', 'ui.sortable'],
|
|||
url: '/'
|
||||
templateUrl: '/templates/home/index.html'
|
||||
|
||||
# Final catch-all for aritsts
|
||||
state.state 'artist',
|
||||
url: '^/:slug'
|
||||
templateUrl: '/templates/artists/show.html'
|
||||
controller: 'artist'
|
||||
|
||||
route.otherwise '/'
|
||||
|
||||
location.html5Mode(true);
|
||||
|
|
18
public/scripts/app/controllers/album.coffee
Normal file
18
public/scripts/app/controllers/album.coffee
Normal file
|
@ -0,0 +1,18 @@
|
|||
window.pfm.preloaders['album'] = [
|
||||
'albums', '$state', 'playlists'
|
||||
(albums, $state, playlists) ->
|
||||
$.when.all [albums.fetch $state.params.id, playlists.refreshOwned(true)]
|
||||
]
|
||||
|
||||
angular.module('ponyfm').controller "album", [
|
||||
'$scope', 'albums', '$state', 'playlists', 'auth'
|
||||
($scope, albums, $state, playlists, auth) ->
|
||||
albums.fetch($state.params.id).done (albumResponse) ->
|
||||
$scope.album = albumResponse.album
|
||||
|
||||
$scope.playlists = []
|
||||
|
||||
if auth.data.isLogged
|
||||
playlists.refreshOwned().done (lists) ->
|
||||
$scope.playlists.push list for list in lists
|
||||
]
|
12
public/scripts/app/controllers/albums-list.coffee
Normal file
12
public/scripts/app/controllers/albums-list.coffee
Normal file
|
@ -0,0 +1,12 @@
|
|||
window.pfm.preloaders['albums-list'] = [
|
||||
'albums', '$state'
|
||||
(albums, $state) ->
|
||||
albums.fetchList($state.params.page, true)
|
||||
]
|
||||
|
||||
angular.module('ponyfm').controller "albums-list", [
|
||||
'$scope', 'albums', '$state'
|
||||
($scope, albums, $state) ->
|
||||
albums.fetchList($state.params.page).done (list) ->
|
||||
$scope.albums = list.albums
|
||||
]
|
21
public/scripts/app/controllers/albums.coffee
Normal file
21
public/scripts/app/controllers/albums.coffee
Normal file
|
@ -0,0 +1,21 @@
|
|||
angular.module('ponyfm').controller "albums", [
|
||||
'$scope', 'albums', '$state'
|
||||
($scope, albums, $state) ->
|
||||
|
||||
refreshPages = (list) ->
|
||||
$scope.albums = list.albums
|
||||
$scope.currentPage = parseInt(list.current_page)
|
||||
$scope.totalPages = parseInt(list.total_pages)
|
||||
|
||||
delete $scope.nextPage
|
||||
delete $scope.prevPage
|
||||
$scope.nextPage = $scope.currentPage + 1 if $scope.currentPage < $scope.totalPages
|
||||
$scope.prevPage = $scope.currentPage - 1 if $scope.currentPage > 1
|
||||
$scope.pages = [1..$scope.totalPages]
|
||||
|
||||
albums.fetchList($state.params.page).done refreshPages
|
||||
$scope.$on 'albums-feteched', (e, list) -> refreshPages(list)
|
||||
|
||||
$scope.gotoPage = (page) ->
|
||||
$state.transitionTo 'albums.list', {page: page}
|
||||
]
|
12
public/scripts/app/controllers/artist.coffee
Normal file
12
public/scripts/app/controllers/artist.coffee
Normal file
|
@ -0,0 +1,12 @@
|
|||
window.pfm.preloaders['artist'] = [
|
||||
'artists', '$state'
|
||||
(artists, $state) ->
|
||||
artists.fetch $state.params.slug
|
||||
]
|
||||
|
||||
angular.module('ponyfm').controller "artist", [
|
||||
'$scope', 'artists', '$state'
|
||||
($scope, artists, $state) ->
|
||||
artists.fetch($state.params.slug).done (artistResponse) ->
|
||||
$scope.artist = artistResponse.artist
|
||||
]
|
12
public/scripts/app/controllers/artists-list.coffee
Normal file
12
public/scripts/app/controllers/artists-list.coffee
Normal file
|
@ -0,0 +1,12 @@
|
|||
window.pfm.preloaders['artists-list'] = [
|
||||
'artists', '$state'
|
||||
(artists, $state) ->
|
||||
artists.fetchList($state.params.page, true)
|
||||
]
|
||||
|
||||
angular.module('ponyfm').controller "artists-list", [
|
||||
'$scope', 'artists', '$state'
|
||||
($scope, artists, $state) ->
|
||||
artists.fetchList($state.params.page).done (list) ->
|
||||
$scope.artists = list.artists
|
||||
]
|
21
public/scripts/app/controllers/artists.coffee
Normal file
21
public/scripts/app/controllers/artists.coffee
Normal file
|
@ -0,0 +1,21 @@
|
|||
angular.module('ponyfm').controller "artists", [
|
||||
'$scope', 'artists', '$state'
|
||||
($scope, artists, $state) ->
|
||||
|
||||
refreshPages = (list) ->
|
||||
$scope.artists = list.artists
|
||||
$scope.currentPage = parseInt(list.current_page)
|
||||
$scope.totalPages = parseInt(list.total_pages)
|
||||
|
||||
delete $scope.nextPage
|
||||
delete $scope.prevPage
|
||||
$scope.nextPage = $scope.currentPage + 1 if $scope.currentPage < $scope.totalPages
|
||||
$scope.prevPage = $scope.currentPage - 1 if $scope.currentPage > 1
|
||||
$scope.pages = [1..$scope.totalPages]
|
||||
|
||||
artists.fetchList($state.params.page).done refreshPages
|
||||
$scope.$on 'artists-feteched', (e, list) -> refreshPages(list)
|
||||
|
||||
$scope.gotoPage = (page) ->
|
||||
$state.transitionTo 'artists.list', {page: page}
|
||||
]
|
|
@ -1,12 +1,18 @@
|
|||
window.pfm.preloaders['track'] = [
|
||||
'tracks', '$state'
|
||||
(tracks, $state) ->
|
||||
tracks.fetch $state.params.id
|
||||
'tracks', '$state', 'playlists'
|
||||
(tracks, $state, playlists) ->
|
||||
$.when.all [tracks.fetch $state.params.id, playlists.refreshOwned(true)]
|
||||
]
|
||||
|
||||
angular.module('ponyfm').controller "track", [
|
||||
'$scope', 'tracks', '$state'
|
||||
($scope, tracks, $state) ->
|
||||
'$scope', 'tracks', '$state', 'playlists', 'auth'
|
||||
($scope, tracks, $state, playlists, auth) ->
|
||||
tracks.fetch($state.params.id).done (trackResponse) ->
|
||||
$scope.track = trackResponse.track
|
||||
|
||||
$scope.playlists = []
|
||||
|
||||
if auth.data.isLogged
|
||||
playlists.refreshOwned().done (lists) ->
|
||||
$scope.playlists.push list for list in lists
|
||||
]
|
32
public/scripts/app/services/albums.coffee
Normal file
32
public/scripts/app/services/albums.coffee
Normal file
|
@ -0,0 +1,32 @@
|
|||
angular.module('ponyfm').factory('albums', [
|
||||
'$rootScope', '$http'
|
||||
($rootScope, $http) ->
|
||||
albumPages = []
|
||||
albums = {}
|
||||
|
||||
self =
|
||||
filters: {}
|
||||
|
||||
fetchList: (page, force) ->
|
||||
force = force || false
|
||||
page = 1 if !page
|
||||
return albumPages[page] if !force && albumPages[page]
|
||||
albumsDef = new $.Deferred()
|
||||
$http.get('/api/web/albums?page=' + page).success (albums) ->
|
||||
albumsDef.resolve albums
|
||||
$rootScope.$broadcast 'albums-feteched', albums
|
||||
|
||||
albumPages[page] = albumsDef.promise()
|
||||
|
||||
fetch: (id, force) ->
|
||||
force = force || false
|
||||
id = 1 if !id
|
||||
return albums[id] if !force && albums[id]
|
||||
albumsDef = new $.Deferred()
|
||||
$http.get('/api/web/albums/' + id).success (albums) ->
|
||||
albumsDef.resolve albums
|
||||
|
||||
albums[id] = albumsDef.promise()
|
||||
|
||||
self
|
||||
])
|
32
public/scripts/app/services/artists.coffee
Normal file
32
public/scripts/app/services/artists.coffee
Normal file
|
@ -0,0 +1,32 @@
|
|||
angular.module('ponyfm').factory('artists', [
|
||||
'$rootScope', '$http'
|
||||
($rootScope, $http) ->
|
||||
artistPage = []
|
||||
artists = {}
|
||||
|
||||
self =
|
||||
filters: {}
|
||||
|
||||
fetchList: (page, force) ->
|
||||
force = force || false
|
||||
page = 1 if !page
|
||||
return artistPage[page] if !force && artistPage[page]
|
||||
artistsDef = new $.Deferred()
|
||||
$http.get('/api/web/artists?page=' + page).success (albums) ->
|
||||
artistsDef.resolve albums
|
||||
$rootScope.$broadcast 'artists-feteched', albums
|
||||
|
||||
artistPage[page] = artistsDef.promise()
|
||||
|
||||
fetch: (slug, force) ->
|
||||
force = force || false
|
||||
slug = 1 if !slug
|
||||
return artists[slug] if !force && artists[slug]
|
||||
artistsDef = new $.Deferred()
|
||||
$http.get('/api/web/artists/' + slug).success (albums) ->
|
||||
artistsDef.resolve albums
|
||||
|
||||
artists[slug] = artistsDef.promise()
|
||||
|
||||
self
|
||||
])
|
|
@ -11,10 +11,16 @@ window.handleResize = () ->
|
|||
|
||||
$('.revealable').each () ->
|
||||
$this = $ this
|
||||
return if $this.data 'real-height'
|
||||
$this.data 'real-height', $this.height()
|
||||
$this.css
|
||||
height: '15em'
|
||||
|
||||
# if $this.height() > $this.data 'real-height'
|
||||
# $this.css {height: 'auto'}
|
||||
# $this.find('.reveal').css {display: 'none'}
|
||||
# return
|
||||
|
||||
$this.find('.reveal').click (e) ->
|
||||
e.preventDefault()
|
||||
$this.css {height: 'auto'}
|
||||
|
|
97
public/styles/albums.less
Normal file
97
public/styles/albums.less
Normal file
|
@ -0,0 +1,97 @@
|
|||
@import-once 'base/bootstrap/bootstrap';
|
||||
@import-once 'mixins';
|
||||
|
||||
.albums-listing, .artist-listing {
|
||||
overflow-y: auto;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
list-style: none;
|
||||
|
||||
li {
|
||||
.box-sizing(border-box);
|
||||
width: 20%;
|
||||
|
||||
float: left;
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
line-height: normal;
|
||||
|
||||
&.empty {
|
||||
.alert();
|
||||
float: none !important;
|
||||
width: auto !important;
|
||||
display: block;
|
||||
padding: 5px;
|
||||
font-size: 9pt;
|
||||
}
|
||||
|
||||
&.is-not-published a {
|
||||
background: fadeout(@yellow, 90%);
|
||||
}
|
||||
|
||||
&.selected, &.selected:hover {
|
||||
a {
|
||||
#gradient>.vertical(#149bdf, #0480be);
|
||||
cursor: default;
|
||||
color: #fff;
|
||||
|
||||
.published {
|
||||
color: #eee;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
.transition(350px ease-out all);
|
||||
|
||||
display: block;
|
||||
margin: 0px;
|
||||
font-size: 9pt;
|
||||
font-weight: normal;
|
||||
overflow: hidden;
|
||||
padding: 15px;
|
||||
|
||||
.image {
|
||||
}
|
||||
|
||||
.title {
|
||||
margin: 0px;
|
||||
font-size: 12pt;
|
||||
padding: 0px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.published {
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.image {
|
||||
.img-polaroid();
|
||||
float: left;
|
||||
display: block;
|
||||
position: relative;
|
||||
left: -5px;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
float: none;
|
||||
}
|
||||
|
||||
.title {
|
||||
.ellipsis();
|
||||
display: block;
|
||||
}
|
||||
|
||||
.published {
|
||||
display: block;
|
||||
color: #777;
|
||||
font-size: 8pt;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: #ddd;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,4 +7,5 @@
|
|||
@import-once 'components';
|
||||
@import-once 'forms';
|
||||
@import-once 'tracks';
|
||||
@import-once 'albums';
|
||||
@import-once 'animations';
|
|
@ -127,14 +127,60 @@
|
|||
}
|
||||
}
|
||||
|
||||
.track-details {
|
||||
.track-details, .album-details {
|
||||
h1 {
|
||||
.box-shadow(0px 2px 3px rgba(0, 0, 0, .3));
|
||||
background: #eee;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.comments {
|
||||
.alert {
|
||||
.border-radius(0px);
|
||||
margin: 5px 0px;
|
||||
}
|
||||
|
||||
button {
|
||||
.border-radius(0px);
|
||||
}
|
||||
}
|
||||
|
||||
.stretch-to-bottom {
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.cover-image {
|
||||
img {
|
||||
.img-polaroid();
|
||||
.box-sizing(border-box);
|
||||
width: 100%;
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
.border-radius(0px);
|
||||
display: block;
|
||||
width: 'auto';
|
||||
margin: 5px 0px;
|
||||
}
|
||||
|
||||
.stats {
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
list-style: none;
|
||||
|
||||
li {
|
||||
margin: 0px;
|
||||
padding: 5px 0px;
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.lyrics {
|
||||
font-size: 10pt;
|
||||
color: #222;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
padding: 0px;
|
||||
|
@ -178,11 +224,27 @@
|
|||
|
||||
html {
|
||||
.track-toolbar {
|
||||
background: #eee;
|
||||
padding: 5px;
|
||||
|
||||
> .btn:first-child, > .btn:last-child {
|
||||
.btn:first-child, > .btn:last-child {
|
||||
.border-radius(0px);
|
||||
}
|
||||
|
||||
> .btn {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.dropdown:first-child {
|
||||
border-right: 1px solid #cccccc;
|
||||
}
|
||||
|
||||
.dropdown {
|
||||
float: right;
|
||||
border-right: none;
|
||||
|
||||
.btn-primary {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,3 +1,15 @@
|
|||
<div>
|
||||
<h1>Albums</h1>
|
||||
|
||||
<div class="pagination" ng-show="totalPages > 1">
|
||||
<ul>
|
||||
<li ng-class="{disabled: !prevPage}"><a href="#" ng-click="gotoPage(prevPage);" pfm-eat-click>Prev</a></li>
|
||||
<li ng-repeat="page in pages" ng-class="{active: page == currentPage}">
|
||||
<a href="#" ng-click="gotoPage(page);" pfm-eat-click>{{page}}</a>
|
||||
</li>
|
||||
<li ng-class="{disabled: !nextPage}"><a href="#" ng-click="gotoPage(nextPage);" pfm-eat-click>Next</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<ui-view></ui-view>
|
||||
</div>
|
14
public/templates/albums/list.html
Normal file
14
public/templates/albums/list.html
Normal file
|
@ -0,0 +1,14 @@
|
|||
<ul class="albums-listing stretch-to-bottom">
|
||||
<li ng-repeat="album in albums">
|
||||
<a href="{{album.url}}">
|
||||
<img class="image" ng-src="{{album.covers.normal}}" />
|
||||
<span class="title">{{album.title}}</span>
|
||||
<span class="published">
|
||||
by {{album.user.name}}
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
<li ng-show="!albums.length" class="empty">
|
||||
No albums found...
|
||||
</li>
|
||||
</ul>
|
88
public/templates/albums/show.html
Normal file
88
public/templates/albums/show.html
Normal file
|
@ -0,0 +1,88 @@
|
|||
<div class="album-details">
|
||||
<ul class="breadcrumb">
|
||||
<li><a href="/albums">Albums</a> <span class="divider">/</span></li>
|
||||
<li class="active">{{album.title}}</li>
|
||||
</ul>
|
||||
|
||||
<div class="track-toolbar btn-group pull-right">
|
||||
<a href="#" class="btn btn-small">
|
||||
Favourite This!
|
||||
<i class="icon-star-empty"></i>
|
||||
</a>
|
||||
<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 album.formats"><a href="{{format.url}}">{{format.name}}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h1>
|
||||
{{album.title}}
|
||||
<span class="subtitle">
|
||||
by: <a href="{{album.user.url}}">{{album.user.name}}</a>
|
||||
</span>
|
||||
</h1>
|
||||
|
||||
<div class="stretch-to-bottom">
|
||||
<div class="row-fluid">
|
||||
<div class="span8">
|
||||
<div class="description">
|
||||
<p ng-bind-html-unsafe="album.description | noHTML | newlines"></p>
|
||||
</div>
|
||||
|
||||
<h2>Tracks</h2>
|
||||
<ul class="tracks-listing">
|
||||
<li ng-repeat="track in album.tracks">
|
||||
<div class="image">
|
||||
<a href="#" class="play-button"><i class="icon-play"></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>
|
||||
</div>
|
||||
<a class="info" href="{{track.url}}">
|
||||
<span class="title">{{track.title}}</span>
|
||||
<span class="metadata">
|
||||
by: <span class="artist">{{track.user.name}}</span> /
|
||||
<span class="genre">{{track.genre.name}}</span> /
|
||||
{{track.published_at.date | momentFromNow}}
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<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>
|
||||
</div>
|
||||
<div class="span4 cover-image">
|
||||
<img ng-src="{{album.covers.normal}}" />
|
||||
|
||||
<div class="fb-like" data-href="{{album.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="{{album.url}}" data-text="{{album.title + ' by ' + album.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>
|
||||
|
||||
<h2>Stats</h2>
|
||||
<ul class="stats">
|
||||
<li>Views: <strong>{{album.stats.views}}</strong></li>
|
||||
<li>Downloads: <strong>{{album.stats.downloads}}</strong></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,3 +1,15 @@
|
|||
<div>
|
||||
<h1>Artists</h1>
|
||||
<h1>Artist Directory</h1>
|
||||
|
||||
<div class="pagination" ng-show="totalPages > 1">
|
||||
<ul>
|
||||
<li ng-class="{disabled: !prevPage}"><a href="#" ng-click="gotoPage(prevPage);" pfm-eat-click>Prev</a></li>
|
||||
<li ng-repeat="page in pages" ng-class="{active: page == currentPage}">
|
||||
<a href="#" ng-click="gotoPage(page);" pfm-eat-click>{{page}}</a>
|
||||
</li>
|
||||
<li ng-class="{disabled: !nextPage}"><a href="#" ng-click="gotoPage(nextPage);" pfm-eat-click>Next</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<ui-view></ui-view>
|
||||
</div>
|
14
public/templates/artists/list.html
Normal file
14
public/templates/artists/list.html
Normal file
|
@ -0,0 +1,14 @@
|
|||
<ul class="artist-listing stretch-to-bottom">
|
||||
<li ng-repeat="artist in artists">
|
||||
<a href="{{artist.url}}">
|
||||
<img class="image" ng-src="{{artist.avatars.normal}}" />
|
||||
<span class="title">{{artist.name}}</span>
|
||||
<span class="published">
|
||||
joined {{artist.created_at | momentFromNow}}
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
<li ng-show="!artists.length" class="empty">
|
||||
No artists found...
|
||||
</li>
|
||||
</ul>
|
8
public/templates/artists/show.html
Normal file
8
public/templates/artists/show.html
Normal file
|
@ -0,0 +1,8 @@
|
|||
<div>
|
||||
<ul class="breadcrumb">
|
||||
<li><a href="/artists">Artist Directory</a> <span class="divider">/</span></li>
|
||||
<li class="active">{{artist.name}}</li>
|
||||
</ul>
|
||||
|
||||
<h1>{{artist.name}}</h1>
|
||||
</div>
|
|
@ -1,4 +1,4 @@
|
|||
<div>
|
||||
<div class="track-details">
|
||||
<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>
|
||||
|
@ -10,15 +10,21 @@
|
|||
Favourite This!
|
||||
<i class="icon-star-empty"></i>
|
||||
</a>
|
||||
<a href="#" class="btn btn-small">
|
||||
Add to Playlist <i class="caret"></i>
|
||||
</a>
|
||||
<div class="dropdown">
|
||||
<a href="#" class="btn btn-small btn-info dropdown-toggle">
|
||||
Downloads <i class="caret"></i>
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="#"></a></li>
|
||||
<li ng-repeat="format in track.formats"><a href="{{format.url}}">{{format.name}} <small>({{format.size}})</small></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="dropdown">
|
||||
<a href="#" class="btn btn-small dropdown-toggle">
|
||||
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>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -26,13 +32,17 @@
|
|||
<h1>
|
||||
{{track.title}}
|
||||
<span class="subtitle">
|
||||
<span ng-show="track.album">
|
||||
from: <a href="{{track.album.url}}">{{track.album.title}}</a>
|
||||
</span>
|
||||
|
||||
by: <a href="{{track.user.url}}">{{track.user.name}}</a>
|
||||
</span>
|
||||
</h1>
|
||||
|
||||
<div class="track-details">
|
||||
<div class="stretch-to-bottom">
|
||||
<div class="row-fluid">
|
||||
<div class="span7">
|
||||
<div class="span8">
|
||||
<div class="description">
|
||||
<p ng-bind-html-unsafe="track.description | noHTML | newlines"></p>
|
||||
</div>
|
||||
|
@ -46,12 +56,37 @@
|
|||
<p class="content" ng-bind-html-unsafe="track.lyrics | noHTML | newlines"></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="span5 cover-image">
|
||||
<img ng-src="{{track.covers.normal}}" />
|
||||
</div>
|
||||
</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>
|
||||
</div>
|
||||
<div class="span4 cover-image">
|
||||
<img ng-src="{{track.covers.normal}}" />
|
||||
|
||||
<div class="fb-like" data-href="{{track.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="{{track.url}}" data-text="{{track.title + ' by ' + track.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>
|
||||
|
||||
<h2>Stats</h2>
|
||||
<ul class="stats">
|
||||
<li>Published: <strong>{{track.published_at | pfmdate:"short"}}</strong></li>
|
||||
<li>Views: <strong>{{track.stats.views}}</strong></li>
|
||||
<li>Plays: <strong>{{track.stats.plays}}</strong></li>
|
||||
<li>Downloads: <strong>{{track.stats.downloads}}</strong></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
Loading…
Reference in a new issue