363 lines
13 KiB
PHP
363 lines
13 KiB
PHP
|
<?php
|
||
|
|
||
|
require_once ("includes/spotifyauth.php");
|
||
|
|
||
|
$numtracks = 0;
|
||
|
$numalbums = 0;
|
||
|
$numartists = 0;
|
||
|
$totaltime = 0;
|
||
|
$count = 1;
|
||
|
$divtype = "album1";
|
||
|
$dbterms = array( 'tags' => null, 'rating' => null );
|
||
|
$trackbytrack = false;
|
||
|
|
||
|
define('ROMPR_MIN_TRACKS_TO_DETERMINE_COMPILATION', 3);
|
||
|
define('ROMPR_MIN_NOT_COMPILATION_THRESHOLD', 0.6);
|
||
|
|
||
|
class musicCollection {
|
||
|
|
||
|
public function __construct() {
|
||
|
$this->albums = array();
|
||
|
$this->filter_duplicates = false;
|
||
|
}
|
||
|
|
||
|
public function newTrack(&$filedata) {
|
||
|
|
||
|
global $trackbytrack, $doing_search;
|
||
|
|
||
|
if ($doing_search) {
|
||
|
// If we're doing a search, we check to see if that track is in the database
|
||
|
// because the user might have set the AlbumArtist to something different
|
||
|
$filedata = array_replace($filedata, get_extra_track_info($filedata));
|
||
|
}
|
||
|
|
||
|
$track = new track($filedata);
|
||
|
if ($trackbytrack && $filedata['AlbumArtist'] && $filedata['Disc'] !== null) {
|
||
|
do_track_by_track( $track );
|
||
|
} else {
|
||
|
$albumkey = md5($track->tags['folder'].strtolower($track->tags['Album']).strtolower($track->get_sort_artist(true)));
|
||
|
if (array_key_exists($albumkey, $this->albums)) {
|
||
|
if (!$this->filter_duplicates || !$this->albums[$albumkey]->checkForDuplicate($track)) {
|
||
|
$this->albums[$albumkey]->newTrack($track);
|
||
|
}
|
||
|
} else {
|
||
|
$this->albums[$albumkey] = new album($track);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
public function getAllTracks($cmd) {
|
||
|
$tracks = array();
|
||
|
foreach($this->albums as $album) {
|
||
|
$tracks = array_merge($album->getAllTracks($cmd), $tracks);
|
||
|
}
|
||
|
return $tracks;
|
||
|
}
|
||
|
|
||
|
public function tracks_to_database() {
|
||
|
// Fluch the previous albumobj from track_by_track
|
||
|
do_track_by_track(null);
|
||
|
foreach ($this->albums as $album) {
|
||
|
$album->sortTracks();
|
||
|
$album->check_database();
|
||
|
}
|
||
|
$this->albums = array();
|
||
|
}
|
||
|
|
||
|
public function get_albumartist_by_folder($f) {
|
||
|
foreach ($this->albums as $album) {
|
||
|
if ($album->folder == $f) {
|
||
|
logger::trace("COLLECTION", " Found albumartist by folder",$album->artist);
|
||
|
return $album->artist;
|
||
|
}
|
||
|
}
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
public function filter_duplicate_tracks() {
|
||
|
$this->filter_duplicates = true;
|
||
|
}
|
||
|
|
||
|
public function tracks_as_array() {
|
||
|
$results = array();
|
||
|
foreach($this->albums as $album) {
|
||
|
logger::log("COLLECTION", "Doing Album",$album->name);
|
||
|
$album->sortTracks();
|
||
|
foreach($album->tracks as $trackobj) {
|
||
|
$track = array(
|
||
|
"uri" => $trackobj->tags['file'],
|
||
|
"album" => $album->name,
|
||
|
"title" => $trackobj->tags['Title'],
|
||
|
"artist" => $trackobj->get_artist_string(),
|
||
|
"albumartist" => $album->artist,
|
||
|
"trackno" => $trackobj->tags['Track'],
|
||
|
"disc" => $trackobj->tags['Disc'],
|
||
|
"albumuri" => $album->uri,
|
||
|
"image" => $album->getImage('asdownloaded'),
|
||
|
"duration" => $trackobj->tags['Time'],
|
||
|
"date" => $album->datestamp
|
||
|
);
|
||
|
logger::log("COLLECTION", "Title - ".$trackobj->tags['Title']);
|
||
|
// A lot of code that depends on this was written to handle mopidy model search results.
|
||
|
// The above is not mopidy model, so friggicate it into just such a thing
|
||
|
$d = getDomain($track['uri']);
|
||
|
if (!array_key_exists($d, $results)) {
|
||
|
logger::log("COLLECTION", "Creating Results Set For ".$d);
|
||
|
$results[$d] = array(
|
||
|
"tracks" => array(),
|
||
|
"uri" => $d.':bodgehack'
|
||
|
);
|
||
|
}
|
||
|
array_push($results[$d]['tracks'], $track);
|
||
|
}
|
||
|
}
|
||
|
return $results;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
class album {
|
||
|
|
||
|
public function __construct(&$track) {
|
||
|
global $numalbums;
|
||
|
$numalbums++;
|
||
|
$this->tracks = array($track);
|
||
|
// Sets album artist to composer (if set and required) or albumartist but NOT trackartist
|
||
|
// therefore may still be null at this point.
|
||
|
$this->artist = $track->get_sort_artist(true);
|
||
|
$this->name = trim($track->tags['Album']);
|
||
|
$this->folder = $track->tags['folder'];
|
||
|
$this->musicbrainz_albumid = $track->tags['MUSICBRAINZ_ALBUMID'];
|
||
|
$this->datestamp = $track->tags['Date'];
|
||
|
$this->uri = $track->tags['X-AlbumUri'];
|
||
|
$this->numOfDiscs = $track->tags['Disc'];
|
||
|
$this->image = $track->tags['X-AlbumImage'];
|
||
|
$this->key = $track->tags['ImgKey'];
|
||
|
$this->numOfDiscs = $track->tags['Disc'];
|
||
|
$this->numOfTrackOnes = $track->tags['Track'] == 1 ? 1 : 0;
|
||
|
$this->domain = $track->tags['domain'];
|
||
|
$this->albumartistindex = null;
|
||
|
$this->albumindex = null;
|
||
|
}
|
||
|
|
||
|
public function newTrack(&$track) {
|
||
|
$this->tracks[] = $track;
|
||
|
if ($this->artist == null) {
|
||
|
$this->artist = $track->get_sort_artist(true);
|
||
|
}
|
||
|
if ($this->image == null) {
|
||
|
$this->image = $track->tags['X-AlbumImage'];
|
||
|
}
|
||
|
if ($this->datestamp == null) {
|
||
|
$this->datestamp = $track->tags['Date'];
|
||
|
}
|
||
|
if ($this->musicbrainz_albumid == '') {
|
||
|
$this->musicbrainz_albumid = $track->tags['MUSICBRAINZ_ALBUMID'];
|
||
|
}
|
||
|
if ($track->tags['Disc'] !== null && $this->numOfDiscs < $track->tags['Disc']) {
|
||
|
$this->numOfDiscs = $track->tags['Disc'];
|
||
|
}
|
||
|
if ($track->tags['Track'] == 1) {
|
||
|
$this->numOfTrackOnes++;
|
||
|
}
|
||
|
if ($this->uri == null) {
|
||
|
$this->uri = $track->tags['X-AlbumUri'];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public function check_database() {
|
||
|
if ($this->albumartistindex == null) {
|
||
|
$this->albumartistindex = check_artist($this->artist);
|
||
|
}
|
||
|
if ($this->albumindex == null) {
|
||
|
$album = array(
|
||
|
'album' => $this->name,
|
||
|
'albumai' => $this->albumartistindex,
|
||
|
'albumuri' => $this->uri,
|
||
|
'image' => $this->getImage('small'),
|
||
|
'date' => $this->getDate(),
|
||
|
'searched' => "0",
|
||
|
'imagekey' => $this->getKey(),
|
||
|
'ambid' => $this->musicbrainz_albumid,
|
||
|
'domain' => $this->domain
|
||
|
);
|
||
|
$this->albumindex = check_album($album);
|
||
|
}
|
||
|
foreach ($this->tracks as $trackobj) {
|
||
|
check_and_update_track($trackobj, $this->albumindex, $this->albumartistindex, $this->artist);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public function getKey() {
|
||
|
return $this->key;
|
||
|
}
|
||
|
|
||
|
public function getImage($size) {
|
||
|
$albumimage = new baseAlbumImage(array(
|
||
|
'baseimage' => ($this->image) ? $this->image : '',
|
||
|
'artist' => artist_for_image($this->tracks[0]->tags['type'], $this->artist),
|
||
|
'album' => $this->name
|
||
|
));
|
||
|
$albumimage->check_image($this->domain, $this->tracks[0]->tags['type']);
|
||
|
$images = $albumimage->get_images();
|
||
|
$this->key = $albumimage->get_image_key();
|
||
|
return $images[$size];
|
||
|
}
|
||
|
|
||
|
public function trackCount() {
|
||
|
return count($this->tracks);
|
||
|
}
|
||
|
|
||
|
public function getDate() {
|
||
|
return getYear($this->datestamp);
|
||
|
}
|
||
|
|
||
|
public function getAllTracks($cmd) {
|
||
|
$tracks = array();
|
||
|
foreach ($this->tracks as $track) {
|
||
|
if (preg_match('/:track:/', $track->tags['file'])) {
|
||
|
$tracks[] = $cmd.' "'.format_for_mpd($track->tags['file']).'"';
|
||
|
}
|
||
|
}
|
||
|
return $tracks;
|
||
|
}
|
||
|
|
||
|
public function sortTracks($always = false) {
|
||
|
|
||
|
// NB. BLOODY WELL CALL THIS FUNCTION
|
||
|
// Unless you're so sure you know how all this works and you really don't need it.
|
||
|
// Collection updates might be one such area but if you're not sure CALL IT ANYWAY and see what happens.
|
||
|
|
||
|
// Some Mopidy backends don't send disc numbers. If we're using the sql backend
|
||
|
// we don't really need to pre-sort tracks because we can do it on the fly.
|
||
|
// However, when there are no disc numbers multi-disc albums don't sort properly.
|
||
|
// Hence we do a little check that we have have the same number of 'Track 1's
|
||
|
// as discs and only do the sort if they're not the same. This'll also
|
||
|
// sort out badly tagged local files. It's essential that disc numbers are set
|
||
|
// because the database will not find the tracks otherwise.
|
||
|
|
||
|
// Also here, because ths gets called always, we try to set our albumartist
|
||
|
// to something sensible. So far it has been set to Composer tags if required by the
|
||
|
// user, or to the AlbumArtist setting, which will be null if no AlbumArtist tag is present -
|
||
|
// as is the case with many mopidy backends
|
||
|
|
||
|
if ($this->artist == null) {
|
||
|
logger::mark("COLLECTION", "Finding AlbumArtist for album ".$this->name);
|
||
|
if (count($this->tracks) < ROMPR_MIN_TRACKS_TO_DETERMINE_COMPILATION) {
|
||
|
logger::log("COLLECTION", " Album ".$this->name." has too few tracks to determine album artist");
|
||
|
$this->decideOnArtist($this->tracks[0]->get_sort_artist());
|
||
|
} else {
|
||
|
$artists = array();
|
||
|
foreach ($this->tracks as $track) {
|
||
|
$a = $track->get_sort_artist();
|
||
|
if (!array_key_exists($a, $artists)) {
|
||
|
$artists[$a] = 0;
|
||
|
}
|
||
|
$artists[$a]++;
|
||
|
}
|
||
|
$q = array_flip($artists);
|
||
|
rsort($q);
|
||
|
$candidate_artist = $q[0];
|
||
|
$fraction = $artists[$candidate_artist]/count($this->tracks);
|
||
|
logger::log("COLLECTION", " Artist ".$candidate_artist." has ".$artists[$candidate_artist]." tracks out of ".count($this->tracks));
|
||
|
if ($fraction > ROMPR_MIN_NOT_COMPILATION_THRESHOLD) {
|
||
|
logger::log("COLLECTION", " ... which is good enough. Album ".$this->name." is by ".$candidate_artist);
|
||
|
$this->artist = $candidate_artist;
|
||
|
} else {
|
||
|
logger::log("COLLECTION", " ... which is not enough");
|
||
|
$this->decideOnArtist("Various Artists");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
foreach ($this->tracks as $track) {
|
||
|
$track->tags['AlbumArtist'] = $this->artist;
|
||
|
}
|
||
|
|
||
|
if ($always == false && $this->numOfDiscs > 0 && ($this->numOfTrackOnes <= 1 || $this->numOfTrackOnes == $this->numOfDiscs)) {
|
||
|
return $this->numOfDiscs;
|
||
|
}
|
||
|
|
||
|
$discs = array();
|
||
|
$number = 1;
|
||
|
foreach ($this->tracks as $ob) {
|
||
|
if ($ob->tags['Track'] !== '') {
|
||
|
$track_no = intval($ob->tags['Track']);
|
||
|
} else {
|
||
|
$track_no = $number;
|
||
|
}
|
||
|
# Just in case we have a multiple disc album with no disc number tags
|
||
|
$discno = intval($ob->tags['Disc']);
|
||
|
if ($discno == '' || $discno == null || $discno == 0) {
|
||
|
$discno = 1;
|
||
|
}
|
||
|
if (!array_key_exists($discno, $discs)) {
|
||
|
$discs[$discno] = array();
|
||
|
}
|
||
|
while(array_key_exists($track_no, $discs[$discno])) {
|
||
|
$discno++;
|
||
|
if (!array_key_exists($discno, $discs)) {
|
||
|
$discs[$discno] = array();
|
||
|
}
|
||
|
}
|
||
|
$discs[$discno][$track_no] = $ob;
|
||
|
$ob->updateTrackInfo(array('Disc' => $discno));
|
||
|
$number++;
|
||
|
}
|
||
|
$numdiscs = count($discs);
|
||
|
|
||
|
$this->tracks = array();
|
||
|
ksort($discs, SORT_NUMERIC);
|
||
|
foreach ($discs as $disc) {
|
||
|
ksort($disc, SORT_NUMERIC);
|
||
|
$this->tracks = array_merge($this->tracks, $disc);
|
||
|
}
|
||
|
$this->numOfDiscs = $numdiscs;
|
||
|
|
||
|
return $numdiscs;
|
||
|
}
|
||
|
|
||
|
public function checkForDuplicate($t) {
|
||
|
foreach ($this->tracks as $track) {
|
||
|
if ($t->tags['file'] == $track->tags['file']) {
|
||
|
logger::trace("COLLECTION", "Filtering Duplicate Track ".$t->tags['file']);
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
private function decideOnArtist($candidate) {
|
||
|
if ($this->artist == null) {
|
||
|
logger::log("COLLECTION", " ... Setting artist to ".$candidate);
|
||
|
$this->artist = $candidate;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
class track {
|
||
|
|
||
|
public $tags;
|
||
|
|
||
|
public function __construct(&$filedata) {
|
||
|
$this->tags = $filedata;
|
||
|
}
|
||
|
|
||
|
public function updateTrackInfo($info) {
|
||
|
$this->tags = array_replace($this->tags, $info);
|
||
|
}
|
||
|
|
||
|
public function get_artist_string() {
|
||
|
return format_artist($this->tags['Artist']);
|
||
|
}
|
||
|
|
||
|
public function get_sort_artist($return_albumartist = false) {
|
||
|
return format_sortartist($this->tags, $return_albumartist);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
?>
|