Documented the Track model's functions

This commit is contained in:
Peter Deltchev 2017-09-22 04:42:59 -07:00
parent b84669c3f1
commit 8eaa22733b

View file

@ -135,12 +135,16 @@ class Track extends Model implements Searchable, Commentable, Favouritable
use RevisionableTrait; use RevisionableTrait;
// Used for the track's upload status. // Used for the track's post-upload status. See UploadTrackCommand.
const STATUS_COMPLETE = 0; const STATUS_COMPLETE = 0;
const STATUS_PROCESSING = 1; const STATUS_PROCESSING = 1;
const STATUS_ERROR = 2; const STATUS_ERROR = 2;
/**
* All the information about how to encode files into Pony.fm's various formats
*
* @var array
*/
public static $Formats = [ public static $Formats = [
'FLAC' => [ 'FLAC' => [
'index' => 0, 'index' => 0,
@ -206,11 +210,27 @@ class Track extends Model implements Searchable, Commentable, Favouritable
'AAC' 'AAC'
]; ];
/**
* Formats in this file are treated specially by Pony.fm as lossless when,
* for example, generating playlist and album downloads that contain a mix
* of lossy and lossless tracks.
*
* The strings in this array must match keys in the `Track::$Formats` array.
*
* @var array
*/
public static $LosslessFormats = [ public static $LosslessFormats = [
'FLAC', 'FLAC',
'ALAC' 'ALAC'
]; ];
/**
* Prepares a query builder object with typical fields used for a track
* "tile" around the site and eager-loaded relations that are almost always
* relevant.
*
* @return \Illuminate\Database\Eloquent\Builder
*/
public static function summary() public static function summary()
{ {
return self::select( return self::select(
@ -237,6 +257,11 @@ class Track extends Model implements Searchable, Commentable, Favouritable
->with('user', 'cover', 'album'); ->with('user', 'cover', 'album');
} }
/**
* If a user is currently logged in, this scope eager-loads them.
*
* @param $query
*/
public function scopeUserDetails($query) public function scopeUserDetails($query)
{ {
if (Auth::check()) { if (Auth::check()) {
@ -248,16 +273,31 @@ class Track extends Model implements Searchable, Commentable, Favouritable
} }
} }
/**
* Limits the query scope to published tracks.
*
* @param $query
*/
public function scopePublished($query) public function scopePublished($query)
{ {
$query->whereNotNull('published_at'); $query->whereNotNull('published_at');
} }
/**
* Limits the query scope to listed tracks.
*
* @param $query
*/
public function scopeListed($query) public function scopeListed($query)
{ {
$query->whereIsListed(true); $query->whereIsListed(true);
} }
/**
* Applies the NSFW filter: only allow explicit tracks to come back for
* this query if a user is logged in and has opted in to them.
* @param $query
*/
public function scopeExplicitFilter($query) public function scopeExplicitFilter($query)
{ {
if (!Auth::check() || !Auth::user()->can_see_explicit_content) { if (!Auth::check() || !Auth::user()->can_see_explicit_content) {
@ -265,6 +305,11 @@ class Track extends Model implements Searchable, Commentable, Favouritable
} }
} }
/**
* Eager-loads track comments with this query.
*
* @param $query
*/
public function scopeWithComments($query) public function scopeWithComments($query)
{ {
$query->with([ $query->with([
@ -285,7 +330,12 @@ class Track extends Model implements Searchable, Commentable, Favouritable
} }
/** /**
* @param integer $count * The (messy) query used to generate the list of popular tracks on PFM's
* homepage.
*
* @param integer $count the number of tracks to return
* @param bool $allowExplicit whether to include explicit tracks in the results
* @param int $skip currently unused as of 2017-09-22
* @return array * @return array
*/ */
public static function popular($count, $allowExplicit = false, $skip = 0) public static function popular($count, $allowExplicit = false, $skip = 0)
@ -393,6 +443,13 @@ class Track extends Model implements Searchable, Commentable, Favouritable
return $processed; return $processed;
} }
/**
* Brings together all the data displayed on a track page.
* The data structure this returns is what the web API spits out.
*
* @param Track $track
* @return array
*/
public static function mapPublicTrackShow(Track $track) public static function mapPublicTrackShow(Track $track)
{ {
$returnValue = self::mapPublicTrackSummary($track); $returnValue = self::mapPublicTrackSummary($track);
@ -440,6 +497,13 @@ class Track extends Model implements Searchable, Commentable, Favouritable
return $returnValue; return $returnValue;
} }
/**
* Brings together all the data shown on "track tiles" throughout the site.
* The data structure this returns is what the web API spits out.
*
* @param Track $track
* @return array
*/
public static function mapPublicTrackSummary(Track $track) public static function mapPublicTrackSummary(Track $track)
{ {
$userData = [ $userData = [
@ -513,6 +577,13 @@ class Track extends Model implements Searchable, Commentable, Favouritable
]; ];
} }
/**
* Brings together the data about a track used by the track editor.
* The data structure this returns is what the web API spits out.
*
* @param Track $track
* @return array
*/
public static function mapPrivateTrackShow(Track $track) public static function mapPrivateTrackShow(Track $track)
{ {
$showSongs = []; $showSongs = [];
@ -541,6 +612,15 @@ class Track extends Model implements Searchable, Commentable, Favouritable
return $returnValue; return $returnValue;
} }
/**
* Brings together the data shown for a "track tile" in a user's account
* area (where they'd manage unlisted and unpublished tracks and the admin
* area.
*
* The data structure this returns is what the web API spits out.
* @param Track $track
* @return array
*/
public static function mapPrivateTrackSummary(Track $track) public static function mapPrivateTrackSummary(Track $track)
{ {
return [ return [
@ -664,6 +744,12 @@ class Track extends Model implements Searchable, Commentable, Favouritable
} }
} }
/**
* Returns true if the given user can view this track.
*
* @param $user
* @return bool
*/
public function canView($user) public function canView($user)
{ {
if ($this->isPublished() || $user->hasRole('admin')) { if ($this->isPublished() || $user->hasRole('admin')) {
@ -673,11 +759,24 @@ class Track extends Model implements Searchable, Commentable, Favouritable
return $this->user_id == $user->id; return $this->user_id == $user->id;
} }
/**
* Magic method to create a "url" attribute on Track objects that contains
* the track's canonical URL.
*
* @return string
*/
public function getUrlAttribute() public function getUrlAttribute()
{ {
return action('TracksController@getTrack', ['id' => $this->id, 'slug' => $this->slug]); return action('TracksController@getTrack', ['id' => $this->id, 'slug' => $this->slug]);
} }
/**
* Magic method to create a "downloadDirectory" attribute on Track objects
* that contains the directory path it should show up in inside downloaded
* TrackCollection zip files.
*
* @return string
*/
public function getDownloadDirectoryAttribute() public function getDownloadDirectoryAttribute()
{ {
if ($this->album) { if ($this->album) {
@ -687,6 +786,12 @@ class Track extends Model implements Searchable, Commentable, Favouritable
return $this->user->display_name; return $this->user->display_name;
} }
/**
* Returns a track's release date, heuristically generating a plausible one
* if it is unknown.
*
* @return \Carbon\Carbon|string
*/
public function getReleaseDate() public function getReleaseDate()
{ {
if ($this->released_at !== null) { if ($this->released_at !== null) {
@ -700,6 +805,10 @@ class Track extends Model implements Searchable, Commentable, Favouritable
return Str::limit($this->attributes['created_at'], 10, ''); return Str::limit($this->attributes['created_at'], 10, '');
} }
/**
* Ensures that the numbered directory this track's files would be stored in
* server-side exists, creating it if needed.
*/
public function ensureDirectoryExists() public function ensureDirectoryExists()
{ {
$destination = $this->getDirectory(); $destination = $this->getDirectory();
@ -710,32 +819,58 @@ class Track extends Model implements Searchable, Commentable, Favouritable
} }
} }
public function hasCover() /**
* @return bool True if the track has an associated cover file.
*/
public function hasCover() : bool
{ {
return $this->cover_id != null; return $this->cover_id != null;
} }
public function isPublished() /**
* @return bool True if the track has been published and not deleted.
*/
public function isPublished() : bool
{ {
return $this->published_at != null && $this->deleted_at == null; return $this->published_at != null && $this->deleted_at == null;
} }
/**
* Returns the TrackFile object that represents the highest-fidelity version
* of this track, which is used as the source file for transcoding to other
* formats.
*
* @return TrackFile
*/
protected function getMasterTrackFile() : TrackFile protected function getMasterTrackFile() : TrackFile
{ {
return $this->trackFiles()->where('is_master', true)->first(); return $this->trackFiles()->where('is_master', true)->first();
} }
/**
* The key in `$Formats` above for the master file's format.
*
* @return string
*/
public function getMasterFormatName() : string public function getMasterFormatName() : string
{ {
return $this->getMasterTrackFile()->format; return $this->getMasterTrackFile()->format;
} }
/**
* @return bool True if this track's master file is a lossy one.
*/
public function isMasterLossy() : bool public function isMasterLossy() : bool
{ {
return $this->getMasterTrackFile()->isLossy(); return $this->getMasterTrackFile()->isLossy();
} }
/**
* Returns the URL to this track's cover art in the given size.
*
* @param int $type one of the image size constants in the `Image` class
* @return string
*/
public function getCoverUrl($type = Image::NORMAL) public function getCoverUrl($type = Image::NORMAL)
{ {
if (!$this->hasCover()) { if (!$this->hasCover()) {
@ -749,11 +884,24 @@ class Track extends Model implements Searchable, Commentable, Favouritable
return $this->cover->getUrl($type); return $this->cover->getUrl($type);
} }
/**
* Returns the "stream" URL for this track in the given format. This is to
* be used by the on-site player.
*
* @param string $format one of the format keys from the `$Formats` array
* @return string
*/
public function getStreamUrl($format = 'MP3') public function getStreamUrl($format = 'MP3')
{ {
return action('TracksController@getStream', ['id' => $this->id, 'extension' => self::$Formats[$format]['extension']]); return action('TracksController@getStream', ['id' => $this->id, 'extension' => self::$Formats[$format]['extension']]);
} }
/**
* Returns the absolute path to the server-side directory that this track's
* audio files are stored in.
*
* @return string
*/
public function getDirectory() public function getDirectory()
{ {
$dir = (string) (floor($this->id / 100) * 100); $dir = (string) (floor($this->id / 100) * 100);
@ -766,7 +914,15 @@ class Track extends Model implements Searchable, Commentable, Favouritable
return ['created_at', 'deleted_at', 'published_at', 'released_at']; return ['created_at', 'deleted_at', 'published_at', 'released_at'];
} }
public function getFilenameFor($format) /**
* Returns the server-side filename (not path) that a current TrackFile in
* the given format would have.
*
* @param string $format one of the format keys from the `$Formats` array
* @return string a filename
* @throws Exception
*/
public function getFilenameFor(string $format) : string
{ {
if (!isset(self::$Formats[$format])) { if (!isset(self::$Formats[$format])) {
throw new Exception("$format is not a valid format!"); throw new Exception("$format is not a valid format!");
@ -777,7 +933,15 @@ class Track extends Model implements Searchable, Commentable, Favouritable
return "{$this->id}-v{$this->current_version}.{$format['extension']}"; return "{$this->id}-v{$this->current_version}.{$format['extension']}";
} }
public function getDownloadFilenameFor($format) /**
* Returns the filename that this track would have in the given format when
* downloaded by a user.
*
* @param string $format one of the format keys from the `$Formats` array
* @return string
* @throws Exception
*/
public function getDownloadFilenameFor(string $format) : string
{ {
if (!isset(self::$Formats[$format])) { if (!isset(self::$Formats[$format])) {
throw new Exception("$format is not a valid format!"); throw new Exception("$format is not a valid format!");
@ -789,11 +953,13 @@ class Track extends Model implements Searchable, Commentable, Favouritable
} }
/** /**
* @param $format * Returns the absolute path to this track's file in a given format.
* @return string *
* @param string $format one of the format keys from the `$Formats` array
* @return string absolute path to a track file
* @throws Exception * @throws Exception
*/ */
public function getFileFor($format) public function getFileFor(string $format) : string
{ {
if (!isset(self::$Formats[$format])) { if (!isset(self::$Formats[$format])) {
throw new Exception("$format is not a valid format!"); throw new Exception("$format is not a valid format!");
@ -817,8 +983,14 @@ class Track extends Model implements Searchable, Commentable, Favouritable
return Config::get('ponyfm.files_directory').'/queued-tracks/'.$this->id.'v'.$version; return Config::get('ponyfm.files_directory').'/queued-tracks/'.$this->id.'v'.$version;
} }
/**
public function getUrlFor($format) * Returns the URL to download a track file from in the given format.
*
* @param string $format one of the format keys from the `$Formats` array
* @return string
* @throws Exception
*/
public function getUrlFor(string $format) : string
{ {
if (!isset(self::$Formats[$format])) { if (!isset(self::$Formats[$format])) {
throw new Exception("$format is not a valid format!"); throw new Exception("$format is not a valid format!");
@ -831,9 +1003,9 @@ class Track extends Model implements Searchable, Commentable, Favouritable
/** /**
* @return string one of the Track::STATUS_* values, indicating whether this track is currently being processed * @return int one of the Track::STATUS_* values, indicating whether this track is currently being processed
*/ */
public function getStatusAttribute() public function getStatusAttribute():int
{ {
return $this->trackFiles->reduce(function ($carry, $trackFile) { return $this->trackFiles->reduce(function ($carry, $trackFile) {
if ((int) $trackFile->status === TrackFile::STATUS_PROCESSING_ERROR) { if ((int) $trackFile->status === TrackFile::STATUS_PROCESSING_ERROR) {
@ -851,12 +1023,20 @@ class Track extends Model implements Searchable, Commentable, Favouritable
}, static::STATUS_COMPLETE); }, static::STATUS_COMPLETE);
} }
/**
* Updates this track's hash of the artist name and track title.
*/
public function updateHash() public function updateHash()
{ {
$this->hash = md5(Helpers::sanitizeInputForHashing($this->user->display_name).' - '.Helpers::sanitizeInputForHashing($this->title)); $this->hash = md5(Helpers::sanitizeInputForHashing($this->user->display_name).' - '.Helpers::sanitizeInputForHashing($this->title));
} }
/**
* Ensures that the metadata for this track's file in the given format is
* up to date.
* @param string $trackFileFormat one of the format keys from the `$Formats` array OR
* 'all' to indicate the operation should be performed on all formats
*/
public function updateTags($trackFileFormat = 'all') public function updateTags($trackFileFormat = 'all')
{ {
if ($trackFileFormat === 'all') { if ($trackFileFormat === 'all') {
@ -869,6 +1049,11 @@ class Track extends Model implements Searchable, Commentable, Favouritable
} }
} }
/**
* Runs the command to update the given TrackFile's metadata.
*
* @param TrackFile $trackFile
*/
private function updateTagsForTrackFile(TrackFile $trackFile) private function updateTagsForTrackFile(TrackFile $trackFile)
{ {
$trackFile->touch(); $trackFile->touch();
@ -881,8 +1066,14 @@ class Track extends Model implements Searchable, Commentable, Favouritable
} }
} }
/** @noinspection PhpUnusedPrivateMethodInspection */ /**
private function updateTagsWithAtomicParsley($format) * Writes a TrackFile's tags using AtomicParsley. This is useful for MP4
* files (AAC and ALAC). This function is called from updateTagsForTrackFile.
*
* @noinspection PhpUnusedPrivateMethodInspection
* @param string $format one of the format keys from the `$Formats` array
*/
private function updateTagsWithAtomicParsley(string $format)
{ {
$command = 'AtomicParsley "'.$this->getFileFor($format).'" '; $command = 'AtomicParsley "'.$this->getFileFor($format).'" ';
$command .= '--title '.escapeshellarg($this->title).' '; $command .= '--title '.escapeshellarg($this->title).' ';
@ -907,8 +1098,15 @@ class Track extends Model implements Searchable, Commentable, Favouritable
External::execute($command); External::execute($command);
} }
/** @noinspection PhpUnusedPrivateMethodInspection */
private function updateTagsWithGetId3($format) /**
* Writes a TrackFile's tags using getId3(). This is useful for MP3, FLAC,
* and OGG Vorbis files. This function is called from updateTagsForTrackFile.
*
* @noinspection PhpUnusedPrivateMethodInspection
* @param string $format one of the format keys from the `$Formats` array
*/
private function updateTagsWithGetId3(string $format)
{ {
require_once(app_path().'/Library/getid3/getid3/getid3.php'); require_once(app_path().'/Library/getid3/getid3/getid3.php');
require_once(app_path().'/Library/getid3/getid3/write.php'); require_once(app_path().'/Library/getid3/getid3/write.php');
@ -965,11 +1163,14 @@ class Track extends Model implements Searchable, Commentable, Favouritable
} }
} }
private function getCacheKey($key) private function getCacheKey($key) : string
{ {
return 'track-'.$this->id.'-'.$key; return 'track-'.$this->id.'-'.$key;
} }
/**
* Marks this track and associated notification records as deleted.
*/
public function delete() public function delete()
{ {
DB::transaction(function () { DB::transaction(function () {
@ -1003,12 +1204,21 @@ class Track extends Model implements Searchable, Commentable, Favouritable
]; ];
} }
/**
* Returns the word used to identify this "type" of content in notifications.
* @return string
*/
public function getResourceType():string public function getResourceType():string
{ {
return 'track'; return 'track';
} }
public function getNextVersion() /**
* Returns the next version after this track's current one.
*
* @return int
*/
public function getNextVersion() : int
{ {
return $this->current_version + 1; return $this->current_version + 1;
} }