diff --git a/app/Album.php b/app/Album.php index a6b8c7be..9c5d2bc3 100644 --- a/app/Album.php +++ b/app/Album.php @@ -282,7 +282,7 @@ class Album extends Model return $trackCount; } - public function countCacheableTrackFiles($format) + public function countCachedTrackFiles($format) { $cachedCount = 0; diff --git a/app/Http/Controllers/Api/Web/AlbumsController.php b/app/Http/Controllers/Api/Web/AlbumsController.php index c2536145..67b0b98d 100644 --- a/app/Http/Controllers/Api/Web/AlbumsController.php +++ b/app/Http/Controllers/Api/Web/AlbumsController.php @@ -102,7 +102,7 @@ class AlbumsController extends ApiControllerBase $trackCount = $album->countDownloadableTracks(); try { - $cachedCount = $album->countCacheableTrackFiles($format); + $cachedCount = $album->countCachedTrackFiles($format); } catch (ModelNotFoundException $e) { return $this->notFound('Track file in album not found!'); } diff --git a/app/Http/Controllers/Api/Web/PlaylistsController.php b/app/Http/Controllers/Api/Web/PlaylistsController.php index 7ed44d91..246d2724 100644 --- a/app/Http/Controllers/Api/Web/PlaylistsController.php +++ b/app/Http/Controllers/Api/Web/PlaylistsController.php @@ -20,6 +20,7 @@ namespace Poniverse\Ponyfm\Http\Controllers\Api\Web; +use Illuminate\Database\Eloquent\ModelNotFoundException; use Poniverse\Ponyfm\Commands\AddTrackToPlaylistCommand; use Poniverse\Ponyfm\Commands\CreatePlaylistCommand; use Poniverse\Ponyfm\Commands\DeletePlaylistCommand; @@ -31,6 +32,7 @@ use Poniverse\Ponyfm\ResourceLogItem; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Input; use Illuminate\Support\Facades\Response; +use Poniverse\Ponyfm\Track; class PlaylistsController extends ApiControllerBase { @@ -110,6 +112,40 @@ class PlaylistsController extends ApiControllerBase 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() { $query = Playlist diff --git a/app/Http/routes.php b/app/Http/routes.php index a2358339..a4e4cd07 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -79,14 +79,15 @@ Route::group(['prefix' => 'api/web'], function() { Route::get('/tracks', 'Api\Web\TracksController@getIndex'); 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/{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/{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+'); diff --git a/app/Playlist.php b/app/Playlist.php index f3621122..216b0cb1 100644 --- a/app/Playlist.php +++ b/app/Playlist.php @@ -20,19 +20,23 @@ namespace Poniverse\Ponyfm; +use File; use Helpers; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\ModelNotFoundException; use Illuminate\Database\Eloquent\SoftDeletes; +use Illuminate\Foundation\Bus\DispatchesJobs; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\URL; +use Poniverse\Ponyfm\Jobs\EncodeTrackFile; use Poniverse\Ponyfm\Traits\SlugTrait; class Playlist extends Model { - protected $table = 'playlists'; + use SoftDeletes, SlugTrait, DispatchesJobs; - use SoftDeletes, SlugTrait; + protected $table = 'playlists'; protected $dates = ['deleted_at']; @@ -68,7 +72,8 @@ class Playlist extends Model 'name' => $name, 'extension' => $format['extension'], '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]; $userData = [ 'stats' => [ - 'views' => (int) $userRow->view_count, - 'downloads' => (int) $userRow->download_count, + 'views' => (int)$userRow->view_count, + 'downloads' => (int)$userRow->download_count, ], - 'is_favourited' => (bool) $userRow->is_favourited + 'is_favourited' => (bool)$userRow->is_favourited ]; } return [ - 'id' => (int) $playlist->id, + 'id' => (int)$playlist->id, 'track_count' => $playlist->track_count, 'title' => $playlist->title, 'slug' => $playlist->slug, 'created_at' => $playlist->created_at, - 'is_public' => (bool) $playlist->is_public, + 'is_public' => (bool)$playlist->is_public, 'stats' => [ - 'views' => (int) $playlist->view_count, - 'downloads' => (int) $playlist->download_count, - 'comments' => (int) $playlist->comment_count, - 'favourites' => (int) $playlist->favourite_count + 'views' => (int)$playlist->view_count, + 'downloads' => (int)$playlist->download_count, + 'comments' => (int)$playlist->comment_count, + 'favourites' => (int)$playlist->favourite_count ], 'covers' => [ 'small' => $playlist->getCoverUrl(Image::SMALL), @@ -130,7 +135,7 @@ class Playlist extends Model ], 'url' => $playlist->url, 'user' => [ - 'id' => (int) $playlist->user->id, + 'id' => (int)$playlist->user->id, 'name' => $playlist->user->display_name, 'url' => $playlist->user->url, ], @@ -197,6 +202,63 @@ class Playlist extends Model 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) { $tracks = $this->tracks; @@ -207,7 +269,10 @@ class Playlist extends Model return Cache::remember($this->getCacheKey('filesize-' . $format), 1440, function () use ($tracks, $format) { $size = 0; 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; diff --git a/public/templates/albums/show.html b/public/templates/albums/show.html index 530c70ac..704aea0e 100644 --- a/public/templates/albums/show.html +++ b/public/templates/albums/show.html @@ -16,7 +16,7 @@
  • -
  • We're getting your download ready! This may take up to a few minutes .
  • +
  • We're getting your download ready! This may take up to a few minutes.
  • Share
  • diff --git a/public/templates/playlists/show.html b/public/templates/playlists/show.html index 4dcf9a69..57a2d2f8 100644 --- a/public/templates/playlists/show.html +++ b/public/templates/playlists/show.html @@ -5,7 +5,18 @@ Downloads
  • Share
  • diff --git a/resources/assets/scripts/app/controllers/playlist.coffee b/resources/assets/scripts/app/controllers/playlist.coffee index 6180ab2c..2f39e744 100644 --- a/resources/assets/scripts/app/controllers/playlist.coffee +++ b/resources/assets/scripts/app/controllers/playlist.coffee @@ -21,8 +21,8 @@ window.pfm.preloaders['playlist'] = [ ] angular.module('ponyfm').controller 'playlist', [ - '$scope', '$state', 'playlists', '$dialog' - ($scope, $state, playlists, $dialog) -> + '$scope', '$state', 'playlists', '$dialog', 'download-cached', '$window', '$timeout' + ($scope, $state, playlists, $dialog, cachedPlaylist, $window, $timeout) -> playlist = null playlists.fetch($state.params.id).done (playlistResponse) -> @@ -34,4 +34,18 @@ angular.module('ponyfm').controller 'playlist', [ templateUrl: '/templates/partials/playlist-share-dialog.html', controller: ['$scope', ($scope) -> $scope.playlist = playlist; $scope.close = () -> dialog.close()] 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 ] +