Added Album Artist support

This commit is contained in:
emeric
2019-05-15 13:15:18 +02:00
parent 397c6f4e9c
commit 3aa6edd74f
25 changed files with 1327 additions and 98 deletions
+2
View File
@@ -21,6 +21,8 @@ lms_SOURCES = \
$(srcdir)/database/Cluster.hpp \
$(srcdir)/database/DatabaseHandler.cpp \
$(srcdir)/database/DatabaseHandler.hpp \
$(srcdir)/database/TrackArtistLink.cpp \
$(srcdir)/database/TrackArtistLink.hpp \
$(srcdir)/database/TrackFeatures.cpp \
$(srcdir)/database/TrackFeatures.hpp \
$(srcdir)/database/TrackList.cpp \
+45 -15
View File
@@ -77,7 +77,7 @@ Artist::getAll(Wt::Dbo::Session& session, boost::optional<std::size_t> offset, b
std::vector<Artist::pointer>
Artist::getAllOrphans(Wt::Dbo::Session& session)
{
Wt::Dbo::collection<Artist::pointer> res = session.query< Wt::Dbo::ptr<Artist> >("SELECT DISTINCT a FROM artist a WHERE NOT EXISTS(SELECT 1 FROM track t INNER JOIN track_artist t_a ON t_a.artist_id = a.id WHERE t.id = t_a.track_id)");
Wt::Dbo::collection<Wt::Dbo::ptr<Artist>> res {session.query<Wt::Dbo::ptr<Artist>>("SELECT DISTINCT a FROM artist a WHERE NOT EXISTS(SELECT 1 FROM track t INNER JOIN track_artist_link t_a_l ON t_a_l.artist_id = a.id WHERE t.id = t_a_l.track_id)")};
return std::vector<pointer>(res.begin(), res.end());
}
@@ -98,7 +98,7 @@ getQuery(Wt::Dbo::Session& session,
if (!clusterIds.empty())
{
oss << " INNER JOIN track t ON t_a.track_id = t.id INNER JOIN track_artist t_a ON t_a.artist_id = a.id INNER JOIN cluster c ON c.id = t_c.cluster_id INNER JOIN track_cluster t_c ON t_c.track_id = t.id";
oss << " INNER JOIN track t ON t.id = t_a_l.track_id INNER JOIN track_artist_link t_a_l ON t_a_l.artist_id = a.id INNER JOIN cluster c ON c.id = t_c.cluster_id INNER JOIN track_cluster t_c ON t_c.track_id = t.id";
WhereClause clusterClause;
@@ -110,7 +110,7 @@ getQuery(Wt::Dbo::Session& session,
oss << " " << where.get();
if (!clusterIds.empty())
oss << " GROUP BY t.id HAVING COUNT(*) = " << clusterIds.size();
oss << " GROUP BY t.id HAVING COUNT(DISTINCT c.id) = " << clusterIds.size();
oss << " ORDER BY a.sort_name COLLATE NOCASE";
@@ -124,6 +124,13 @@ getQuery(Wt::Dbo::Session& session,
return query;
}
std::vector<Artist::pointer>
Artist::getByFilter(Wt::Dbo::Session& session, const std::set<IdType>& clusters)
{
bool more;
return getByFilter(session, clusters, {}, {}, {}, more);
}
std::vector<Artist::pointer>
Artist::getByFilter(Wt::Dbo::Session& session,
const std::set<IdType>& clusters,
@@ -152,7 +159,7 @@ Artist::getByFilter(Wt::Dbo::Session& session,
std::vector<Artist::pointer>
Artist::getLastAdded(Wt::Dbo::Session& session, Wt::WDateTime after, boost::optional<std::size_t> limit)
{
Wt::Dbo::collection<Artist::pointer> res = session.query<Artist::pointer>("SELECT a from artist a INNER JOIN track_artist t_a ON t_a.artist_id = a.id INNER JOIN track t ON t.id = t_a.track_id")
Wt::Dbo::collection<Artist::pointer> res = session.query<Artist::pointer>("SELECT a from artist a INNER JOIN track_artist_link t_a_l ON t_a_l.artist_id = a.id INNER JOIN track t ON t.id = t_a_l.track_id")
.where("t.file_added > ?").bind(after)
.groupBy("a.id")
.orderBy("t.file_added DESC")
@@ -171,7 +178,8 @@ Artist::getReleases(const std::set<IdType>& clusterIds) const
WhereClause where;
std::ostringstream oss;
oss << "SELECT DISTINCT r FROM release r INNER JOIN artist a ON t_a.artist_id = a.id INNER JOIN track_artist t_a ON t_a.track_id = t.id INNER JOIN track t ON t.release_id = r.id";
oss << "SELECT DISTINCT r FROM release r INNER JOIN artist a ON a.id = t_a_l.artist_id INNER JOIN track_artist_link t_a_l ON t_a_l.track_id = t.id INNER JOIN track t ON t.release_id = r.id";
if (!clusterIds.empty())
{
@@ -190,32 +198,54 @@ Artist::getReleases(const std::set<IdType>& clusterIds) const
oss << " " << where.get();
if (!clusterIds.empty())
oss << " GROUP BY t.id HAVING COUNT(*) = " << clusterIds.size();
oss << " GROUP BY t.id HAVING COUNT(DISTINCT c.id) = " << clusterIds.size();
oss << " ORDER BY t.year,r.name";
Wt::Dbo::Query<Release::pointer> query = session()->query<Release::pointer>( oss.str() );
for (const std::string& bindArg : where.getBindArgs())
{
query.bind(bindArg);
}
Wt::Dbo::collection< Wt::Dbo::ptr<Release> > res = query;
Wt::Dbo::collection<Wt::Dbo::ptr<Release>> res = query;
return std::vector< Wt::Dbo::ptr<Release> > (res.begin(), res.end());
return std::vector<Wt::Dbo::ptr<Release>>(res.begin(), res.end());
}
std::vector<Wt::Dbo::ptr<Track>>
Artist::getTracks() const
Artist::getTracks(boost::optional<TrackArtistLink::Type> linkType) const
{
Wt::Dbo::collection<Wt::Dbo::ptr<Track>> tracks {session()->query<Wt::Dbo::ptr<Track>>("SELECT t from Track t INNER JOIN artist a ON t_a.artist_id = a.id INNER JOIN track_artist t_a ON t_a.track_id = t.id INNER JOIN release r ON r.id = t.release_id")
assert(self());
assert(IdIsValid(self()->id()));
assert(session());
auto query {session()->query<Wt::Dbo::ptr<Track>>("SELECT DISTINCT t FROM track t INNER JOIN artist a ON a.id = t_a_l.artist_id INNER JOIN track_artist_link t_a_l ON t_a_l.track_id = t.id")
.where("a.id = ?").bind(self()->id())
.orderBy("t.year,t.release_id,t.disc_number,t.track_number")};
if (linkType)
query.where("t_a_l.type = ?").bind(*linkType);
Wt::Dbo::collection<Wt::Dbo::ptr<Track>> tracks {query.resultList()};
return std::vector<Wt::Dbo::ptr<Track>>(tracks.begin(), tracks.end());
}
std::vector<Wt::Dbo::ptr<Track>>
Artist::getTracksWithRelease(boost::optional<TrackArtistLink::Type> linkType) const
{
assert(self());
assert(IdIsValid(self()->id()));
assert(session());
Wt::Dbo::collection<Wt::Dbo::ptr<Track>> tracks {session()->query<Wt::Dbo::ptr<Track>>("SELECT t FROM track t INNER JOIN artist a ON a.id = t_a_l.artist_id INNER JOIN track_artist_link t_a_l ON t_a_l.track_id = t.id INNER JOIN release r ON r.id = t.release_id")
.where("a.id = ?").bind(self()->id())
.orderBy("t.year,r.name,t.disc_number,t.track_number")};
return std::vector<Wt::Dbo::ptr<Track>>(tracks.begin(), tracks.end());
}
std::vector<Wt::Dbo::ptr<Track>>
Artist::getRandomTracks(boost::optional<std::size_t> count) const
{
@@ -223,7 +253,7 @@ Artist::getRandomTracks(boost::optional<std::size_t> count) const
assert(IdIsValid(self()->id()));
assert(session());
Wt::Dbo::collection<Wt::Dbo::ptr<Track>> tracks {session()->query<Wt::Dbo::ptr<Track>>("SELECT t from Track t INNER JOIN artist a ON t_a.artist_id = a.id INNER JOIN track_artist t_a ON t_a.track_id = t.id")
Wt::Dbo::collection<Wt::Dbo::ptr<Track>> tracks {session()->query<Wt::Dbo::ptr<Track>>("SELECT t from track t INNER JOIN artist a ON a.id = t_a_l.artist_id INNER JOIN track_artist_link t_a_l ON t_a_l.track_id = t.id")
.where("a.id = ?").bind(self()->id())
.orderBy("RANDOM()")
.limit(count ? static_cast<int>(*count) : -1)};
@@ -241,7 +271,7 @@ Artist::getClusterGroups(std::vector<ClusterType::pointer> clusterTypes, std::si
WhereClause where;
std::ostringstream oss;
oss << "SELECT c from cluster c INNER JOIN track t ON c.id = t_c.cluster_id INNER JOIN track_cluster t_c ON t_c.track_id = t.id INNER JOIN cluster_type c_type ON c.cluster_type_id = c_type.id INNER JOIN artist a ON t_a.artist_id = a.id INNER JOIN track_artist t_a ON t_a.track_id = t.id";
oss << "SELECT c FROM cluster c INNER JOIN track t ON c.id = t_c.cluster_id INNER JOIN track_cluster t_c ON t_c.track_id = t.id INNER JOIN cluster_type c_type ON c.cluster_type_id = c_type.id INNER JOIN artist a ON t_a_l.artist_id = a.id INNER JOIN track_artist_link t_a_l ON t_a_l.track_id = t.id";
where.And(WhereClause("a.id = ?")).bind(std::to_string(self()->id()));
{
@@ -252,7 +282,7 @@ Artist::getClusterGroups(std::vector<ClusterType::pointer> clusterTypes, std::si
where.And(clusterClause);
}
oss << " " << where.get();
oss << "GROUP BY c.id ORDER BY COUNT(c.id) DESC";
oss << "GROUP BY c.id ORDER BY COUNT(DISTINCT c.id) DESC";
Wt::Dbo::Query<Cluster::pointer> query = session()->query<Cluster::pointer>( oss.str() );
+8 -5
View File
@@ -27,15 +27,16 @@
#include <Wt/WDateTime.h>
#include <Wt/Dbo/Dbo.h>
#include "TrackArtistLink.hpp"
#include "Types.hpp"
namespace Database
{
class Track;
class Cluster;
class ClusterType;
class Release;
class Track;
class Artist : public Wt::Dbo::Dbo<Artist>
{
@@ -50,6 +51,8 @@ class Artist : public Wt::Dbo::Dbo<Artist>
static pointer getByMBID(Wt::Dbo::Session& session, const std::string& MBID);
static pointer getById(Wt::Dbo::Session& session, IdType id);
static std::vector<pointer> getByName(Wt::Dbo::Session& session, const std::string& name);
static std::vector<pointer> getByFilter(Wt::Dbo::Session& session,
const std::set<IdType>& clusters); // at least one track that belongs to these clusters
static std::vector<pointer> getByFilter(Wt::Dbo::Session& session,
const std::set<IdType>& clusters, // at least one track that belongs to these clusters
const std::vector<std::string>& keywords, // name must match all of these keywords
@@ -65,9 +68,9 @@ class Artist : public Wt::Dbo::Dbo<Artist>
const std::string& getName(void) const { return _name; }
const std::string& getMBID(void) const { return _MBID; }
// Get the releases that have at least one track for this artist that belongs to optional cluster filters
std::vector<Wt::Dbo::ptr<Release>> getReleases(const std::set<IdType>& clusterIds = std::set<IdType>()) const;
std::vector<Wt::Dbo::ptr<Track>> getTracks() const;
std::vector<Wt::Dbo::ptr<Track>> getTracks(boost::optional<TrackArtistLink::Type> linkType = {}) const;
std::vector<Wt::Dbo::ptr<Track>> getTracksWithRelease(boost::optional<TrackArtistLink::Type> linkType = {}) const;
std::vector<Wt::Dbo::ptr<Track>> getRandomTracks(boost::optional<std::size_t> count) const;
// Get the cluster of the tracks made by this artist
@@ -89,7 +92,7 @@ class Artist : public Wt::Dbo::Dbo<Artist>
Wt::Dbo::field(a, _name, "sort_name");
Wt::Dbo::field(a, _MBID, "mbid");
Wt::Dbo::hasMany(a, _tracks, Wt::Dbo::ManyToMany, "track_artist", "", Wt::Dbo::OnDeleteCascade);
Wt::Dbo::hasMany(a, _trackArtistLinks, Wt::Dbo::ManyToOne, "artist");
}
private:
@@ -100,7 +103,7 @@ class Artist : public Wt::Dbo::Dbo<Artist>
std::string _sortName;
std::string _MBID; // Musicbrainz Identifier
Wt::Dbo::collection<Wt::Dbo::ptr<Track>> _tracks; // Tracks of this artist
Wt::Dbo::collection<Wt::Dbo::ptr<TrackArtistLink>> _trackArtistLinks; // Tracks involving this artist
};
} // namespace Database
+6
View File
@@ -108,6 +108,12 @@ ClusterType::getByName(Wt::Dbo::Session& session, std::string name)
return session.find<ClusterType>().where("name = ?").bind(name);
}
ClusterType::pointer
ClusterType::getById(Wt::Dbo::Session& session, IdType id)
{
return session.find<ClusterType>().where("id= ?").bind(id);
}
std::vector<ClusterType::pointer>
ClusterType::getAll(Wt::Dbo::Session& session)
{
+1
View File
@@ -89,6 +89,7 @@ class ClusterType : public Wt::Dbo::Dbo<ClusterType>
static std::vector<pointer> getAllOrphans(Wt::Dbo::Session& session);
static pointer getByName(Wt::Dbo::Session& session, std::string name);
static pointer getById(Wt::Dbo::Session& session, IdType id);
static std::vector<pointer> getAll(Wt::Dbo::Session& session);
static pointer create(Wt::Dbo::Session& session, std::string name);
+2
View File
@@ -39,6 +39,7 @@
#include "ScanSettings.hpp"
#include "SimilaritySettings.hpp"
#include "Track.hpp"
#include "TrackArtistLink.hpp"
#include "TrackList.hpp"
#include "TrackFeatures.hpp"
@@ -106,6 +107,7 @@ Handler::Handler(Wt::Dbo::SqlConnectionPool& connectionPool)
_session.mapClass<TrackListEntry>("tracklist_entry");
_session.mapClass<Release>("release");
_session.mapClass<Track>("track");
_session.mapClass<TrackArtistLink>("track_artist_link");
_session.mapClass<TrackFeatures>("track_features");
_session.mapClass<ScanSettings>("scan_settings");
+18 -6
View File
@@ -73,7 +73,7 @@ Release::getAll(Wt::Dbo::Session& session, boost::optional<std::size_t> offset,
{
Wt::Dbo::collection<pointer> res = session.find<Release>()
.offset(offset ? static_cast<int>(*offset) : -1)
.limit(size ? static_cast<int>(*size) : - 1)
.limit(size ? static_cast<int>(*size) : -1)
.orderBy("name COLLATE NOCASE");
return std::vector<pointer>(res.begin(), res.end());
@@ -83,7 +83,7 @@ std::vector<Release::pointer>
Release::getAllRandom(Wt::Dbo::Session& session, boost::optional<std::size_t> size)
{
Wt::Dbo::collection<pointer> res = session.find<Release>()
.limit(size ? static_cast<int>(*size) : - 1)
.limit(size ? static_cast<int>(*size) : -1)
.orderBy("RANDOM()");
return std::vector<pointer>(res.begin(), res.end());
@@ -183,6 +183,18 @@ Release::getByFilter(Wt::Dbo::Session& session,
return res;
}
boost::optional<std::size_t>
Release::getTotalTrackNumber(void) const
{
return (_totalTrackNumber > 0) ? boost::make_optional<std::size_t>(_totalTrackNumber) : boost::none;
}
boost::optional<std::size_t>
Release::getTotalDiscNumber(void) const
{
return (_totalDiscNumber > 0) ? boost::make_optional<std::size_t>(_totalDiscNumber) : boost::none;
}
boost::optional<int>
Release::getReleaseYear(bool original) const
{
@@ -249,16 +261,16 @@ Release::getCopyrightURL() const
}
std::vector<Wt::Dbo::ptr<Artist>>
Release::getArtists() const
Release::getArtists(TrackArtistLink::Type linkType) const
{
assert(self());
assert(IdIsValid(self()->id()));
assert(session());
Wt::Dbo::collection<Wt::Dbo::ptr<Artist>> res = session()->query<Wt::Dbo::ptr<Artist>>(
"SELECT DISTINCT a FROM artist a INNER JOIN release r ON t.id = t_a.track_id INNER JOIN track_artist t_a ON t_a.artist_id = a.id INNER JOIN track t ON t.release_id = r.id")
.where("r.id = ?")
.bind(id());
"SELECT DISTINCT a FROM artist a INNER JOIN track_artist_link t_a_l ON t_a_l.artist_id = a.id INNER JOIN track t ON t.id = t_a_l.track_id INNER JOIN release r ON r.id = t.release_id")
.where("r.id = ?").bind(self()->id())
.where("t_a_l.type = ?").bind(linkType);
return std::vector<Wt::Dbo::ptr<Artist>>(res.begin(), res.end());
}
+19 -7
View File
@@ -23,6 +23,7 @@
#include <Wt/Dbo/WtSqlTraits.h>
#include "TrackArtistLink.hpp"
#include "Types.hpp"
namespace Database
@@ -76,13 +77,20 @@ class Release : public Wt::Dbo::Dbo<Release>
boost::optional<std::string> getCopyright() const;
boost::optional<std::string> getCopyrightURL() const;
// Modifiers
void setTotalDiscNumber(std::size_t num) { _totalDiscNumber = static_cast<int>(num); }
void setTotalTrackNumber(std::size_t num) { _totalTrackNumber = static_cast<int>(num); }
// Accessors
std::string getName() const { return _name; }
std::string getMBID() const { return _MBID; }
std::string getName() const { return _name; }
std::string getMBID() const { return _MBID; }
boost::optional<std::size_t> getTotalTrackNumber() const;
boost::optional<std::size_t> getTotalDiscNumber() const;
std::chrono::milliseconds getDuration() const;
// Get the artists of this release
std::vector<Wt::Dbo::ptr<Artist> > getArtists() const;
std::vector<Wt::Dbo::ptr<Artist> > getArtists(TrackArtistLink::Type type = TrackArtistLink::Type::Artist) const;
std::vector<Wt::Dbo::ptr<Artist> > getReleaseArtists() const { return getArtists(TrackArtistLink::Type::ReleaseArtist); }
bool hasVariousArtists() const;
void setMBID(std::string mbid) { _MBID = mbid; }
@@ -92,17 +100,21 @@ class Release : public Wt::Dbo::Dbo<Release>
{
Wt::Dbo::field(a, _name, "name");
Wt::Dbo::field(a, _MBID, "mbid");
Wt::Dbo::field(a, _totalDiscNumber, "total_disc_number");
Wt::Dbo::field(a, _totalTrackNumber, "total_track_number");
Wt::Dbo::hasMany(a, _tracks, Wt::Dbo::ManyToOne, "release");
}
private:
static const std::size_t _maxNameLength = 128;
static const std::size_t _maxNameLength {128};
std::string _name;
std::string _MBID;
std::string _name;
std::string _MBID;
int _totalDiscNumber {};
int _totalTrackNumber {};
Wt::Dbo::collection< Wt::Dbo::ptr<Track> > _tracks; // Tracks in the release
Wt::Dbo::collection<Wt::Dbo::ptr<Track>> _tracks; // Tracks in the release
};
} // namespace Database
+34 -22
View File
@@ -37,12 +37,13 @@ _filePath( p.string() )
{
}
Wt::Dbo::collection< Track::pointer >
std::vector<Track::pointer>
Track::getAll(Wt::Dbo::Session& session, boost::optional<std::size_t> limit)
{
int size {limit ? static_cast<int>(*limit) : -1};
Wt::Dbo::collection<Track::pointer> res {session.find<Track>()
.limit(limit ? static_cast<int>(*limit) : -1)};
return session.find<Track>().limit(size);
return std::vector<Track::pointer>(std::cbegin(res), std::cend(res));
}
std::vector<Track::pointer>
@@ -229,15 +230,24 @@ Track::getByFilter(Wt::Dbo::Session& session,
{
bool moreResults;
return getByFilter(session, clusters, std::vector<std::string>{}, -1, -1, moreResults);
return getByFilter(session,
clusters,
std::vector<std::string> {},
boost::optional<std::size_t> {},
boost::optional<std::size_t> {},
moreResults);
}
void
Track::setArtists(const std::vector<Wt::Dbo::ptr<Artist>>& artists)
Track::clearArtistLinks()
{
_artists.clear();
for (const Wt::Dbo::ptr<Artist>& artist : artists)
_artists.insert(artist);
_trackArtistLinks.clear();
}
void
Track::addArtistLink(const Wt::Dbo::ptr<TrackArtistLink>& artistLink)
{
_trackArtistLinks.insert(artistLink);
}
void
@@ -260,24 +270,12 @@ Track::getTrackNumber(void) const
return (_trackNumber > 0) ? boost::make_optional<std::size_t>(_trackNumber) : boost::none;
}
boost::optional<std::size_t>
Track::getTotalTrackNumber(void) const
{
return (_totalTrackNumber > 0) ? boost::make_optional<std::size_t>(_totalTrackNumber) : boost::none;
}
boost::optional<std::size_t>
Track::getDiscNumber(void) const
{
return (_discNumber > 0) ? boost::make_optional<std::size_t>(_discNumber) : boost::none;
}
boost::optional<std::size_t>
Track::getTotalDiscNumber(void) const
{
return (_totalDiscNumber > 0) ? boost::make_optional<std::size_t>(_totalDiscNumber) : boost::none;
}
boost::optional<int>
Track::getYear() const
{
@@ -303,9 +301,23 @@ Track::getCopyrightURL() const
}
std::vector<Wt::Dbo::ptr<Artist>>
Track::getArtists() const
Track::getArtists(TrackArtistLink::Type type) const
{
return std::vector<Wt::Dbo::ptr<Artist>>(_artists.begin(), _artists.end());
assert(self());
assert(IdIsValid(self()->id()));
assert(session());
Wt::Dbo::collection<Wt::Dbo::ptr<Artist>> artists {session()->query<Artist::pointer>("SELECT a from artist a INNER JOIN track_artist_link t_a_l ON a.id = t_a_l.artist_id INNER JOIN track t ON t.id = t_a_l.track_id")
.where("t.id = ?").bind(self()->id())
.where("t_a_l.type = ?").bind(type)};
return std::vector<Wt::Dbo::ptr<Artist>>(artists.begin(), artists.end());
}
std::vector<Wt::Dbo::ptr<TrackArtistLink>>
Track::getArtistLinks() const
{
return std::vector<Wt::Dbo::ptr<TrackArtistLink>>(_trackArtistLinks.begin(), _trackArtistLinks.end());
}
Wt::Dbo::ptr<TrackFeatures>
+8 -13
View File
@@ -29,6 +29,7 @@
#include <Wt/Dbo/Dbo.h>
#include <Wt/WDateTime.h>
#include "TrackArtistLink.hpp"
#include "Types.hpp"
namespace Database {
@@ -63,7 +64,7 @@ class Track : public Wt::Dbo::Dbo<Track>
boost::optional<std::size_t> size,
bool& moreExpected);
static Wt::Dbo::collection< pointer > getAll(Wt::Dbo::Session& session, boost::optional<std::size_t> limit = {});
static std::vector<pointer> getAll(Wt::Dbo::Session& session, boost::optional<std::size_t> limit = {});
static std::vector<pointer> getAllRandom(Wt::Dbo::Session& session, boost::optional<std::size_t> limit = {});
static std::vector<IdType> getAllIds(Wt::Dbo::Session& session); // nested transaction
static std::vector<boost::filesystem::path> getAllPaths(Wt::Dbo::Session& session); // nested transaction
@@ -79,9 +80,7 @@ class Track : public Wt::Dbo::Dbo<Track>
// Accessors
void setScanVersion(std::size_t version) { _scanVersion = version; }
void setTrackNumber(int num) { _trackNumber = num; }
void setTotalTrackNumber(int num) { _totalTrackNumber = num; }
void setDiscNumber(int num) { _discNumber = num; }
void setTotalDiscNumber(int num) { _totalDiscNumber = num; }
void setName(const std::string& name) { _name = std::string(name, 0, _maxNameLength); }
void setDuration(std::chrono::milliseconds duration) { _duration = duration; }
void setLastWriteTime(Wt::WDateTime time) { _fileLastWrite = time; }
@@ -93,16 +92,15 @@ class Track : public Wt::Dbo::Dbo<Track>
void setMBID(const std::string& MBID) { _MBID = MBID; }
void setCopyright(const std::string& copyright) { _copyright = std::string(copyright, 0, _maxCopyrightLength); }
void setCopyrightURL(const std::string& copyrightURL) { _copyrightURL = std::string(copyrightURL, 0, _maxCopyrightURLLength); }
void setArtists(const std::vector<Wt::Dbo::ptr<Artist>>& artists);
void clearArtistLinks();
void addArtistLink(const Wt::Dbo::ptr<TrackArtistLink>& artistLink);
void setRelease(Wt::Dbo::ptr<Release> release) { _release = release; }
void setClusters(const std::vector<Wt::Dbo::ptr<Cluster>>& clusters );
void setFeatures(const Wt::Dbo::ptr<TrackFeatures>& features);
std::size_t getScanVersion() const { return _scanVersion; }
boost::optional<std::size_t> getTrackNumber() const;
boost::optional<std::size_t> getTotalTrackNumber() const;
boost::optional<std::size_t> getDiscNumber() const;
boost::optional<std::size_t> getTotalDiscNumber() const;
std::string getName() const { return _name; }
boost::filesystem::path getPath() const { return _filePath; }
std::chrono::milliseconds getDuration() const { return _duration; }
@@ -115,7 +113,8 @@ class Track : public Wt::Dbo::Dbo<Track>
const std::string& getMBID() const { return _MBID; }
boost::optional<std::string> getCopyright() const;
boost::optional<std::string> getCopyrightURL() const;
std::vector<Wt::Dbo::ptr<Artist>> getArtists() const;
std::vector<Wt::Dbo::ptr<Artist>> getArtists(TrackArtistLink::Type type = {TrackArtistLink::Type::Artist}) const;
std::vector<Wt::Dbo::ptr<TrackArtistLink>> getArtistLinks() const;
Wt::Dbo::ptr<Release> getRelease() const { return _release; }
std::vector<Wt::Dbo::ptr<Cluster>> getClusters() const;
bool hasTrackFeatures() const;
@@ -128,9 +127,7 @@ class Track : public Wt::Dbo::Dbo<Track>
{
Wt::Dbo::field(a, _scanVersion, "scan_version");
Wt::Dbo::field(a, _trackNumber, "track_number");
Wt::Dbo::field(a, _totalTrackNumber, "total_track_number");
Wt::Dbo::field(a, _discNumber, "disc_number");
Wt::Dbo::field(a, _totalDiscNumber, "total_disc_number");
Wt::Dbo::field(a, _name, "name");
Wt::Dbo::field(a, _duration, "duration");
Wt::Dbo::field(a, _year, "year");
@@ -144,7 +141,7 @@ class Track : public Wt::Dbo::Dbo<Track>
Wt::Dbo::field(a, _copyright, "copyright");
Wt::Dbo::field(a, _copyrightURL, "copyright_url");
Wt::Dbo::belongsTo(a, _release, "release", Wt::Dbo::OnDeleteCascade);
Wt::Dbo::hasMany(a, _artists, Wt::Dbo::ManyToMany, "track_artist", "", Wt::Dbo::OnDeleteCascade);
Wt::Dbo::hasMany(a, _trackArtistLinks, Wt::Dbo::ManyToOne, "track");
Wt::Dbo::hasMany(a, _clusters, Wt::Dbo::ManyToMany, "track_cluster", "", Wt::Dbo::OnDeleteCascade);
Wt::Dbo::hasMany(a, _playlistEntries, Wt::Dbo::ManyToOne, "track");
Wt::Dbo::hasOne(a, _trackFeatures);
@@ -158,9 +155,7 @@ class Track : public Wt::Dbo::Dbo<Track>
int _scanVersion = 0;
int _trackNumber = 0;
int _totalTrackNumber = 0;
int _discNumber = 0;
int _totalDiscNumber = 0;
std::string _name;
std::string _artistName;
std::string _releaseName;
@@ -177,7 +172,7 @@ class Track : public Wt::Dbo::Dbo<Track>
std::string _copyrightURL;
Wt::Dbo::ptr<Release> _release;
Wt::Dbo::collection<Wt::Dbo::ptr<Artist>> _artists;
Wt::Dbo::collection<Wt::Dbo::ptr<TrackArtistLink>> _trackArtistLinks;
Wt::Dbo::collection<Wt::Dbo::ptr<Cluster>> _clusters;
Wt::Dbo::collection<Wt::Dbo::ptr<TrackListEntry>> _playlistEntries;
Wt::Dbo::weak_ptr<TrackFeatures> _trackFeatures;
+41
View File
@@ -0,0 +1,41 @@
/*
* Copyright (C) 2013-2016 Emeric Poupon
*
* This file is part of LMS.
*
* LMS is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LMS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/
#include "TrackArtistLink.hpp"
#include "Artist.hpp"
#include "Track.hpp"
namespace Database {
TrackArtistLink::TrackArtistLink(Wt::Dbo::ptr<Track> track, Wt::Dbo::ptr<Artist> artist, Type type)
: _type {type},
_track {track},
_artist {artist}
{
}
TrackArtistLink::pointer
TrackArtistLink::create(Wt::Dbo::Session& session, Wt::Dbo::ptr<Track> track, Wt::Dbo::ptr<Artist> artist,Type type)
{
return session.add(std::make_unique<TrackArtistLink>(track, artist, type));
}
}
+81
View File
@@ -0,0 +1,81 @@
/*
* Copyright (C) 2013-2016 Emeric Poupon
*
* This file is part of LMS.
*
* LMS is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LMS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <Wt/Dbo/Dbo.h>
#include "Types.hpp"
namespace Database {
class Artist;
class Track;
class TrackArtistLink
{
public:
enum class Type
{
Artist, // regular artist
Arranger,
Composer,
Conductor,
Lyricist,
Mixer,
Performer,
Producer,
ReleaseArtist,
Remixer,
Writer,
};
using pointer = Wt::Dbo::ptr<TrackArtistLink>;
TrackArtistLink() = default;
TrackArtistLink(Wt::Dbo::ptr<Track> track, Wt::Dbo::ptr<Artist> artist, Type type);
static pointer create(Wt::Dbo::Session& session, Wt::Dbo::ptr<Track> track, Wt::Dbo::ptr<Artist> artist,Type type);
Wt::Dbo::ptr<Track> getTrack() const { return _track; }
Wt::Dbo::ptr<Artist> getArtist() const { return _artist; }
Type getType() const { return _type; }
template<class Action>
void persist(Action& a)
{
Wt::Dbo::field(a, _type, "type");
Wt::Dbo::field(a, _type, "name");
Wt::Dbo::belongsTo(a, _track, "track", Wt::Dbo::OnDeleteCascade);
Wt::Dbo::belongsTo(a, _artist, "artist", Wt::Dbo::OnDeleteCascade);
}
private:
Type _type;
std::string _name;
Wt::Dbo::ptr<Track> _track;
Wt::Dbo::ptr<Artist> _artist;
};
}
+1 -1
View File
@@ -223,7 +223,7 @@ TrackList::getTopArtists(std::size_t limit) const
assert(session());
assert(IdIsValid(self()->id()));
Wt::Dbo::collection<Artist::pointer> res = session()->query<Artist::pointer>("SELECT a from artist a INNER JOIN track t ON t.id = t_a.track_id INNER JOIN track_artist t_a ON t_a.artist_id = a.id INNER JOIN tracklist_entry p_e ON p_e.track_id = t.id INNER JOIN tracklist p ON p.id = p_e.tracklist_id")
Wt::Dbo::collection<Artist::pointer> res = session()->query<Artist::pointer>("SELECT a from artist a INNER JOIN track t ON t.id = t_a_l.track_id INNER JOIN track_artist_link t_a_l ON t_a_l.artist_id = a.id INNER JOIN tracklist_entry p_e ON p_e.track_id = t.id INNER JOIN tracklist p ON p.id = p_e.tracklist_id")
.where("p.id = ?").bind(self()->id())
.groupBy("a.id")
.orderBy("COUNT(a.id) DESC")
+2
View File
@@ -63,6 +63,8 @@ std::vector<std::string> generateWtConfig(std::string execPath)
args.push_back("--http-address=" + Config::instance().getString("listen-addr", "0.0.0.0"));
}
args.push_back("--gdb");
// Generate the wt_config.xml file
boost::property_tree::ptree pt;
+24 -9
View File
@@ -85,7 +85,7 @@ isPathInParentPath(const boost::filesystem::path& path, const boost::filesystem:
}
std::vector<Artist::pointer>
getArtists(Wt::Dbo::Session& session, const std::vector<MetaData::Artist>& artistsInfo)
getOrCreateArtists(Wt::Dbo::Session& session, const std::vector<MetaData::Artist>& artistsInfo)
{
std::vector<Artist::pointer> artists;
@@ -129,7 +129,7 @@ getArtists(Wt::Dbo::Session& session, const std::vector<MetaData::Artist>& artis
}
Release::pointer
getRelease(Wt::Dbo::Session& session, const MetaData::Album& album)
getOrCreateRelease(Wt::Dbo::Session& session, const MetaData::Album& album)
{
Release::pointer release;
@@ -166,7 +166,7 @@ getRelease(Wt::Dbo::Session& session, const MetaData::Album& album)
}
std::vector<Cluster::pointer>
getClusters(Wt::Dbo::Session& session, const MetaData::Clusters& clustersNames)
getOrCreateClusters(Wt::Dbo::Session& session, const MetaData::Clusters& clustersNames)
{
std::vector< Cluster::pointer > clusters;
@@ -465,15 +465,18 @@ MediaScanner::scanAudioFile(const boost::filesystem::path& file, bool forceScan,
}
// ***** Clusters
std::vector<Cluster::pointer> clusters {getClusters(_db.getSession(), trackInfo->clusters)};
std::vector<Cluster::pointer> clusters {getOrCreateClusters(_db.getSession(), trackInfo->clusters)};
// ***** Artists
std::vector<Artist::pointer> artists {getArtists(_db.getSession(), trackInfo->artists)};
std::vector<Artist::pointer> artists {getOrCreateArtists(_db.getSession(), trackInfo->artists)};
// ***** Release artists
std::vector<Artist::pointer> releaseArtists {getOrCreateArtists(_db.getSession(), trackInfo->albumArtists)};
// ***** Release
Release::pointer release;
if (trackInfo->album)
release = getRelease(_db.getSession(), *trackInfo->album);
release = getOrCreateRelease(_db.getSession(), *trackInfo->album);
// If file already exist, update data
// Otherwise, create it
@@ -493,11 +496,25 @@ MediaScanner::scanAudioFile(const boost::filesystem::path& file, bool forceScan,
stats.updates++;
}
// Release related data
if (release)
{
release.modify()->setTotalTrackNumber(trackInfo->totalTrack ? *trackInfo->totalTrack : 0);
release.modify()->setTotalDiscNumber(trackInfo->totalDisc ? *trackInfo->totalDisc : 0);
}
// Track related data
assert(track);
track.modify()->clearArtistLinks();
for (const auto& artist : artists)
track.modify()->addArtistLink(Database::TrackArtistLink::create(_db.getSession(), track, artist, Database::TrackArtistLink::Type::Artist));
for (const auto& releaseArtist : releaseArtists)
track.modify()->addArtistLink(Database::TrackArtistLink::create(_db.getSession(), track, releaseArtist, Database::TrackArtistLink::Type::ReleaseArtist));
track.modify()->setScanVersion(_scanVersion);
track.modify()->setChecksum(checksum);
track.modify()->setArtists(artists);
track.modify()->setRelease(release);
track.modify()->setClusters(clusters);
track.modify()->setLastWriteTime(lastWriteTime);
@@ -505,9 +522,7 @@ MediaScanner::scanAudioFile(const boost::filesystem::path& file, bool forceScan,
track.modify()->setDuration(trackInfo->duration);
track.modify()->setAddedTime(Wt::WLocalDateTime::currentServerDateTime().toUTC());
track.modify()->setTrackNumber(trackInfo->trackNumber ? *trackInfo->trackNumber : 0);
track.modify()->setTotalTrackNumber(trackInfo->totalTrack ? *trackInfo->totalTrack : 0);
track.modify()->setDiscNumber(trackInfo->discNumber ? *trackInfo->discNumber : 0);
track.modify()->setTotalDiscNumber(trackInfo->totalDisc ? *trackInfo->totalDisc : 0);
track.modify()->setYear(trackInfo->year ? *trackInfo->year : 0);
track.modify()->setOriginalYear(trackInfo->originalYear ? *trackInfo->originalYear : 0);
+22 -2
View File
@@ -120,6 +120,7 @@ Artist::refresh()
Wt::WContainerWidget* releasesContainer = t->bindNew<Wt::WContainerWidget>("releases");
auto releases = artist->getReleases(_filters->getClusterIds());
for (auto release : releases)
{
auto releaseId = release.id();
@@ -139,8 +140,27 @@ Artist::refresh()
entry->bindWidget("name", LmsApplication::createReleaseAnchor(release));
if (release->hasVariousArtists())
entry->setCondition("if-has-various-artists", true);
auto artists {release->getReleaseArtists()};
LMS_LOG(UI, DEBUG) << "Found " << artists.size() << " release artists";
for (auto artist : artists)
LMS_LOG(UI, DEBUG) << "\tArtist = '" << artist->getName() << "'";
if (artists.empty())
artists = release->getArtists();
bool isSameArtist {(std::find(std::cbegin(artists), std::cend(artists), artist) != artists.end())};
if (artists.size() > 1)
{
entry->setCondition("if-has-artist", true);
entry->bindNew<Wt::WText>("artist", Wt::WString::tr("Lms.Explore.various-artists"));
}
else if (artists.size() == 1 && !isSameArtist)
{
entry->setCondition("if-has-artist", true);
entry->bindWidget("artist", LmsApplication::createArtistAnchor(artists.front()));
}
boost::optional<int> year = release->getReleaseYear();
if (year)
+4 -1
View File
@@ -44,7 +44,10 @@ ReleaseLink::ReleaseLink(Database::Release::pointer release)
cover->setWidth(48);
anchor->setImage(std::move(cover));
auto artists = release->getArtists();
auto artists = release->getReleaseArtists();
if (artists.empty())
artists = release->getArtists();
if (artists.size() > 1)
{
setCondition("if-has-artist", true);
+9 -4
View File
@@ -96,16 +96,21 @@ Release::refresh()
}
{
auto artists {release->getArtists()};
std::vector<Wt::Dbo::ptr<Database::Artist>> artists;
artists = release->getReleaseArtists();
if (artists.empty())
artists = release->getArtists();
if (artists.size() > 1)
{
t->setCondition("if-has-artist", true);
t->bindNew<Wt::WText>("artist-name", Wt::WString::tr("Lms.Explore.various-artists"));
t->bindNew<Wt::WText>("artist", Wt::WString::tr("Lms.Explore.various-artists"));
}
else if (artists.size() == 1)
{
t->setCondition("if-has-artist", true);
t->bindWidget("artist-name", LmsApplication::createArtistAnchor(artists.front()));
t->bindWidget("artist", LmsApplication::createArtistAnchor(artists.front()));
}
}
@@ -181,7 +186,7 @@ Release::refresh()
}
auto discNumber {track->getDiscNumber()};
auto totalDiscNumber {track->getTotalDiscNumber()};
auto totalDiscNumber {release->getTotalDiscNumber()};
if (discNumber && totalDiscNumber && *totalDiscNumber > 1)
{
entry->setCondition("if-has-disc-number", true);
+4 -1
View File
@@ -96,7 +96,10 @@ Releases::addSome()
entry->bindWidget("release-name", LmsApplication::createReleaseAnchor(release));
auto artists = release->getArtists();
auto artists = release->getReleaseArtists();
if (artists.empty())
artists = release->getArtists();
if (artists.size() > 1)
{
entry->setCondition("if-has-artist", true);