mirror of
https://github.com/Poniverse/Pony.fm.git
synced 2024-11-25 14:37:59 +01:00
Add in changes from code review
This commit is contained in:
parent
eca01b5f29
commit
b2408e3d84
19 changed files with 354 additions and 260 deletions
|
@ -21,17 +21,21 @@
|
|||
namespace Poniverse\Ponyfm;
|
||||
|
||||
use Exception;
|
||||
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\File;
|
||||
use Illuminate\Support\Facades\URL;
|
||||
use Poniverse\Ponyfm\Jobs\EncodeTrackFile;
|
||||
use Poniverse\Ponyfm\Traits\SlugTrait;
|
||||
use Helpers;
|
||||
|
||||
class Album extends Model
|
||||
{
|
||||
use SoftDeletes, SlugTrait;
|
||||
use SoftDeletes, SlugTrait, DispatchesJobs;
|
||||
|
||||
protected $dates = ['deleted_at'];
|
||||
|
||||
|
@ -100,7 +104,7 @@ class Album extends Model
|
|||
'extension' => $format['extension'],
|
||||
'url' => $album->getDownloadUrl($name),
|
||||
'size' => Helpers::formatBytes($album->getFilesize($name)),
|
||||
'isCacheable' => (in_array($name, array_keys(Track::$CacheableFormats)) ? true : false)
|
||||
'isCacheable' => (in_array($name, Track::$CacheableFormats) ? true : false)
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -146,24 +150,24 @@ class Album extends Model
|
|||
$userRow = $album->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) $album->id,
|
||||
'track_count' => (int) $album->track_count,
|
||||
'id' => (int)$album->id,
|
||||
'track_count' => (int)$album->track_count,
|
||||
'title' => $album->title,
|
||||
'slug' => $album->slug,
|
||||
'created_at' => $album->created_at,
|
||||
'stats' => [
|
||||
'views' => (int) $album->view_count,
|
||||
'downloads' => (int) $album->download_count,
|
||||
'comments' => (int) $album->comment_count,
|
||||
'favourites' => (int) $album->favourite_count
|
||||
'views' => (int)$album->view_count,
|
||||
'downloads' => (int)$album->download_count,
|
||||
'comments' => (int)$album->comment_count,
|
||||
'favourites' => (int)$album->favourite_count
|
||||
],
|
||||
'covers' => [
|
||||
'small' => $album->getCoverUrl(Image::SMALL),
|
||||
|
@ -171,7 +175,7 @@ class Album extends Model
|
|||
],
|
||||
'url' => $album->url,
|
||||
'user' => [
|
||||
'id' => (int) $album->user->id,
|
||||
'id' => (int)$album->user->id,
|
||||
'name' => $album->user->display_name,
|
||||
'url' => $album->user->url,
|
||||
],
|
||||
|
@ -263,6 +267,63 @@ class Album extends Model
|
|||
}
|
||||
}
|
||||
|
||||
public function countDownloadableTracks()
|
||||
{
|
||||
$trackCount = 0;
|
||||
|
||||
foreach ($this->tracks as $track) {
|
||||
if ($track->is_downloadable == true) {
|
||||
$trackCount++;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return $trackCount;
|
||||
}
|
||||
|
||||
public function countCacheableTrackFiles($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 syncTrackIds($trackIds)
|
||||
{
|
||||
$trackIdsInAlbum = [];
|
||||
|
@ -332,7 +393,7 @@ class Album extends Model
|
|||
}
|
||||
}
|
||||
|
||||
private function getCacheKey($key)
|
||||
public function getCacheKey($key)
|
||||
{
|
||||
return 'album-' . $this->id . '-' . $key;
|
||||
}
|
||||
|
|
|
@ -136,7 +136,7 @@ class UploadTrackCommand extends CommandBase
|
|||
$trackFile = new TrackFile();
|
||||
$trackFile->is_master = $name === 'FLAC' ? true : false;
|
||||
$trackFile->format = $name;
|
||||
if (array_key_exists($name, Track::$CacheableFormats) && $trackFile->is_master == false) {
|
||||
if (in_array($name, Track::$CacheableFormats) && $trackFile->is_master == false) {
|
||||
$trackFile->is_cacheable = true;
|
||||
} else {
|
||||
$trackFile->is_cacheable = false;
|
||||
|
@ -144,7 +144,7 @@ class UploadTrackCommand extends CommandBase
|
|||
$track->trackFiles()->save($trackFile);
|
||||
|
||||
// Encode track file
|
||||
$target = $destination . '/' . $trackFile->getFilename();
|
||||
$target = $trackFile->getFile();
|
||||
|
||||
$command = $format['command'];
|
||||
$command = str_replace('{$source}', '"' . $source . '"', $command);
|
||||
|
@ -161,7 +161,7 @@ class UploadTrackCommand extends CommandBase
|
|||
|
||||
// Delete track file if it is cacheable
|
||||
if ($trackFile->is_cacheable == true) {
|
||||
\Illuminate\Support\Facades\File::delete($trackFile->getFile());
|
||||
File::delete($trackFile->getFile());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -60,45 +60,45 @@ class ClearTrackCache extends Command
|
|||
*/
|
||||
public function handle()
|
||||
{
|
||||
if ($this->option('tracks') == 'all') {
|
||||
// Get all cacheable tracks
|
||||
$trackFiles = TrackFile::where('is_cacheable', true)->get();
|
||||
} else {
|
||||
// Get all expired tracks
|
||||
if ($this->option('tracks') === 'all') {
|
||||
// Get all cacheable track files
|
||||
$trackFiles = TrackFile::where('is_cacheable', true)
|
||||
->where('expiration', '<=', Carbon::now())
|
||||
->with('track.album')
|
||||
->get();
|
||||
} else {
|
||||
// Get all expired track files
|
||||
$trackFiles = TrackFile::where('is_cacheable', true)
|
||||
->where('expires_at', '<=', Carbon::now())
|
||||
->with('track.album')
|
||||
->get();
|
||||
|
||||
}
|
||||
|
||||
// Delete above tracks
|
||||
// Delete above track files
|
||||
if (count($trackFiles) === 0) {
|
||||
$this->info('No tracks found. Exiting.');
|
||||
} else {
|
||||
|
||||
if ($this->option('force') || $this->confirm(count($trackFiles) . ' tracks found. Proceed to delete? [y|N]', false)) {
|
||||
if ($this->option('force') || $this->confirm(count($trackFiles) . ' cacheable track files found. Proceed to delete their files if they exist? [y|N]', false)) {
|
||||
|
||||
$count = 0;
|
||||
|
||||
foreach ($trackFiles as $trackFile) {
|
||||
|
||||
// Set expiration to null (so can be re-cached upon request)
|
||||
$trackFile->expiration = null;
|
||||
$trackFile->expires_at = null;
|
||||
$trackFile->update();
|
||||
|
||||
// Delete files if exists
|
||||
// Delete file if exists
|
||||
if (File::exists($trackFile->getFile())) {
|
||||
$count++;
|
||||
File::delete($trackFile->getFile());
|
||||
|
||||
// Remove the cached file sizes for main, trackfile and format
|
||||
Cache::forget($trackFile->getCacheKey('filesize'));
|
||||
Cache::forget($trackFile->track()->getCacheKey('filesize-' . $trackFile->format));
|
||||
Cache::forget($trackFile->track()->album()->getCacheKey('filesize-' . $trackFile->format));
|
||||
|
||||
$this->info('Deleted ' . $trackFile->getFile());
|
||||
}
|
||||
|
||||
// Remove the cached file size for the album
|
||||
Cache::forget($trackFile->track->album->getCacheKey('filesize-' . $trackFile->format));
|
||||
|
||||
}
|
||||
$this->info($count . ' files deleted. Deletion complete. Exiting.');
|
||||
} else {
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
namespace Poniverse\Ponyfm\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Poniverse\Ponyfm\TrackFile;
|
||||
|
||||
class RebuildFilesizes extends Command
|
||||
|
@ -68,18 +67,12 @@ class RebuildFilesizes extends Command
|
|||
$this->info('========== Start Chunk ==========');
|
||||
|
||||
foreach ($trackFiles as $trackFile) {
|
||||
$file = $trackFile->getFile();
|
||||
|
||||
if (File::exists($file)) {
|
||||
$size = File::size($file);
|
||||
$size = $trackFile->updateFilesize();
|
||||
if ($size !== null) {
|
||||
$this->info('ID ' . $trackFile->id . ' processed - ' . $size . ' bytes');
|
||||
} else {
|
||||
$size = null;
|
||||
$this->info('ID ' . $trackFile->id . ' skipped');
|
||||
}
|
||||
|
||||
$trackFile->filesize = $size;
|
||||
$trackFile->update();
|
||||
}
|
||||
|
||||
$this->info('=========== End Chunk ===========');
|
||||
|
|
|
@ -63,154 +63,192 @@ class RebuildTrackCache extends Command
|
|||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->info('This will run \'php artisan down\' if you proceed and \'php artisan up\' when complete.');
|
||||
$this->info('***');
|
||||
$this->info('If this is your first time running this command, it is *highly* recommended that you run `php artisan filesize:rebuild` to cache file sizes for all files before they are deleted.');
|
||||
$this->info('If this is your first time running this command, it is *highly* recommended that you ensure the file sizes for all track files have been populated.');
|
||||
$this->info('***');
|
||||
|
||||
if ($this->option('force') || $this->confirm('Are you sure you want to delete all to-be-cached files and encode missing non-cached files? [y|N]',
|
||||
if ($this->option('force') || $this->confirm('Are you sure you want to delete all to-be-cached track files and encode missing non-cached track files?',
|
||||
false)
|
||||
) {
|
||||
|
||||
$this->call('down');
|
||||
|
||||
//==========================================================================================================
|
||||
// Delete previously cached tracks
|
||||
// Delete previously cached track files
|
||||
//==========================================================================================================
|
||||
|
||||
// Find files which are cacheable and NOT master
|
||||
$trackFiles = TrackFile::where('is_cacheable', true)
|
||||
$this->output->newLine(1);
|
||||
$this->info('========== Step 1/4 - Deleting previously cached track files. ==========');
|
||||
|
||||
$count = 0;
|
||||
|
||||
// Chunk track files which are cacheable and NOT master
|
||||
TrackFile::where('is_cacheable', true)
|
||||
->where('is_master', false)
|
||||
->get();
|
||||
->chunk(200, function ($trackFiles) use (&$count) {
|
||||
// Delete chunked track files
|
||||
foreach ($trackFiles as $trackFile) {
|
||||
// Clear expiration so will be re-cached on next request
|
||||
$trackFile->expires_at = null;
|
||||
$trackFile->update();
|
||||
|
||||
// Delete above files
|
||||
if (count($trackFiles) == 0) {
|
||||
$this->info('No tracks found. Continuing.');
|
||||
} else {
|
||||
$this->info(count($trackFiles) . ' tracks found.');
|
||||
$count = 0;
|
||||
|
||||
foreach ($trackFiles as $trackFile) {
|
||||
// Clear expiration so will be re-cached on next request
|
||||
$trackFile->expiration = null;
|
||||
$trackFile->update();
|
||||
|
||||
// Delete files
|
||||
if (File::exists($trackFile->getFile())) {
|
||||
$count++;
|
||||
File::delete($trackFile->getFile());
|
||||
$this->info('Deleted ' . $trackFile->getFile());
|
||||
// Delete files
|
||||
if (File::exists($trackFile->getFile())) {
|
||||
$count++;
|
||||
File::delete($trackFile->getFile());
|
||||
$this->info('Deleted ' . $trackFile->getFile());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->info($count . ' files deleted. Deletion complete. Continuing.');
|
||||
}
|
||||
$this->info($count . ' track files deleted. Deletion complete. Continuing.');
|
||||
});
|
||||
|
||||
//==========================================================================================================
|
||||
// Update the database entries for cacheable files - non-cacheable to cacheable
|
||||
// Update the database entries for cacheable track files - non-cacheable to cacheable
|
||||
//==========================================================================================================
|
||||
|
||||
$this->info('--- Step 2/4 - Updating is_cacheable entries in database. ---');
|
||||
|
||||
// Find files which are meant to be cacheable and NOT master, but currently not cacheable
|
||||
$trackFiles = TrackFile::where('is_cacheable', false)
|
||||
->whereIn('format', array_keys(Track::$CacheableFormats))
|
||||
->where('is_master', false)
|
||||
->get();
|
||||
$this->output->newLine(3);
|
||||
$this->info('========== Step 2/4 - Updating is_cacheable entries in database. ==========');
|
||||
|
||||
$trackFileCount = 0;
|
||||
$formats = [];
|
||||
|
||||
// Set above files to cacheable in the database
|
||||
foreach ($trackFiles as $trackFile) {
|
||||
// Let user know which formats, previously not cached, were made cacheable
|
||||
$formats[] = $trackFile->format;
|
||||
// Find track files which are meant to be cacheable and NOT master, but currently not cacheable
|
||||
TrackFile::where('is_cacheable', false)
|
||||
->whereIn('format', Track::$CacheableFormats)
|
||||
->where('is_master', false)
|
||||
->chunk(200, function ($trackFiles) use (&$trackFileCount, &$formats) {
|
||||
$this->output->newLine(1);
|
||||
$this->info('---------- Start Chunk ----------');
|
||||
|
||||
$trackFile->expiration = null;
|
||||
$trackFile->is_cacheable = true;
|
||||
$trackFile->update();
|
||||
}
|
||||
// Set above files to cacheable in the database
|
||||
foreach ($trackFiles as $trackFile) {
|
||||
$trackFileCount++;
|
||||
|
||||
// Let user know which formats, previously not cached, were made cacheable
|
||||
$formats[] = $trackFile->format;
|
||||
|
||||
$trackFile->expires_at = null;
|
||||
$trackFile->is_cacheable = true;
|
||||
$trackFile->update();
|
||||
}
|
||||
|
||||
$this->info('----------- End Chunk -----------');
|
||||
$this->output->newLine(1);
|
||||
});
|
||||
|
||||
$this->info('Format(s) set from non-cacheable to cacheable: ' . implode(' ', array_unique($formats)));
|
||||
$this->info(count($trackFiles) . ' non-cacheable tracks set to cacheable.');
|
||||
$this->info($trackFileCount . ' non-cacheable track files set to cacheable.');
|
||||
|
||||
$this->output->newLine(2);
|
||||
|
||||
//==========================================================================================================
|
||||
// Update the database entries for cacheable files - cacheable to non-cacheable
|
||||
// Update the database entries for cacheable track files - cacheable to non-cacheable
|
||||
//==========================================================================================================
|
||||
|
||||
// Find files which are NOT meant to be cacheable, but currently cacheable
|
||||
$trackFiles = TrackFile::where('is_cacheable', true)
|
||||
->whereNotIn('format', array_keys(Track::$CacheableFormats))
|
||||
->get();
|
||||
|
||||
$trackFileCount = 0;
|
||||
$formats = [];
|
||||
|
||||
// Set above files to non-cacheable in the database
|
||||
foreach ($trackFiles as $trackFile) {
|
||||
// Let user know which formats, previously not cached, were made cacheable
|
||||
$formats[] = $trackFile->format;
|
||||
// Chunk track files which are NOT meant to be cacheable, but currently cacheable
|
||||
TrackFile::where('is_cacheable', true)
|
||||
->whereNotIn('format', Track::$CacheableFormats)
|
||||
->chunk(200, function ($trackFiles) use (&$trackFileCount, &$formats) {
|
||||
$this->output->newLine(1);
|
||||
$this->info('---------- Start Chunk ----------');
|
||||
|
||||
// Set chunked track files to non-cacheable in the database
|
||||
foreach ($trackFiles as $trackFile) {
|
||||
$trackFileCount++;
|
||||
|
||||
// Let user know which formats, previously not cached, were made cacheable
|
||||
$formats[] = $trackFile->format;
|
||||
|
||||
$trackFile->expires_at = null;
|
||||
$trackFile->is_cacheable = false;
|
||||
$trackFile->update();
|
||||
}
|
||||
|
||||
$this->info('----------- End Chunk -----------');
|
||||
$this->output->newLine(1);
|
||||
$this->output->newLine(1);
|
||||
});
|
||||
|
||||
$trackFile->expiration = null;
|
||||
$trackFile->is_cacheable = false;
|
||||
$trackFile->update();
|
||||
}
|
||||
|
||||
$this->info('Format(s) set from cacheable to non-cacheable: ' . implode(' ', array_unique($formats)));
|
||||
$this->info(count($trackFiles) . ' cacheable tracks set to non-cacheable.');
|
||||
$this->info($trackFileCount . ' cacheable track files set to non-cacheable.');
|
||||
|
||||
//==========================================================================================================
|
||||
// Delete files which have now been marked as cacheable
|
||||
// Delete track files which have now been marked as cacheable
|
||||
//==========================================================================================================
|
||||
|
||||
$this->info('--- Step 3/4 - Deleting now-cacheable files. ---');
|
||||
$this->output->newLine(3);
|
||||
$this->info('========== Step 3/4 - Deleting now-cacheable track files. ==========');
|
||||
|
||||
// Find files which are cacheable and NOT master
|
||||
$trackFiles = TrackFile::whereIn('format', array_keys(Track::$CacheableFormats))
|
||||
$count = 0;
|
||||
$trackFileCount = 0;
|
||||
|
||||
// Find track files which are cacheable and NOT master
|
||||
TrackFile::whereIn('format', Track::$CacheableFormats)
|
||||
->where('is_master', false)
|
||||
->get();
|
||||
->chunk(200, function ($trackFiles) use (&$count, &$trackFileCount) {
|
||||
$this->output->newLine(1);
|
||||
$this->info('---------- Start Chunk ----------');
|
||||
|
||||
foreach ($trackFiles as $trackFile) {
|
||||
$trackFileCount++;
|
||||
|
||||
// Delete track files if track files exist; double-check that they are NOT master files
|
||||
if (File::exists($trackFile->getFile()) && $trackFile->is_master == false) {
|
||||
$count++;
|
||||
|
||||
File::delete($trackFile->getFile());
|
||||
$this->info('Deleted ' . $trackFile->getFile());
|
||||
}
|
||||
}
|
||||
|
||||
$this->info('----------- End Chunk -----------');
|
||||
$this->output->newLine(1);
|
||||
});
|
||||
|
||||
|
||||
$this->info(sprintf('%d track files deleted out of %d track files. Continuing.', $count, $trackFileCount));
|
||||
|
||||
//==========================================================================================================
|
||||
// Encode missing (i.e., now non-cacheable) track files
|
||||
//==========================================================================================================
|
||||
|
||||
$this->output->newLine(3);
|
||||
$this->info('========== Step 4/4 - Encoding missing track files. ==========');
|
||||
|
||||
$count = 0;
|
||||
|
||||
// Chunk non-cacheable track files
|
||||
TrackFile::where('is_cacheable', false)->chunk(200, function ($trackFiles) use (&$count) {
|
||||
$this->output->newLine(1);
|
||||
$this->info('---------- Start Chunk ----------');
|
||||
|
||||
// Record the track files which do not exist (i.e., have not been encoded yet)
|
||||
$emptyTrackFiles = [];
|
||||
|
||||
// Delete above files
|
||||
if (count($trackFiles) == 0) {
|
||||
$this->info('No tracks to delete found. Continuing.');
|
||||
} else {
|
||||
$count = 0;
|
||||
foreach ($trackFiles as $trackFile) {
|
||||
// Delete files if files exist; double-check that they are NOT master files
|
||||
if (File::exists($trackFile->getFile()) && $trackFile->is_master == false) {
|
||||
if (!File::exists($trackFile->getFile())) {
|
||||
$count++;
|
||||
File::delete($trackFile->getFile());
|
||||
$this->info('Deleted ' . $trackFile->getFile());
|
||||
$emptyTrackFiles[] = $trackFile;
|
||||
}
|
||||
}
|
||||
$this->info(sprintf('%d files deleted out of %d tracks. Continuing.', $count, count($trackFiles)));
|
||||
}
|
||||
|
||||
//==========================================================================================================
|
||||
// Encode missing (i.e., now non-cacheable) files
|
||||
//==========================================================================================================
|
||||
|
||||
$this->info('--- Step 4/4 - Encoding missing files. ---');
|
||||
|
||||
// Get non-cacheable files
|
||||
$trackFiles = TrackFile::where('is_cacheable', false)->get();
|
||||
|
||||
// Record the above files which do not exist (i.e., have not been encoded yet)
|
||||
$emptyTrackFiles = [];
|
||||
$count = 0;
|
||||
foreach ($trackFiles as $trackFile) {
|
||||
if (!File::exists($trackFile->getFile())) {
|
||||
$count++;
|
||||
$emptyTrackFiles[] = $trackFile;
|
||||
// Encode recorded track files
|
||||
foreach ($emptyTrackFiles as $emptyTrackFile) {
|
||||
$this->info("Started encoding track file ID {$emptyTrackFile->id}");
|
||||
$this->dispatch(new EncodeTrackFile($emptyTrackFile, false));
|
||||
}
|
||||
}
|
||||
|
||||
// Encode recorded files
|
||||
foreach ($emptyTrackFiles as $emptyTrackFile) {
|
||||
$this->dispatch(new EncodeTrackFile($emptyTrackFile, false));
|
||||
}
|
||||
$this->info('----------- End Chunk -----------');
|
||||
$this->output->newLine(1);
|
||||
});
|
||||
|
||||
$this->info($count . ' tracks encoded.');
|
||||
|
||||
$this->call('up');
|
||||
$this->info($count . ' track files encoded.');
|
||||
$this->output->newLine(1);
|
||||
|
||||
$this->info('Rebuild complete. Exiting.');
|
||||
|
||||
} else {
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
namespace Poniverse\Ponyfm\Http\Controllers\Api\Web;
|
||||
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Poniverse\Ponyfm\Album;
|
||||
use Poniverse\Ponyfm\Commands\CreateAlbumCommand;
|
||||
use Poniverse\Ponyfm\Commands\DeleteAlbumCommand;
|
||||
|
@ -90,43 +91,26 @@ class AlbumsController extends ApiControllerBase
|
|||
{
|
||||
// Validation
|
||||
try {
|
||||
$album = Album::findOrFail($id);
|
||||
$album = Album::with('tracks.trackFiles')->findOrFail($id);
|
||||
} catch (ModelNotFoundException $e) {
|
||||
return $this->notFound('Album not found!');
|
||||
}
|
||||
|
||||
if (!array_key_exists($format, Track::$CacheableFormats)) {
|
||||
if (!in_array($format, Track::$CacheableFormats)) {
|
||||
return $this->notFound('Format not found!');
|
||||
}
|
||||
|
||||
$tracks = $album->tracks;
|
||||
|
||||
$trackCount = 0;
|
||||
$cachedCount = 0;
|
||||
|
||||
foreach($tracks as $track) {
|
||||
if ($track->is_downloadable == false) {
|
||||
continue;
|
||||
} else {
|
||||
$trackCount++;
|
||||
}
|
||||
|
||||
try {
|
||||
$trackFile = $track->trackFiles()->where('format', $format)->firstOrFail();
|
||||
} catch (ModelNotFoundException $e) {
|
||||
return $this->notFound('Track file for track ID ' . $track->id . ' not found!');
|
||||
}
|
||||
|
||||
if ($trackFile->expiration != null) {
|
||||
$cachedCount++;
|
||||
} elseif ($trackFile->in_progress != true) {
|
||||
$this->dispatch(new EncodeTrackFile($trackFile, true));
|
||||
}
|
||||
$trackCount = $album->countDownloadableTracks();
|
||||
try {
|
||||
$cachedCount = $album->countCacheableTrackFiles($format);
|
||||
} catch (ModelNotFoundException $e) {
|
||||
return $this->notFound('Track file in album not found!');
|
||||
}
|
||||
|
||||
if ($trackCount === $cachedCount) {
|
||||
$url = $album->getDownloadUrl($format);
|
||||
} else {
|
||||
$album->encodeCacheableTrackFiles($format);
|
||||
$url = null;
|
||||
}
|
||||
|
||||
|
|
|
@ -85,7 +85,10 @@ class TracksController extends ApiControllerBase
|
|||
if (!$track->canView(Auth::user()))
|
||||
return $this->notFound('Track not found!');
|
||||
|
||||
if (!in_array($format, array_keys(Track::$CacheableFormats))) {
|
||||
if ($track->is_downloadable == false)
|
||||
return $this->notFound('Track not found!');
|
||||
|
||||
if (!in_array($format, Track::$CacheableFormats)) {
|
||||
return $this->notFound('Format not found!');
|
||||
}
|
||||
|
||||
|
@ -96,9 +99,9 @@ class TracksController extends ApiControllerBase
|
|||
}
|
||||
|
||||
// Return URL or begin encoding
|
||||
if ($trackFile->expiration != null && File::exists($trackFile->getFile())) {
|
||||
if ($trackFile->expires_at != null && File::exists($trackFile->getFile())) {
|
||||
$url = $track->getUrlFor($format);
|
||||
} elseif ($trackFile->in_progress === true) {
|
||||
} elseif ($trackFile->is_in_progress === true) {
|
||||
$url = null;
|
||||
} else {
|
||||
$this->dispatch(new EncodeTrackFile($trackFile, true));
|
||||
|
|
|
@ -65,10 +65,10 @@ class EncodeTrackFile extends Job implements SelfHandling, ShouldQueue
|
|||
public function handle()
|
||||
{
|
||||
// Start the job
|
||||
$this->trackFile->in_progress = true;
|
||||
$this->trackFile->is_in_progress = true;
|
||||
$this->trackFile->update();
|
||||
|
||||
// Assign the source to the track's master file
|
||||
// Use the track's master file as the source
|
||||
$source = TrackFile::where('track_id', $this->trackFile->track_id)
|
||||
->where('is_master', true)
|
||||
->first()
|
||||
|
@ -77,7 +77,7 @@ class EncodeTrackFile extends Job implements SelfHandling, ShouldQueue
|
|||
// Assign the target
|
||||
$destination = $this->trackFile->track->getDirectory();
|
||||
$this->trackFile->track->ensureDirectoryExists();
|
||||
$target = $destination . '/' . $this->trackFile->getFilename();
|
||||
$target = $this->trackFile->getFile();
|
||||
|
||||
// Prepare the command
|
||||
$format = Track::$Formats[$this->trackFile->format];
|
||||
|
@ -100,21 +100,20 @@ class EncodeTrackFile extends Job implements SelfHandling, ShouldQueue
|
|||
}
|
||||
|
||||
// Update the tags of the track
|
||||
$this->trackFile->track->updateTags();
|
||||
$this->trackFile->track->updateTags($this->trackFile->format);
|
||||
|
||||
// Insert the expiration time for cached tracks
|
||||
if ($this->isExpirable) {
|
||||
$this->trackFile->expiration = Carbon::now()->addMinutes(Config::get('ponyfm.cache_duration'));
|
||||
$this->trackFile->expires_at = Carbon::now()->addMinutes(Config::get('ponyfm.track_file_cache_duration'));
|
||||
$this->trackFile->update();
|
||||
}
|
||||
|
||||
// Complete the job
|
||||
$this->trackFile->in_progress = false;
|
||||
$this->trackFile->update();
|
||||
|
||||
// Update file size
|
||||
$this->trackFile->updateFilesize();
|
||||
|
||||
// Complete the job
|
||||
$this->trackFile->is_in_progress = false;
|
||||
$this->trackFile->update();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -124,8 +123,8 @@ class EncodeTrackFile extends Job implements SelfHandling, ShouldQueue
|
|||
*/
|
||||
public function failed()
|
||||
{
|
||||
$this->trackFile->in_progress = false;
|
||||
$this->trackFile->expiration = null;
|
||||
$this->trackFile->is_in_progress = false;
|
||||
$this->trackFile->expires_at = null;
|
||||
$this->trackFile->update();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,20 +20,20 @@
|
|||
|
||||
namespace Poniverse\Ponyfm;
|
||||
|
||||
use Poniverse\Ponyfm\Traits\SlugTrait;
|
||||
use Auth;
|
||||
use Cache;
|
||||
use Config;
|
||||
use DB;
|
||||
use Exception;
|
||||
use External;
|
||||
use getid3_writetags;
|
||||
use Helpers;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Auth;
|
||||
use Cache;
|
||||
use Config;
|
||||
use DB;
|
||||
use Log;
|
||||
use URL;
|
||||
use Illuminate\Support\Str;
|
||||
use Log;
|
||||
use Poniverse\Ponyfm\Traits\SlugTrait;
|
||||
use URL;
|
||||
|
||||
class Track extends Model
|
||||
{
|
||||
|
@ -94,29 +94,14 @@ class Track extends Model
|
|||
];
|
||||
|
||||
public static $CacheableFormats = [
|
||||
'OGG Vorbis' => [
|
||||
'index' => 2,
|
||||
'is_lossless' => false,
|
||||
'extension' => 'ogg',
|
||||
'tag_format' => 'vorbiscomment',
|
||||
'tag_method' => 'updateTagsWithGetId3',
|
||||
'mime_type' => 'audio/ogg',
|
||||
'command' => 'ffmpeg 2>&1 -y -i {$source} -acodec libvorbis -aq 7 -f ogg {$target}'
|
||||
],
|
||||
'ALAC' => [
|
||||
'index' => 4,
|
||||
'is_lossless' => true,
|
||||
'extension' => 'alac.m4a',
|
||||
'tag_format' => 'AtomicParsley',
|
||||
'tag_method' => 'updateTagsWithAtomicParsley',
|
||||
'mime_type' => 'audio/mp4',
|
||||
'command' => 'ffmpeg 2>&1 -y -i {$source} -acodec alac {$target}'
|
||||
],
|
||||
'OGG Vorbis',
|
||||
'ALAC',
|
||||
];
|
||||
|
||||
public static function summary()
|
||||
{
|
||||
return self::select('tracks.id', 'title', 'user_id', 'slug', 'is_vocal', 'is_explicit', 'created_at', 'published_at',
|
||||
return self::select('tracks.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', 'comment_count',
|
||||
'download_count', 'view_count', 'play_count', 'favourite_count');
|
||||
}
|
||||
|
@ -269,41 +254,41 @@ class Track extends Model
|
|||
$userRow = $track->users[0];
|
||||
$userData = [
|
||||
'stats' => [
|
||||
'views' => (int) $userRow->view_count,
|
||||
'plays' => (int) $userRow->play_count,
|
||||
'views' => (int)$userRow->view_count,
|
||||
'plays' => (int)$userRow->play_count,
|
||||
'downloads' => $userRow->download_count,
|
||||
],
|
||||
'is_favourited' => (bool) $userRow->is_favourited
|
||||
'is_favourited' => (bool)$userRow->is_favourited
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'id' => (int) $track->id,
|
||||
'id' => (int)$track->id,
|
||||
'title' => $track->title,
|
||||
'user' => [
|
||||
'id' => (int) $track->user->id,
|
||||
'id' => (int)$track->user->id,
|
||||
'name' => $track->user->display_name,
|
||||
'url' => $track->user->url
|
||||
],
|
||||
'stats' => [
|
||||
'views' => (int) $track->view_count,
|
||||
'plays' => (int) $track->play_count,
|
||||
'downloads' => (int) $track->download_count,
|
||||
'comments' => (int) $track->comment_count,
|
||||
'favourites' => (int) $track->favourite_count
|
||||
'views' => (int)$track->view_count,
|
||||
'plays' => (int)$track->play_count,
|
||||
'downloads' => (int)$track->download_count,
|
||||
'comments' => (int)$track->comment_count,
|
||||
'favourites' => (int)$track->favourite_count
|
||||
],
|
||||
'url' => $track->url,
|
||||
'slug' => $track->slug,
|
||||
'is_vocal' => (bool) $track->is_vocal,
|
||||
'is_explicit' => (bool) $track->is_explicit,
|
||||
'is_downloadable' => (bool) $track->is_downloadable,
|
||||
'is_published' => (bool) $track->isPublished(),
|
||||
'is_vocal' => (bool)$track->is_vocal,
|
||||
'is_explicit' => (bool)$track->is_explicit,
|
||||
'is_downloadable' => (bool)$track->is_downloadable,
|
||||
'is_published' => (bool)$track->isPublished(),
|
||||
'published_at' => $track->published_at,
|
||||
'duration' => $track->duration,
|
||||
'genre' => $track->genre != null
|
||||
?
|
||||
[
|
||||
'id' => (int) $track->genre->id,
|
||||
'id' => (int)$track->genre->id,
|
||||
'slug' => $track->genre->slug,
|
||||
'name' => $track->genre->name
|
||||
] : null,
|
||||
|
@ -571,11 +556,22 @@ class Track extends Model
|
|||
$this->hash = md5(Helpers::sanitizeInputForHashing($this->user->display_name) . ' - ' . Helpers::sanitizeInputForHashing($this->title));
|
||||
}
|
||||
|
||||
public function updateTags()
|
||||
public function updateTags($trackFileFormat = 'all')
|
||||
{
|
||||
$this->trackFiles()->touch();
|
||||
if ($trackFileFormat === 'all') {
|
||||
foreach ($this->trackFiles as $trackFile) {
|
||||
$this->updateTagsForTrackFile($trackFile);
|
||||
}
|
||||
} else {
|
||||
$trackFile = $this->trackFiles()->where('format', $trackFileFormat)->firstOrFail();
|
||||
$this->updateTagsForTrackFile($trackFile);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->trackFiles as $trackFile) {
|
||||
private function updateTagsForTrackFile($trackFile) {
|
||||
$trackFile->touch();
|
||||
|
||||
if (\File::exists($trackFile->getFile())) {
|
||||
$format = $trackFile->format;
|
||||
$data = self::$Formats[$format];
|
||||
|
||||
|
@ -654,10 +650,12 @@ class Track extends Model
|
|||
|
||||
if ($tagWriter->WriteTags()) {
|
||||
if (!empty($tagWriter->warnings)) {
|
||||
Log::warning('Track #'.$this->id.': There were some warnings:<br />' . implode('<br /><br />', $tagWriter->warnings));
|
||||
Log::warning('Track #' . $this->id . ': There were some warnings:<br />' . implode('<br /><br />',
|
||||
$tagWriter->warnings));
|
||||
}
|
||||
} else {
|
||||
Log::error('Track #' . $this->id . ': Failed to write tags!<br />' . implode('<br /><br />', $tagWriter->errors));
|
||||
Log::error('Track #' . $this->id . ': Failed to write tags!<br />' . implode('<br /><br />',
|
||||
$tagWriter->errors));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -139,5 +139,7 @@ class TrackFile extends Model
|
|||
|
||||
$this->filesize = $size;
|
||||
$this->update();
|
||||
|
||||
return $size;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,10 +59,10 @@ return [
|
|||
| Cache Duration
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Duration in minutes for tracks to be stored in cache.
|
||||
| Duration in minutes for track files to be stored in cache.
|
||||
|
|
||||
*/
|
||||
|
||||
'cache_duration' => 1440,
|
||||
'track_file_cache_duration' => 1440,
|
||||
|
||||
];
|
||||
|
|
|
@ -32,8 +32,8 @@ class UpdateTrackFilesWithCache extends Migration
|
|||
{
|
||||
Schema::table('track_files', function (Blueprint $table) {
|
||||
$table->boolean('is_cacheable')->default(false);
|
||||
$table->boolean('in_progress')->default(false);
|
||||
$table->dateTime('expiration')->nullable();
|
||||
$table->boolean('is_in_progress')->default(false);
|
||||
$table->dateTime('expires_at')->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -18,8 +18,9 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
|
||||
class UpdateTrackFilesWithFilesize extends Migration
|
||||
{
|
||||
|
@ -33,6 +34,11 @@ class UpdateTrackFilesWithFilesize extends Migration
|
|||
Schema::table('track_files', function (Blueprint $table) {
|
||||
$table->integer('filesize')->nullable()->unsigned();
|
||||
});
|
||||
|
||||
// Populate file sizes
|
||||
Artisan::call('rebuild:filesizes', [
|
||||
'--force' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -5,12 +5,18 @@
|
|||
Downloads
|
||||
</a>
|
||||
<ul class="dropdown-menu" ng-show="album.is_downloadable == 1">
|
||||
<li bindonce ng-repeat="format in album.formats" ng-hide="isEncoding">
|
||||
<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="getCachedAlbum(album.id, format.name);" href=""><span bo-text="format.name"></span> <small bo-text="'(' + format.size + ')'"></small></a>
|
||||
<li bindonce ng-repeat="format in album.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="getCachedAlbum(album.id, format.name);" href="">
|
||||
<span bo-text="format.name"></span>
|
||||
<small bo-text="'(' + format.size + ')'"></small>
|
||||
</a>
|
||||
</li>
|
||||
<li ng-show="isEncoding" class="cache-loading"><img src="/images/loading.gif" /></li>
|
||||
<li ng-show="isEncoding" 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"><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>
|
||||
</li>
|
||||
<li><a href="#" class="btn" pfm-eat-click ng-click="share()">Share</a></li>
|
||||
|
|
|
@ -5,12 +5,18 @@
|
|||
Downloads
|
||||
</a>
|
||||
<ul class="dropdown-menu" ng-show="track.is_downloadable == 1">
|
||||
<li bindonce ng-repeat="format in track.formats" ng-hide="isEncoding">
|
||||
<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="getCachedTrack(track.id, format.name);" href=""><span bo-text="format.name"></span> <small bo-text="'(' + format.size + ')'"></small></a>
|
||||
<li bindonce ng-repeat="format in track.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="getCachedTrack(track.id, format.name);" href="">
|
||||
<span bo-text="format.name"></span>
|
||||
<small bo-text="'(' + format.size + ')'"></small>
|
||||
</a>
|
||||
</li>
|
||||
<li ng-show="isEncoding" class="cache-loading"><img src="/images/loading.gif" /></li>
|
||||
<li ng-show="isEncoding" class="cache-loading"><small>We're getting your download ready! This'll take a few seconds.</small></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'll take a few seconds.</small></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="dropdown">
|
||||
|
|
|
@ -42,16 +42,15 @@ angular.module('ponyfm').controller "album", [
|
|||
$scope.playlists.push list for list in lists
|
||||
|
||||
$scope.getCachedAlbum = (id, format) ->
|
||||
$scope.isEncoding = true
|
||||
$scope.isInProgress = true
|
||||
|
||||
cachedAlbum.download('albums', id, format).then (response) ->
|
||||
albumUrl = response
|
||||
$scope.albumUrl = albumUrl
|
||||
if albumUrl == 'error'
|
||||
$scope.isEncoding = false
|
||||
else if albumUrl == 'pending'
|
||||
$scope.albumUrl = response
|
||||
if $scope.albumUrl == 'error'
|
||||
$scope.isInProgress = false
|
||||
else if $scope.albumUrl == 'pending'
|
||||
$timeout $scope.getCachedAlbum(id, format), 5000
|
||||
else
|
||||
$scope.isEncoding = false
|
||||
$window.open albumUrl, '_blank'
|
||||
$scope.isInProgress = false
|
||||
$window.open $scope.albumUrl, '_blank'
|
||||
]
|
||||
|
|
|
@ -74,17 +74,15 @@ angular.module('ponyfm').controller "track", [
|
|||
playlist.message = res.message
|
||||
|
||||
$scope.getCachedTrack = (id, format) ->
|
||||
$scope.isEncoding = true
|
||||
$scope.isInProgress = true
|
||||
|
||||
cachedTrack.download('tracks', id, format).then (response) ->
|
||||
trackUrl = response
|
||||
$scope.trackUrl = trackUrl
|
||||
console.log(trackUrl);
|
||||
if trackUrl == 'error'
|
||||
$scope.isEncoding = false
|
||||
else if trackUrl == 'pending'
|
||||
$scope.trackUrl = response
|
||||
if $scope.trackUrl == 'error'
|
||||
$scope.isInProgress = false
|
||||
else if $scope.trackUrl == 'pending'
|
||||
$timeout $scope.getCachedTrack(id, format), 5000
|
||||
else
|
||||
$scope.isEncoding = false
|
||||
$window.open trackUrl, '_blank'
|
||||
$scope.isInProgress = false
|
||||
$window.open $scope.trackUrl, '_blank'
|
||||
]
|
||||
|
|
|
@ -18,7 +18,7 @@ angular.module('ponyfm').factory('download-cached', [
|
|||
'$rootScope', '$http', '$log'
|
||||
($rootScope, $http, $log) ->
|
||||
download = (type, id, format) ->
|
||||
url = '/api/web/' + type + '/cached/' + id + '/' + format
|
||||
url = "/api/web/#{type}/cached/#{id}/#{format}"
|
||||
|
||||
encodingComplete = (response) ->
|
||||
if response.data.url == null
|
||||
|
|
1
resources/assets/styles/content.less
vendored
1
resources/assets/styles/content.less
vendored
|
@ -360,6 +360,7 @@ html {
|
|||
background-color: lighten(@pfm-purple, 30%);
|
||||
}
|
||||
}
|
||||
|
||||
.cache-loading {
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
|
|
Loading…
Reference in a new issue