mirror of
https://github.com/Poniverse/Pony.fm.git
synced 2024-11-29 08:07:59 +01:00
#4: Implement cached playlist downloads
This commit is contained in:
parent
80ad614b5f
commit
6c5155f583
8 changed files with 149 additions and 22 deletions
|
@ -282,7 +282,7 @@ class Album extends Model
|
||||||
return $trackCount;
|
return $trackCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function countCacheableTrackFiles($format)
|
public function countCachedTrackFiles($format)
|
||||||
{
|
{
|
||||||
$cachedCount = 0;
|
$cachedCount = 0;
|
||||||
|
|
||||||
|
|
|
@ -102,7 +102,7 @@ class AlbumsController extends ApiControllerBase
|
||||||
|
|
||||||
$trackCount = $album->countDownloadableTracks();
|
$trackCount = $album->countDownloadableTracks();
|
||||||
try {
|
try {
|
||||||
$cachedCount = $album->countCacheableTrackFiles($format);
|
$cachedCount = $album->countCachedTrackFiles($format);
|
||||||
} catch (ModelNotFoundException $e) {
|
} catch (ModelNotFoundException $e) {
|
||||||
return $this->notFound('Track file in album not found!');
|
return $this->notFound('Track file in album not found!');
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
|
|
||||||
namespace Poniverse\Ponyfm\Http\Controllers\Api\Web;
|
namespace Poniverse\Ponyfm\Http\Controllers\Api\Web;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||||
use Poniverse\Ponyfm\Commands\AddTrackToPlaylistCommand;
|
use Poniverse\Ponyfm\Commands\AddTrackToPlaylistCommand;
|
||||||
use Poniverse\Ponyfm\Commands\CreatePlaylistCommand;
|
use Poniverse\Ponyfm\Commands\CreatePlaylistCommand;
|
||||||
use Poniverse\Ponyfm\Commands\DeletePlaylistCommand;
|
use Poniverse\Ponyfm\Commands\DeletePlaylistCommand;
|
||||||
|
@ -31,6 +32,7 @@ use Poniverse\Ponyfm\ResourceLogItem;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\Support\Facades\Input;
|
use Illuminate\Support\Facades\Input;
|
||||||
use Illuminate\Support\Facades\Response;
|
use Illuminate\Support\Facades\Response;
|
||||||
|
use Poniverse\Ponyfm\Track;
|
||||||
|
|
||||||
class PlaylistsController extends ApiControllerBase
|
class PlaylistsController extends ApiControllerBase
|
||||||
{
|
{
|
||||||
|
@ -110,6 +112,40 @@ class PlaylistsController extends ApiControllerBase
|
||||||
return Response::json(Playlist::mapPublicPlaylistShow($playlist), 200);
|
return Response::json(Playlist::mapPublicPlaylistShow($playlist), 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getCachedPlaylist($id, $format)
|
||||||
|
{
|
||||||
|
// Validation
|
||||||
|
try {
|
||||||
|
$playlist = Playlist::with('tracks.trackFiles')->findOrFail($id);
|
||||||
|
} catch (ModelNotFoundException $e) {
|
||||||
|
return $this->notFound('Playlist not found!');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((!$playlist->is_public && !Auth::check()) || (!$playlist->is_public && ($playlist->user_id !== Auth::user()->id))) {
|
||||||
|
return $this->notFound('Playlist not found!');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!in_array($format, Track::$CacheableFormats)) {
|
||||||
|
return $this->notFound('Format not found!');
|
||||||
|
}
|
||||||
|
|
||||||
|
$trackCount = $playlist->countDownloadableTracks();
|
||||||
|
try {
|
||||||
|
$cachedCount = $playlist->countCachedTrackFiles($format);
|
||||||
|
} catch (ModelNotFoundException $e) {
|
||||||
|
return $this->notFound('Track file in playlist not found!');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($trackCount === $cachedCount) {
|
||||||
|
$url = $playlist->getDownloadUrl($format);
|
||||||
|
} else {
|
||||||
|
$playlist->encodeCacheableTrackFiles($format);
|
||||||
|
$url = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Response::json(['url' => $url], 200);
|
||||||
|
}
|
||||||
|
|
||||||
public function getPinned()
|
public function getPinned()
|
||||||
{
|
{
|
||||||
$query = Playlist
|
$query = Playlist
|
||||||
|
|
|
@ -79,14 +79,15 @@ Route::group(['prefix' => 'api/web'], function() {
|
||||||
|
|
||||||
Route::get('/tracks', 'Api\Web\TracksController@getIndex');
|
Route::get('/tracks', 'Api\Web\TracksController@getIndex');
|
||||||
Route::get('/tracks/{id}', 'Api\Web\TracksController@getShow')->where('id', '\d+');
|
Route::get('/tracks/{id}', 'Api\Web\TracksController@getShow')->where('id', '\d+');
|
||||||
Route::get('/tracks/cached/{id}/{format}', 'Api\Web\TracksController@getCachedTrack')->where(['id' => '\d+', 'name' => '.+']);
|
Route::get('/tracks/cached/{id}/{format}', 'Api\Web\TracksController@getCachedTrack')->where(['id' => '\d+', 'format' => '.+']);
|
||||||
|
|
||||||
Route::get('/albums', 'Api\Web\AlbumsController@getIndex');
|
Route::get('/albums', 'Api\Web\AlbumsController@getIndex');
|
||||||
Route::get('/albums/{id}', 'Api\Web\AlbumsController@getShow')->where('id', '\d+');
|
Route::get('/albums/{id}', 'Api\Web\AlbumsController@getShow')->where('id', '\d+');
|
||||||
Route::get('/albums/cached/{id}/{format}', 'Api\Web\AlbumsController@getCachedAlbum')->where(['id' => '\d+', 'name' => '.+']);
|
Route::get('/albums/cached/{id}/{format}', 'Api\Web\AlbumsController@getCachedAlbum')->where(['id' => '\d+', 'format' => '.+']);
|
||||||
|
|
||||||
Route::get('/playlists', 'Api\Web\PlaylistsController@getIndex');
|
Route::get('/playlists', 'Api\Web\PlaylistsController@getIndex');
|
||||||
Route::get('/playlists/{id}', 'Api\Web\PlaylistsController@getShow')->where('id', '\d+');
|
Route::get('/playlists/{id}', 'Api\Web\PlaylistsController@getShow')->where('id', '\d+');
|
||||||
|
Route::get('/playlists/cached/{id}/{format}', 'Api\Web\PlaylistsController@getCachedPlaylist')->where(['id' => '\d+', 'format' => '.+']);
|
||||||
|
|
||||||
Route::get('/comments/{type}/{id}', 'Api\Web\CommentsController@getIndex')->where('id', '\d+');
|
Route::get('/comments/{type}/{id}', 'Api\Web\CommentsController@getIndex')->where('id', '\d+');
|
||||||
|
|
||||||
|
|
|
@ -20,19 +20,23 @@
|
||||||
|
|
||||||
namespace Poniverse\Ponyfm;
|
namespace Poniverse\Ponyfm;
|
||||||
|
|
||||||
|
use File;
|
||||||
use Helpers;
|
use Helpers;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
use Illuminate\Foundation\Bus\DispatchesJobs;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\Support\Facades\Cache;
|
use Illuminate\Support\Facades\Cache;
|
||||||
use Illuminate\Support\Facades\URL;
|
use Illuminate\Support\Facades\URL;
|
||||||
|
use Poniverse\Ponyfm\Jobs\EncodeTrackFile;
|
||||||
use Poniverse\Ponyfm\Traits\SlugTrait;
|
use Poniverse\Ponyfm\Traits\SlugTrait;
|
||||||
|
|
||||||
class Playlist extends Model
|
class Playlist extends Model
|
||||||
{
|
{
|
||||||
protected $table = 'playlists';
|
use SoftDeletes, SlugTrait, DispatchesJobs;
|
||||||
|
|
||||||
use SoftDeletes, SlugTrait;
|
protected $table = 'playlists';
|
||||||
|
|
||||||
protected $dates = ['deleted_at'];
|
protected $dates = ['deleted_at'];
|
||||||
|
|
||||||
|
@ -68,7 +72,8 @@ class Playlist extends Model
|
||||||
'name' => $name,
|
'name' => $name,
|
||||||
'extension' => $format['extension'],
|
'extension' => $format['extension'],
|
||||||
'url' => $playlist->getDownloadUrl($name),
|
'url' => $playlist->getDownloadUrl($name),
|
||||||
'size' => Helpers::formatBytes($playlist->getFilesize($name))
|
'size' => Helpers::formatBytes($playlist->getFilesize($name)),
|
||||||
|
'isCacheable' => (in_array($name, Track::$CacheableFormats) ? true : false)
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,25 +109,25 @@ class Playlist extends Model
|
||||||
$userRow = $playlist->users[0];
|
$userRow = $playlist->users[0];
|
||||||
$userData = [
|
$userData = [
|
||||||
'stats' => [
|
'stats' => [
|
||||||
'views' => (int) $userRow->view_count,
|
'views' => (int)$userRow->view_count,
|
||||||
'downloads' => (int) $userRow->download_count,
|
'downloads' => (int)$userRow->download_count,
|
||||||
],
|
],
|
||||||
'is_favourited' => (bool) $userRow->is_favourited
|
'is_favourited' => (bool)$userRow->is_favourited
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'id' => (int) $playlist->id,
|
'id' => (int)$playlist->id,
|
||||||
'track_count' => $playlist->track_count,
|
'track_count' => $playlist->track_count,
|
||||||
'title' => $playlist->title,
|
'title' => $playlist->title,
|
||||||
'slug' => $playlist->slug,
|
'slug' => $playlist->slug,
|
||||||
'created_at' => $playlist->created_at,
|
'created_at' => $playlist->created_at,
|
||||||
'is_public' => (bool) $playlist->is_public,
|
'is_public' => (bool)$playlist->is_public,
|
||||||
'stats' => [
|
'stats' => [
|
||||||
'views' => (int) $playlist->view_count,
|
'views' => (int)$playlist->view_count,
|
||||||
'downloads' => (int) $playlist->download_count,
|
'downloads' => (int)$playlist->download_count,
|
||||||
'comments' => (int) $playlist->comment_count,
|
'comments' => (int)$playlist->comment_count,
|
||||||
'favourites' => (int) $playlist->favourite_count
|
'favourites' => (int)$playlist->favourite_count
|
||||||
],
|
],
|
||||||
'covers' => [
|
'covers' => [
|
||||||
'small' => $playlist->getCoverUrl(Image::SMALL),
|
'small' => $playlist->getCoverUrl(Image::SMALL),
|
||||||
|
@ -130,7 +135,7 @@ class Playlist extends Model
|
||||||
],
|
],
|
||||||
'url' => $playlist->url,
|
'url' => $playlist->url,
|
||||||
'user' => [
|
'user' => [
|
||||||
'id' => (int) $playlist->user->id,
|
'id' => (int)$playlist->user->id,
|
||||||
'name' => $playlist->user->display_name,
|
'name' => $playlist->user->display_name,
|
||||||
'url' => $playlist->user->url,
|
'url' => $playlist->user->url,
|
||||||
],
|
],
|
||||||
|
@ -197,6 +202,63 @@ class Playlist extends Model
|
||||||
return URL::to('p' . $this->id . '/dl.' . Track::$Formats[$format]['extension']);
|
return URL::to('p' . $this->id . '/dl.' . Track::$Formats[$format]['extension']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function countDownloadableTracks()
|
||||||
|
{
|
||||||
|
$trackCount = 0;
|
||||||
|
|
||||||
|
foreach ($this->tracks as $track) {
|
||||||
|
if ($track->is_downloadable == true) {
|
||||||
|
$trackCount++;
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $trackCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function countCachedTrackFiles($format)
|
||||||
|
{
|
||||||
|
$cachedCount = 0;
|
||||||
|
|
||||||
|
foreach ($this->tracks as $track) {
|
||||||
|
if ($track->is_downloadable == false) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$trackFile = $track->trackFiles()->where('format', $format)->firstOrFail();
|
||||||
|
} catch (ModelNotFoundException $e) {
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($trackFile->expires_at != null && File::exists($trackFile->getFile())) {
|
||||||
|
$cachedCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $cachedCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function encodeCacheableTrackFiles($format)
|
||||||
|
{
|
||||||
|
foreach ($this->tracks as $track) {
|
||||||
|
if ($track->is_downloadable == false) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$trackFile = $track->trackFiles()->where('format', $format)->firstOrFail();
|
||||||
|
} catch (ModelNotFoundException $e) {
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!File::exists($trackFile->getFile()) && $trackFile->is_in_progress != true) {
|
||||||
|
$this->dispatch(new EncodeTrackFile($trackFile, true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function getFilesize($format)
|
public function getFilesize($format)
|
||||||
{
|
{
|
||||||
$tracks = $this->tracks;
|
$tracks = $this->tracks;
|
||||||
|
@ -207,7 +269,10 @@ class Playlist extends Model
|
||||||
return Cache::remember($this->getCacheKey('filesize-' . $format), 1440, function () use ($tracks, $format) {
|
return Cache::remember($this->getCacheKey('filesize-' . $format), 1440, function () use ($tracks, $format) {
|
||||||
$size = 0;
|
$size = 0;
|
||||||
foreach ($tracks as $track) {
|
foreach ($tracks as $track) {
|
||||||
$size += $track->getFilesize($format);
|
// Ensure that only downloadable tracks are added onto the file size
|
||||||
|
if ($track->is_downloadable == 1) {
|
||||||
|
$size += $track->getFilesize($format);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $size;
|
return $size;
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li ng-show="isInProgress" class="cache-loading"><img src="/images/loading.gif" /></li>
|
<li ng-show="isInProgress" class="cache-loading"><img src="/images/loading.gif" /></li>
|
||||||
<li ng-show="isInProgress" class="cache-loading"><small>We're getting your download ready! This may take up to a few minutes .</small></li>
|
<li ng-show="isInProgress" class="cache-loading"><small>We're getting your download ready! This may take up to a few minutes.</small></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li><a href="#" class="btn" pfm-eat-click ng-click="share()">Share</a></li>
|
<li><a href="#" class="btn" pfm-eat-click ng-click="share()">Share</a></li>
|
||||||
|
|
|
@ -5,7 +5,18 @@
|
||||||
Downloads
|
Downloads
|
||||||
</a>
|
</a>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
<li bindonce ng-repeat="format in playlist.formats"><a target="_blank" bo-href="format.url"><span bo-text="format.name"></span> <small bo-text="'(' + format.size + ')'"></small></a></li>
|
<li bindonce ng-repeat="format in playlist.formats" ng-hide="isInProgress">
|
||||||
|
<a target="_blank" ng-if="!format.isCacheable" bo-href="format.url">
|
||||||
|
<span bo-text="format.name"></span>
|
||||||
|
<small bo-text="'(' + format.size + ')'"></small>
|
||||||
|
</a>
|
||||||
|
<a ng-if="format.isCacheable" ng-click="getCachedPlaylist(playlist.id, format.name);" href="">
|
||||||
|
<span bo-text="format.name"></span>
|
||||||
|
<small bo-text="'(' + format.size + ')'"></small>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li ng-show="isInProgress" class="cache-loading"><img src="/images/loading.gif" /></li>
|
||||||
|
<li ng-show="isInProgress" class="cache-loading"><small>We're getting your download ready! This may take up to a few minutes.</small></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li><a href="#" class="btn" pfm-eat-click ng-click="share()">Share</a></li>
|
<li><a href="#" class="btn" pfm-eat-click ng-click="share()">Share</a></li>
|
||||||
|
|
|
@ -21,8 +21,8 @@ window.pfm.preloaders['playlist'] = [
|
||||||
]
|
]
|
||||||
|
|
||||||
angular.module('ponyfm').controller 'playlist', [
|
angular.module('ponyfm').controller 'playlist', [
|
||||||
'$scope', '$state', 'playlists', '$dialog'
|
'$scope', '$state', 'playlists', '$dialog', 'download-cached', '$window', '$timeout'
|
||||||
($scope, $state, playlists, $dialog) ->
|
($scope, $state, playlists, $dialog, cachedPlaylist, $window, $timeout) ->
|
||||||
playlist = null
|
playlist = null
|
||||||
|
|
||||||
playlists.fetch($state.params.id).done (playlistResponse) ->
|
playlists.fetch($state.params.id).done (playlistResponse) ->
|
||||||
|
@ -34,4 +34,18 @@ angular.module('ponyfm').controller 'playlist', [
|
||||||
templateUrl: '/templates/partials/playlist-share-dialog.html',
|
templateUrl: '/templates/partials/playlist-share-dialog.html',
|
||||||
controller: ['$scope', ($scope) -> $scope.playlist = playlist; $scope.close = () -> dialog.close()]
|
controller: ['$scope', ($scope) -> $scope.playlist = playlist; $scope.close = () -> dialog.close()]
|
||||||
dialog.open()
|
dialog.open()
|
||||||
|
|
||||||
|
$scope.getCachedPlaylist = (id, format) ->
|
||||||
|
$scope.isInProgress = true
|
||||||
|
|
||||||
|
cachedPlaylist.download('playlists', id, format).then (response) ->
|
||||||
|
$scope.playlistUrl = response
|
||||||
|
if $scope.playlistUrl == 'error'
|
||||||
|
$scope.isInProgress = false
|
||||||
|
else if $scope.playlistUrl == 'pending'
|
||||||
|
$timeout $scope.getCachedPlaylist(id, format), 5000
|
||||||
|
else
|
||||||
|
$scope.isInProgress = false
|
||||||
|
$window.open $scope.playlistUrl
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue