Simplified db schema

This commit is contained in:
emeric
2014-11-24 19:42:33 +01:00
parent f7edebaf8e
commit b9657a5680
24 changed files with 485 additions and 691 deletions
+1 -2
View File
@@ -11,11 +11,9 @@ lms_SOURCES = \
$(srcdir)/config/ConfigReader.cpp \
$(srcdir)/cover/CoverArt.cpp \
$(srcdir)/cover/CoverArtGrabber.cpp \
$(srcdir)/database/Artist.cpp \
$(srcdir)/database/DatabaseHandler.cpp \
$(srcdir)/database/Genre.cpp \
$(srcdir)/database/MediaDirectory.cpp \
$(srcdir)/database/Release.cpp \
$(srcdir)/database/SqlQuery.cpp \
$(srcdir)/database/Track.cpp \
$(srcdir)/database/User.cpp \
@@ -48,6 +46,7 @@ lms_SOURCES = \
$(srcdir)/ui/audio/KeywordSearchFilter.cpp \
$(srcdir)/ui/audio/PlayQueue.cpp \
$(srcdir)/ui/audio/TableFilter.cpp \
$(srcdir)/ui/audio/TableFilterGenre.cpp \
$(srcdir)/ui/audio/TrackView.cpp \
$(srcdir)/ui/common/DirectoryValidator.cpp \
$(srcdir)/ui/common/SessionData.cpp \
-25
View File
@@ -77,30 +77,5 @@ Grabber::getFromTrack(Database::Track::pointer track)
return res;
}
std::vector<CoverArt>
Grabber::getFromRelease(Database::Release::pointer release)
{
if (!release)
return std::vector<CoverArt>();
// TODO
// Check if there is an image file in the directory of the release
// For now, just get the cover art from the first track of the release
Wt::Dbo::collection<Database::Track::pointer> tracks (release->getTracks());
Database::Track::pointer firstTrack;
if (tracks.begin() != tracks.end())
firstTrack = *tracks.begin();
if (firstTrack)
{
return Grabber::getFromTrack(firstTrack);
}
else
return std::vector<CoverArt>();
}
} // namespace CoverArt
-1
View File
@@ -36,7 +36,6 @@ class Grabber
static std::vector<CoverArt> getFromInputFormatContext(const Av::InputFormatContext& input);
static std::vector<CoverArt> getFromTrack(Database::Track::pointer track);
static std::vector<CoverArt> getFromRelease(Database::Release::pointer release);
};
} // namespace CoverArt
+7 -54
View File
@@ -320,35 +320,6 @@ Updater::processAudioFile( const boost::filesystem::path& file, Stats& stats)
title = file.filename().string();
}
// ***** Artist
Wt::Dbo::ptr<Artist> artist;
if (items.find(MetaData::Type::Artist) != items.end())
{
const std::string artistName (boost::any_cast<std::string>(items[MetaData::Type::Artist]));
artist = Artist::getByName(_db.getSession(), artistName );
if (!artist)
artist = Artist::create( _db.getSession(), artistName );
}
else
artist = Artist::getNone(_db.getSession());
assert(artist);
// ***** Release
Wt::Dbo::ptr<Release> release;
if (items.find(MetaData::Type::Album) != items.end())
{
const std::string albumName (boost::any_cast<std::string>(items[MetaData::Type::Album]));
release = Release::getByName(_db.getSession(), albumName);
if (!release)
release = Release::create( _db.getSession(), albumName );
}
else
release = Release::getNone( _db.getSession() );
assert(release);
// ***** Genres
typedef std::list<std::string> GenreList;
@@ -376,7 +347,7 @@ Updater::processAudioFile( const boost::filesystem::path& file, Stats& stats)
if (!track)
{
// Create a new song
track = Track::create(_db.getSession(), file, artist, release);
track = Track::create(_db.getSession(), file);
LMS_LOG(MOD_DBUPDATER, SEV_INFO) << "Adding '" << file << "'";
stats.nbAdded++;
}
@@ -403,8 +374,12 @@ Updater::processAudioFile( const boost::filesystem::path& file, Stats& stats)
track.modify()->setGenres( trackGenreList );
}
track.modify()->setGenres( genres );
track.modify()->setArtist( artist );
track.modify()->setRelease( release );
if (items.find(MetaData::Type::Artist) != items.end())
track.modify()->setArtistName( boost::any_cast<std::string>(items[MetaData::Type::Artist]) );
if (items.find(MetaData::Type::Album) != items.end())
track.modify()->setReleaseName( boost::any_cast<std::string>(items[MetaData::Type::Album]) );
if (items.find(MetaData::Type::TrackNumber) != items.end())
track.modify()->setTrackNumber( boost::any_cast<std::size_t>(items[MetaData::Type::TrackNumber]) );
@@ -540,28 +515,6 @@ Updater::checkAudioFiles( Stats& stats )
}
}
LMS_LOG(MOD_DBUPDATER, SEV_DEBUG) << "Checking Artists...";
// Now process orphan Artists (no track)
typedef Wt::Dbo::collection< Wt::Dbo::ptr<Artist> > Artists;
Artists artists = Artist::getAllOrphans(_db.getSession());
for (Artists::iterator it = artists.begin(); it != artists.end(); ++it)
{
LMS_LOG(MOD_DBUPDATER, SEV_INFO) << "Removing orphan artist " << (*it)->getName();
(*it).remove();
}
LMS_LOG(MOD_DBUPDATER, SEV_DEBUG) << "Checking Releases...";
// Now process orphan Release (no track)
typedef Wt::Dbo::collection< Wt::Dbo::ptr<Release> > Releases;
Releases releases = Release::getAllOrphans(_db.getSession());
for (Releases::iterator it = releases.begin(); it != releases.end(); ++it)
{
LMS_LOG(MOD_DBUPDATER, SEV_INFO) << "Removing orphan release " << (*it)->getName();
(*it).remove();
}
// Now process orphan Genre (no track)
LMS_LOG(MOD_DBUPDATER, SEV_DEBUG) << "Checking Genres...";
typedef Wt::Dbo::collection< Wt::Dbo::ptr<Genre> > Genres;
-106
View File
@@ -1,106 +0,0 @@
/*
* Copyright (C) 2013 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 <boost/foreach.hpp>
#include "AudioTypes.hpp"
#include "SqlQuery.hpp"
namespace Database
{
Artist::Artist(const std::string& name)
: _name(std::string(name, 0 , _maxNameLength))
{
}
// Accesoors
Artist::pointer
Artist::getByName(Wt::Dbo::Session& session, const std::string& name)
{
return session.find<Artist>().where("name = ?").bind( std::string(name, 0, _maxNameLength) );
}
// Create
Artist::pointer
Artist::create(Wt::Dbo::Session& session, const std::string& name)
{
return session.add( new Artist( name ) );
}
Artist::pointer
Artist::getNone(Wt::Dbo::Session& session)
{
pointer res = getByName(session, "<None>");
if (!res)
res = create(session, "<None>");
return res;
}
Wt::Dbo::collection<Artist::pointer>
Artist::getAll(Wt::Dbo::Session& session, int offset, int size)
{
return session.find<Artist>().offset(offset).limit(size);
}
Wt::Dbo::collection<Artist::pointer>
Artist::getAllOrphans(Wt::Dbo::Session& session)
{
return session.query< Wt::Dbo::ptr<Artist> >("select a from artist a LEFT OUTER JOIN Track t ON a.id = t.artist_id WHERE t.id IS NULL");
}
Wt::Dbo::collection<Artist::pointer>
Artist::getAll(Wt::Dbo::Session& session,
const std::vector<Genre::id_type>& genreIds,
int offset, int size)
{
std::string sqlQuery = "SELECT a FROM artist a";
if (!genreIds.empty())
{
sqlQuery += " INNER JOIN genre g ON g.id = t_g.genre_id";
sqlQuery += " INNER JOIN track t ON t.id = t_g.track_id AND t.artist_id = a.id";
sqlQuery += " INNER JOIN track_genre t_g ON t_g.track_id = t.id AND t_g.genre_id = g.id";
}
WhereClause where;
{
WhereClause genreWhere;
for (std::size_t i = 0; i < genreIds.size(); ++i)
genreWhere.Or( WhereClause("g.id = ?") );
where.And(genreWhere);
}
Wt::Dbo::Query<Artist::pointer> query = session.query<Artist::pointer>( sqlQuery + " " + where.get() ).offset(offset).limit(size);
BOOST_FOREACH(const Genre::id_type genreId, genreIds)
query.bind(genreId);
query.groupBy("a");
return query;
}
} // namespace Database
+24 -109
View File
@@ -33,8 +33,6 @@
namespace Database {
class Track;
class Release;
class Artist;
class Genre
{
@@ -73,99 +71,6 @@ class Genre
Wt::Dbo::collection< Wt::Dbo::ptr<Track> > _tracks;
};
class Artist
{
public:
typedef Wt::Dbo::ptr<Artist> pointer;
typedef Wt::Dbo::dbo_traits<Artist>::IdType id_type;
Artist() {}
Artist(const std::string& p_name);
// Accessors
static pointer getByName(Wt::Dbo::Session& session, const std::string& name);
static pointer getNone(Wt::Dbo::Session& session);
static Wt::Dbo::collection<pointer> getAll(Wt::Dbo::Session& session, int offset = -1, int size = -1);
static Wt::Dbo::collection<pointer> getAll(Wt::Dbo::Session& session,
const std::vector<Genre::id_type>& genreIds,
int offset = -1, int size = -1);
static Wt::Dbo::collection<pointer> getAllOrphans(Wt::Dbo::Session& session);
const std::string& getName(void) const { return _name; }
const Wt::Dbo::collection< Wt::Dbo::ptr<Track> >& getTracks(void) const { return _tracks;}
// Create
static pointer create(Wt::Dbo::Session& session, const std::string& name);
bool isNone(void) const;
template<class Action>
void persist(Action& a)
{
Wt::Dbo::field(a, _name, "name");
Wt::Dbo::hasMany(a, _tracks, Wt::Dbo::ManyToOne, "artist");
}
private:
static const std::size_t _maxNameLength = 128;
std::string _name;
Wt::Dbo::collection< Wt::Dbo::ptr<Track> > _tracks; // Tracks of this artist
};
// Album release
class Release
{
public:
typedef Wt::Dbo::ptr<Release> pointer;
typedef Wt::Dbo::dbo_traits<Release>::IdType id_type;
Release() {}
Release(const std::string& name);
// Accessors
static pointer getByName(Wt::Dbo::Session& session, const std::string& name);
static pointer getById(Wt::Dbo::Session& session, id_type id);
static pointer getNone(Wt::Dbo::Session& session);
static Wt::Dbo::collection<pointer> getAllOrphans(Wt::Dbo::Session& session);
static Wt::Dbo::collection<pointer> getAll(Wt::Dbo::Session& session,
const std::vector<Artist::id_type>& artistIds,
const std::vector<Genre::id_type>& genreIds,
int offset = -1, int size = -1);
// Create
static pointer create(Wt::Dbo::Session& session, const std::string& name);
std::string getName() const { return _name; }
bool isNone(void) const;
const Wt::Dbo::collection<Wt::Dbo::ptr<Track> >& getTracks(void) const { return _tracks;}
boost::posix_time::time_duration getDuration(void) const;
template<class Action>
void persist(Action& a)
{
Wt::Dbo::field(a, _name, "name");
Wt::Dbo::hasMany(a, _tracks, Wt::Dbo::ManyToOne, "release");
}
private:
static const std::size_t _maxNameLength = 128;
std::string _name;
Wt::Dbo::collection< Wt::Dbo::ptr<Track> > _tracks; // Tracks in the release
};
class Track
{
public:
@@ -174,25 +79,37 @@ class Track
typedef Wt::Dbo::dbo_traits<Track>::IdType id_type;
Track() {}
Track(const boost::filesystem::path& p, Artist::pointer artist, Release::pointer release);
Track(const boost::filesystem::path& p);
// Find utilities
static pointer getByPath(Wt::Dbo::Session& session, const boost::filesystem::path& p);
static pointer getById(Wt::Dbo::Session& session, id_type id);
static Wt::Dbo::collection< pointer > getAll(Wt::Dbo::Session& session);
static Wt::Dbo::collection< pointer > getAll(Wt::Dbo::Session& session,
const std::vector<Artist::id_type>& artistIds,
const std::vector<Release::id_type>& releaseIds,
const std::vector<Genre::id_type>& genreIds,
const std::vector<std::string>& artists, // OR filter if many
const std::vector<std::string>& releases, // OR filter if many
const std::vector<std::string>& genres, // OR filter if many
int offset = -1, int size = -1);
static std::vector<std::string> getReleases(Wt::Dbo::Session& session,
const std::vector<std::string>& artists, // OR filter if many
const std::vector<std::string>& genres, // OR filter if many
int offset = -1, int size = -1);
static std::vector<std::string> getArtists(Wt::Dbo::Session& session,
const std::vector<std::string>& genres, // OR filter
int offset = -1, int size = -1);
// Create utility
static pointer create(Wt::Dbo::Session& session, const boost::filesystem::path& p, Artist::pointer artist, Release::pointer release);
static pointer create(Wt::Dbo::Session& session, const boost::filesystem::path& p);
// Accessors
void setTrackNumber(int num) { _trackNumber = num; }
void setDiscNumber(int num) { _discNumber = num; }
void setName(const std::string& name) { _name = std::string(name, 0, _maxNameLength); }
void setArtistName(const std::string& name) { _artistName = std::string(name, 0, _maxNameLength); }
void setReleaseName(const std::string& name) { _releaseName = std::string(name, 0, _maxNameLength); }
void setDuration(boost::posix_time::time_duration duration) { _duration = duration; }
void setLastWriteTime(boost::posix_time::ptime time) { _fileLastWrite = time; }
void setChecksum(const std::vector<unsigned char>& checksum) { _fileChecksum = checksum; }
@@ -200,18 +117,16 @@ class Track
void setOriginalDate(const boost::posix_time::ptime& date) { _originalDate = date; }
void setGenres(const std::string& genreList) { _genreList = genreList; }
void setGenres(std::vector<Genre::pointer> genres);
void setArtist(Artist::pointer artist) { _artist = artist; }
void setRelease(Release::pointer release) { _release = release; }
int getTrackNumber(void) const { return _trackNumber; }
int getDiscNumber(void) const { return _discNumber; }
std::string getName(void) const { return _name; }
std::string getArtistName(void) const { return _artistName; }
std::string getReleaseName(void) const { return _releaseName; }
const std::string& getPath(void) const { return _filePath; }
boost::posix_time::time_duration getDuration(void) const { return _duration; }
boost::posix_time::ptime getDate(void) const { return _date; }
boost::posix_time::ptime getOriginalDate(void) const { return _originalDate; }
Artist::pointer getArtist(void) const { return _artist; }
Release::pointer getRelease(void) const { return _release; }
bool hasGenre(Genre::pointer genre) const { return _genres.count(genre); }
std::vector< Genre::pointer > getGenres(void) const;
@@ -224,6 +139,8 @@ class Track
Wt::Dbo::field(a, _trackNumber, "track_number");
Wt::Dbo::field(a, _discNumber, "disc_number");
Wt::Dbo::field(a, _name, "name");
Wt::Dbo::field(a, _artistName, "artist_name");
Wt::Dbo::field(a, _releaseName, "release_name");
Wt::Dbo::field(a, _duration, "duration");
Wt::Dbo::field(a, _date, "date");
Wt::Dbo::field(a, _originalDate, "original_date");
@@ -232,8 +149,6 @@ class Track
Wt::Dbo::field(a, _fileLastWrite, "last_write");
Wt::Dbo::field(a, _fileChecksum, "checksum");
Wt::Dbo::hasMany(a, _genres, Wt::Dbo::ManyToMany, "track_genre", "", Wt::Dbo::OnDeleteCascade);
Wt::Dbo::belongsTo(a, _release, "release", Wt::Dbo::OnDeleteCascade);
Wt::Dbo::belongsTo(a, _artist, "artist", Wt::Dbo::OnDeleteCascade);
}
private:
@@ -243,6 +158,8 @@ class Track
int _trackNumber;
int _discNumber;
std::string _name;
std::string _artistName;
std::string _releaseName;
boost::posix_time::time_duration _duration;
boost::posix_time::ptime _date;
boost::posix_time::ptime _originalDate;
@@ -251,9 +168,7 @@ class Track
std::vector<unsigned char> _fileChecksum;
boost::posix_time::ptime _fileLastWrite;
Artist::pointer _artist; // Associated Artist
Release::pointer _release; // Associated Release
Wt::Dbo::collection< Genre::pointer > _genres; // Tracks in the release
Wt::Dbo::collection< Genre::pointer > _genres; // Tracks that belong to this genre
};
} // namespace database
+2 -3
View File
@@ -85,9 +85,6 @@ _dbBackend( db.string() )
_session.setConnection(_dbBackend);
_session.mapClass<Database::Genre>("genre");
_session.mapClass<Database::Track>("track");
_session.mapClass<Database::Artist>("artist");
_session.mapClass<Database::Release>("release");
_session.mapClass<Database::Release>("release");
_session.mapClass<Database::Video>("video");
_session.mapClass<Database::MediaDirectory>("media_directory");
_session.mapClass<Database::MediaDirectorySettings>("media_directory_settings");
@@ -99,6 +96,8 @@ _dbBackend( db.string() )
try {
_session.createTables();
_dbBackend.executeSql("CREATE INDEX artist_name_idx ON track(artist_name)");
_dbBackend.executeSql("CREATE INDEX release_name_idx ON track(release_name)");
}
catch(std::exception& e) {
LMS_LOG(MOD_DB, SEV_ERROR) << "Cannot create tables: " << e.what();
-136
View File
@@ -1,136 +0,0 @@
/*
* Copyright (C) 2013 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 <boost/foreach.hpp>
#include "SqlQuery.hpp"
#include "AudioTypes.hpp"
namespace Database {
Release::Release(const std::string& name)
: _name(std::string(name, 0, _maxNameLength))
{
}
Release::pointer
Release::getByName(Wt::Dbo::Session& session, const std::string& name)
{
return session.find<Release>().where("name = ?").bind( std::string(name, 0, _maxNameLength) );
}
Release::pointer
Release::getById(Wt::Dbo::Session& session, id_type id)
{
return session.find<Release>().where("id = ?").bind(id);
}
Release::pointer
Release::getNone(Wt::Dbo::Session& session)
{
pointer res = getByName(session, "<None>");
if (!res)
res = create(session, "<None>");
return res;
}
Release::pointer
Release::create(Wt::Dbo::Session& session, const std::string& name)
{
return session.add(new Release(name));
}
Wt::Dbo::collection<Release::pointer>
Release::getAll(Wt::Dbo::Session& session,
const std::vector<Artist::id_type>& artistIds,
const std::vector<Genre::id_type>& genreIds,
int offset, int size)
{
std::string sqlQuery = "SELECT r FROM release r";
if (!artistIds.empty() || !genreIds.empty())
sqlQuery += " INNER JOIN track t ON t.release_id = r.id";
if (!artistIds.empty())
{
sqlQuery += " INNER JOIN artist a ON a.id = t.artist_id";
}
if (!genreIds.empty())
{
sqlQuery += " INNER JOIN genre g ON g.id = t_g.genre_id";
sqlQuery += " INNER JOIN track_genre t_g ON t_g.track_id = t.id AND t_g.genre_id = g.id";
}
WhereClause where;
{
WhereClause artistWhere;
for (std::size_t i = 0; i < artistIds.size(); ++i)
artistWhere.Or( WhereClause("a.id = ?") );
where.And(artistWhere);
}
{
WhereClause genreWhere;
for (std::size_t i = 0; i < genreIds.size(); ++i)
genreWhere.Or( WhereClause("g.id = ?") );
where.And(genreWhere);
}
Wt::Dbo::Query<Release::pointer> query = session.query<Release::pointer>( sqlQuery + " " + where.get() ).offset(offset).limit(size);
BOOST_FOREACH(const Artist::id_type artistId, artistIds)
query.bind(artistId);
BOOST_FOREACH(const Genre::id_type genreId, genreIds)
query.bind(genreId);
query.groupBy("r");
return query;
}
boost::posix_time::time_duration
Release::getDuration(void) const
{
typedef Wt::Dbo::collection< Wt::Dbo::ptr<Track> > Tracks;
boost::posix_time::time_duration res;
for (Tracks::const_iterator it = _tracks.begin(); it != _tracks.end(); ++it)
res += (*it)->getDuration();
return res;
}
Wt::Dbo::collection<Release::pointer>
Release::getAllOrphans(Wt::Dbo::Session& session)
{
return session.query< Wt::Dbo::ptr<Release> >("select r from release r LEFT OUTER JOIN Track t ON r.id = t.release_id WHERE t.id IS NULL");
}
} // namespace Database
+104 -28
View File
@@ -25,13 +25,11 @@
namespace Database {
Track::Track(const boost::filesystem::path& p, Artist::pointer artist, Release::pointer release)
Track::Track(const boost::filesystem::path& p)
:
_trackNumber(0),
_discNumber(0),
_filePath( p.string() ),
_artist(artist),
_release(release)
_filePath( p.string() )
{
}
@@ -61,9 +59,9 @@ Track::getById(Wt::Dbo::Session& session, id_type id)
Track::pointer
Track::create(Wt::Dbo::Session& session, const boost::filesystem::path& p, Artist::pointer artist, Release::pointer release)
Track::create(Wt::Dbo::Session& session, const boost::filesystem::path& p)
{
return session.add(new Track(p, artist, release) );
return session.add(new Track(p) );
}
Wt::Dbo::collection< Track::pointer >
@@ -82,20 +80,14 @@ Track::getGenres(void) const
Wt::Dbo::collection< Track::pointer >
Track::getAll(Wt::Dbo::Session& session,
const std::vector<Artist::id_type>& artistIds,
const std::vector<Release::id_type>& releaseIds,
const std::vector<Genre::id_type>& genreIds,
const std::vector<std::string>& artists,
const std::vector<std::string>& releases,
const std::vector<std::string>& genres,
int offset, int size)
{
std::string sqlQuery = "SELECT t FROM track t";
if (!artistIds.empty())
sqlQuery += " INNER JOIN artist a ON a.id = t.artist_id";
if (!releaseIds.empty())
sqlQuery += " INNER JOIN release r ON r.id = t.release_id";
if (!genreIds.empty())
if (!genres.empty())
{
sqlQuery += " INNER JOIN genre g ON g.id = t_g.genre_id";
sqlQuery += " INNER JOIN track_genre t_g ON t_g.track_id = t.id AND t_g.genre_id = g.id";
@@ -106,8 +98,8 @@ Track::getAll(Wt::Dbo::Session& session,
{
WhereClause artistWhere;
for (std::size_t i = 0; i < artistIds.size(); ++i)
artistWhere.Or( WhereClause("a.id = ?") );
for (std::size_t i = 0; i < artists.size(); ++i)
artistWhere.Or( WhereClause("t.artist_name = ?") );
where.And(artistWhere);
}
@@ -115,8 +107,8 @@ Track::getAll(Wt::Dbo::Session& session,
{
WhereClause releaseWhere;
for (std::size_t i = 0; i < releaseIds.size(); ++i)
releaseWhere.Or( WhereClause("r.id = ?") );
for (std::size_t i = 0; i < releases.size(); ++i)
releaseWhere.Or( WhereClause("t.release_name = ?") );
where.And(releaseWhere);
}
@@ -124,8 +116,8 @@ Track::getAll(Wt::Dbo::Session& session,
{
WhereClause genreWhere;
for (std::size_t i = 0; i < genreIds.size(); ++i)
genreWhere.Or( WhereClause("g.id = ?") );
for (std::size_t i = 0; i < genres.size(); ++i)
genreWhere.Or( WhereClause("g.name = ?") );
where.And(genreWhere);
}
@@ -133,20 +125,104 @@ Track::getAll(Wt::Dbo::Session& session,
Wt::Dbo::Query<Track::pointer> query = session.query<Track::pointer>( sqlQuery + " " + where.get() ).offset(offset).limit(size);
BOOST_FOREACH(const Artist::id_type artistId, artistIds)
query.bind(artistId);
BOOST_FOREACH(const std::string& artist, artists)
query.bind(artist);
BOOST_FOREACH(const Release::id_type releaseId, releaseIds)
query.bind(releaseId);
BOOST_FOREACH(const std::string& release, releases)
query.bind(release);
BOOST_FOREACH(const Genre::id_type genreId, genreIds)
query.bind(genreId);
BOOST_FOREACH(const std::string& genre, genres)
query.bind(genre);
query.groupBy("t");
return query;
}
std::vector<std::string>
Track::getArtists(Wt::Dbo::Session& session,
const std::vector<std::string>& genres,
int offset, int size)
{
std::string sqlQuery = "SELECT t.artist_name FROM track t";
if (!genres.empty())
{
sqlQuery += " INNER JOIN genre g ON g.id = t_g.genre_id";
sqlQuery += " INNER JOIN track_genre t_g ON t_g.track_id = t.id AND t_g.genre_id = g.id";
}
WhereClause whereClause;
for (std::size_t i = 0; i < genres.size(); ++i)
whereClause.Or( WhereClause("g.name = ?") );
Wt::Dbo::Query<std::string> query = session.query<std::string>( sqlQuery + " " + whereClause.get() ).offset(offset).limit(size).groupBy("t.artist_name");
BOOST_FOREACH(const std::string& genre, genres)
query.bind(genre);
typedef Wt::Dbo::collection< std::string > ArtistNames;
ArtistNames artistNames(query);
std::vector<std::string> res;
for (ArtistNames::const_iterator it = artistNames.begin(); it != artistNames.end(); ++it)
res.push_back((*it));
return res;
}
std::vector<std::string>
Track::getReleases(Wt::Dbo::Session& session,
const std::vector<std::string>& artists,
const std::vector<std::string>& genres,
int offset, int size)
{
std::string sqlQuery = "SELECT t.release_name FROM track t";
if (!genres.empty())
{
sqlQuery += " INNER JOIN genre g ON g.id = t_g.genre_id";
sqlQuery += " INNER JOIN track_genre t_g ON t_g.track_id = t.id AND t_g.genre_id = g.id";
}
WhereClause whereClause;
{
WhereClause clause;
BOOST_FOREACH(const std::string& artist, artists)
clause.Or( WhereClause("t.artist_name = ?") ).bind(artist);
whereClause.And(clause);
}
{
WhereClause clause;
BOOST_FOREACH(const std::string& genre, genres)
clause.Or( WhereClause("g.name = ?") ).bind(genre);
whereClause.And(clause);
}
Wt::Dbo::Query<std::string> query = session.query<std::string>( sqlQuery + " " + whereClause.get() ).offset(offset).limit(size).groupBy("t.release_name");
BOOST_FOREACH(const std::string& bindArg, whereClause.getBindArgs())
query.bind(bindArg);
typedef Wt::Dbo::collection< std::string > ReleaseNames;
ReleaseNames releaseNames(query);
std::vector<std::string> res;
for (ReleaseNames::const_iterator it = releaseNames.begin(); it != releaseNames.end(); ++it)
res.push_back((*it));
return res;
}
} // namespace Database
+17 -23
View File
@@ -20,24 +20,24 @@ message AudioCollectionRequest
{
required BatchParameter batch_parameter = 1;
// Search filters
repeated uint64 genre_id = 2; // Artist that has at least a track of the genre
repeated string genre = 2; // Artist that has at least a track of the genre
}
message GetReleaseList
{
required BatchParameter batch_parameter = 1;
// Search filters
repeated uint64 artist_id = 2; // Release that contains at least a track of one of these artists
repeated uint64 genre_id = 3; // Release that has at least a track of the genre
repeated string artist = 2; // Release that contains at least a track of one of these artists
repeated string genre = 3; // Release that has at least a track of the genre
}
message GetTrackList
{
required BatchParameter batch_parameter = 1;
// Search filters
repeated uint64 artist_id = 2; // Track that belongs to these artists
repeated uint64 release_id = 3; // Track that is part of these releases
repeated uint64 genre_id = 4; // Track that has at least a track of the genre
repeated string artist = 2; // Track that belongs to these artists
repeated string release = 3; // Track that is part of these releases
repeated string genre = 4; // Track that has at least a track of the genre
}
message GetCoverArt
@@ -50,7 +50,7 @@ message AudioCollectionRequest
required Type type = 1;
optional uint64 release_id = 2; // Release that owns the cover art
optional string release = 2; // Release that owns the cover art
optional uint64 track_id = 3; // Track that owns the cover art
required uint32 size = 4; // Scale image to size*size pixels. Set 0 to get the biggest image
@@ -86,22 +86,22 @@ message AudioCollectionResponse
message GenreList
{
repeated Genre genres = 2;
repeated Genre genres = 1;
}
message ArtistList
{
repeated Artist artists = 2;
repeated Artist artists = 1;
}
message ReleaseList
{
repeated Release releases = 2;
repeated Release releases = 1;
}
message TrackList
{
repeated Track tracks = 2;
repeated Track tracks = 1;
}
message CoverArt
@@ -112,32 +112,26 @@ message AudioCollectionResponse
message Genre
{
required uint64 id = 1; // Genre Id
required string name = 2;
required string name = 1;
}
message Artist
{
required uint64 id = 1; // Artist Id
required string name = 2;
required string name = 1;
}
message Release
{
required uint64 id = 1; // Release id
required string name = 2;
required string name = 1;
}
message Track
{
required uint64 id = 1; // Track id
required uint64 artist_id = 2;
required uint64 release_id = 3;
repeated uint64 genre_id = 4;
required string artist = 2;
required string release = 3;
repeated string genre = 4;
optional uint32 disc_number = 5;
optional uint32 track_number = 6;
@@ -143,7 +143,6 @@ AudioCollectionRequestHandler::processGetGenres(const AudioCollectionRequest::Ge
AudioCollectionResponse_Genre* genre = response.add_genres();
genre->set_name( std::string( boost::locale::conv::to_utf<char>((*it)->getName(), "UTF-8") ) );
genre->set_id(it->id());
}
return true;
@@ -165,24 +164,22 @@ AudioCollectionRequestHandler::processGetArtists(const AudioCollectionRequest::G
size = std::min(size, _maxListArtists);
// Get filters
std::vector<Database::Artist::id_type> genreIds;
for (int id = 0; id < request.genre_id_size(); ++id)
genreIds.push_back( request.genre_id(id) );
std::vector<std::string> genres;
for (int id = 0; id < request.genre_size(); ++id)
genres.push_back( request.genre(id) );
// Now fetch requested data...
Wt::Dbo::Transaction transaction( _db.getSession() );
Wt::Dbo::collection<Database::Artist::pointer> artists = Database::Artist::getAll( _db.getSession(), genreIds, request.batch_parameter().offset(), static_cast<int>(size) );
std::vector<std::string> artists
= Database::Track::getArtists(_db.getSession(),
genres,
request.batch_parameter().offset(), static_cast<int>(size) );
typedef Wt::Dbo::collection< Database::Artist::pointer > Artists;
for (Artists::const_iterator it = artists.begin(); it != artists.end(); ++it)
BOOST_FOREACH(const std::string& artist, artists)
{
AudioCollectionResponse_Artist* artist = response.add_artists();
artist->set_name( std::string( boost::locale::conv::to_utf<char>((*it)->getName(), "UTF-8") ) );
artist->set_id(it->id());
response.add_artists()->set_name( std::string( boost::locale::conv::to_utf<char>(artist, "UTF-8") ) );
}
return true;
@@ -203,30 +200,25 @@ AudioCollectionRequestHandler::processGetReleases(const AudioCollectionRequest::
size = _maxListReleases;
size = std::min(size, _maxListReleases);
std::vector<Database::Artist::id_type> artistIds;
for (int id = 0; id < request.artist_id_size(); ++id)
artistIds.push_back( request.artist_id(id) );
std::vector<std::string> artists;
for (int id = 0; id < request.artist_size(); ++id)
artists.push_back( request.artist(id) );
std::vector<Database::Genre::id_type> genreIds;
for (int id = 0; id < request.genre_id_size(); ++id)
genreIds.push_back( request.genre_id(id) );
std::vector<std::string> genres;
for (int id = 0; id < request.genre_size(); ++id)
genres.push_back( request.genre(id) );
Wt::Dbo::Transaction transaction( _db.getSession() );
Wt::Dbo::collection<Database::Release::pointer> releases
= Database::Release::getAll( _db.getSession(),
artistIds,
genreIds,
std::vector<std::string> releases
= Database::Track::getReleases( _db.getSession(),
artists,
genres,
request.batch_parameter().offset(), static_cast<int>(size));
typedef Wt::Dbo::collection< Database::Release::pointer > Releases;
for (Releases::const_iterator it = releases.begin(); it != releases.end(); ++it)
BOOST_FOREACH(const std::string& release, releases)
{
AudioCollectionResponse_Release* release = response.add_releases();
release->set_name( std::string( boost::locale::conv::to_utf<char>((*it)->getName(), "UTF-8") ) );
release->set_id(it->id());
response.add_releases()->set_name( std::string( boost::locale::conv::to_utf<char>(release, "UTF-8")));
}
return true;
@@ -248,27 +240,26 @@ AudioCollectionRequestHandler::processGetTracks(const AudioCollectionRequest::Ge
size = std::min(size, _maxListTracks);
// Get filters
std::vector<Database::Artist::id_type> artistIds;
for (int id = 0; id < request.artist_id_size(); ++id)
artistIds.push_back( request.artist_id(id) );
std::vector<std::string> artists;
for (int id = 0; id < request.artist_size(); ++id)
artists.push_back( request.artist(id) );
std::vector<Database::Release::id_type> releaseIds;
for (int id = 0; id < request.release_id_size(); ++id)
releaseIds.push_back( request.release_id(id) );
std::vector<std::string> releases;
for (int id = 0; id < request.release_size(); ++id)
releases.push_back( request.release(id) );
std::vector<Database::Release::id_type> genreIds;
for (int id = 0; id < request.genre_id_size(); ++id)
genreIds.push_back( request.genre_id(id) );
std::vector<std::string> genres;
for (int id = 0; id < request.genre_size(); ++id)
genres.push_back( request.genre(id) );
Wt::Dbo::Transaction transaction( _db.getSession() );
Wt::Dbo::collection<Database::Track::pointer> tracks
= Database::Track::getAll( _db.getSession(),
artistIds,
releaseIds,
genreIds,
request.batch_parameter().offset(),
static_cast<int>(size));
artists,
releases,
genres,
request.batch_parameter().offset(), static_cast<int>(size));
typedef Wt::Dbo::collection< Database::Track::pointer > Tracks;
@@ -279,8 +270,8 @@ AudioCollectionRequestHandler::processGetTracks(const AudioCollectionRequest::Ge
track->set_id(it->id());
track->set_disc_number( (*it)->getDiscNumber() );
track->set_track_number( (*it)->getTrackNumber() );
track->set_artist_id( (*it)->getArtist().id() );
track->set_release_id( (*it)->getRelease().id() );
track->set_artist( (*it)->getArtistName() );
track->set_release( (*it)->getReleaseName() );
track->set_name( std::string( boost::locale::conv::to_utf<char>((*it)->getName(), "UTF-8") ) );
track->set_duration_secs( (*it)->getDuration().total_seconds() );
@@ -292,7 +283,7 @@ AudioCollectionRequestHandler::processGetTracks(const AudioCollectionRequest::Ge
track->set_original_release_date( std::to_string((*it)->getOriginalDate().date().year()) );
BOOST_FOREACH(Database::Genre::pointer genre, (*it)->getGenres())
track->add_genre_id( genre.id() );
track->add_genre( genre->getName() );
}
@@ -306,74 +297,64 @@ AudioCollectionRequestHandler::processGetCoverArt(const AudioCollectionRequest::
response.set_type(AudioCollectionResponse::TypeCoverArt);
Wt::Dbo::Transaction transaction( _db.getSession() );
Database::Track::pointer track;
switch(request.type())
{
case AudioCollectionRequest::GetCoverArt::TypeGetCoverArtRelease:
if (request.has_release_id())
if (request.has_release())
{
Wt::Dbo::Transaction transaction( _db.getSession() );
Wt::Dbo::collection<Database::Track::pointer> tracks = Database::Track::getAll(_db.getSession(),
std::vector<std::string>(), // artist
std::vector<std::string>(1, request.release()), // release
std::vector<std::string>(), // genre
-1, 1);
// Get the request release
Database::Release::pointer release = Database::Release::getById( _db.getSession(), request.release_id());
Wt::Dbo::collection<Database::Track::pointer>::iterator it = tracks.begin();
std::vector<CoverArt::CoverArt> coverArts = CoverArt::Grabber::getFromRelease(release);
if (it != tracks.end())
track = *it;
BOOST_FOREACH(CoverArt::CoverArt& coverArt, coverArts)
{
AudioCollectionResponse_CoverArt* cover_art = response.add_cover_art();
if (request.has_size())
{
std::size_t size = request.size();
if (size > _maxCoverArtSize || size == 0)
size = _maxCoverArtSize;
if (size < _minCoverArtSize)
size = _minCoverArtSize;
coverArt.scale(size);
}
cover_art->set_mime_type(coverArt.getMimeType());
cover_art->set_data( std::string( coverArt.getData().begin(), coverArt.getData().end()) );
}
res = true;
}
res = true;
break;
case AudioCollectionRequest::GetCoverArt::TypeGetCoverArtTrack:
if (request.has_track_id())
{
Wt::Dbo::Transaction transaction( _db.getSession() );
// Get the request release
Database::Track::pointer track = Database::Track::getById( _db.getSession(), request.track_id());
track = Database::Track::getById( _db.getSession(), request.track_id());
std::vector<CoverArt::CoverArt> coverArts = CoverArt::Grabber::getFromTrack(track);
BOOST_FOREACH(CoverArt::CoverArt& coverArt, coverArts)
{
AudioCollectionResponse_CoverArt* cover_art = response.add_cover_art();
if (request.has_size())
{
std::size_t size = request.size();
if (size > _maxCoverArtSize || size == 0)
size = _maxCoverArtSize;
if (size < _minCoverArtSize)
size = _minCoverArtSize;
coverArt.scale(size);
}
cover_art->set_mime_type(coverArt.getMimeType());
cover_art->set_data( std::string( coverArt.getData().begin(), coverArt.getData().end()) );
}
res = true;
}
res = true;
break;
}
if (!res)
return false;
std::vector<CoverArt::CoverArt> coverArts = CoverArt::Grabber::getFromTrack(track);
BOOST_FOREACH(CoverArt::CoverArt& coverArt, coverArts)
{
AudioCollectionResponse_CoverArt* cover_art = response.add_cover_art();
if (request.has_size())
{
std::size_t size = request.size();
if (size > _maxCoverArtSize || size == 0)
size = _maxCoverArtSize;
if (size < _minCoverArtSize)
size = _minCoverArtSize;
coverArt.scale(size);
}
cover_art->set_mime_type(coverArt.getMimeType());
cover_art->set_data( std::string( coverArt.getData().begin(), coverArt.getData().end()) );
}
return res;
}
+4 -3
View File
@@ -28,6 +28,7 @@
#include "logger/Logger.hpp"
#include "TableFilter.hpp"
#include "TableFilterGenre.hpp"
#include "KeywordSearchFilter.hpp"
#include "TrackView.hpp"
#include "PlayQueue.hpp"
@@ -49,17 +50,17 @@ _mediaPlayer(nullptr)
Wt::WHBoxLayout *filterLayout = new Wt::WHBoxLayout();
{
TableFilter *filter = new TableFilter(_db, "genre", "name");
TableFilterGenre *filter = new TableFilterGenre(_db);
filterLayout->addWidget(filter);
_filterChain.addFilter(filter);
}
{
TableFilter *filter = new TableFilter(_db, "artist", "name");
TableFilter *filter = new TableFilter(_db, "track", "artist_name");
filterLayout->addWidget(filter);
_filterChain.addFilter(filter);
}
{
TableFilter *filter = new TableFilter(_db, "release", "name");
TableFilter *filter = new TableFilter(_db, "track", "release_name");
filterLayout->addWidget(filter);
_filterChain.addFilter(filter);
}
-3
View File
@@ -52,9 +52,6 @@ FilterChain::updateFilters(std::size_t startIdx)
Filter::Constraint currentConstraint;
// TODO: replace with inner joins?
currentConstraint.where.And( WhereClause( "track.artist_id = artist.id and track.release_id = release.id and track_genre.track_id = track.id and genre.id = track_genre.genre_id"));
for (std::size_t idFilter = 0; idFilter < _filters.size(); ++idFilter)
{
Filter* filter = _filters.at(idFilter);
+1 -1
View File
@@ -39,7 +39,7 @@ KeywordSearchFilter::getConstraint(Constraint& constraint)
// No active search means no constaint!
if (!_lastEmittedText.empty()) {
const std::string bindText ("%%" + _lastEmittedText + "%%");
constraint.where.And( WhereClause("(track.name like ? or release.name like ? or artist.name like ?)").bind(bindText).bind(bindText).bind(bindText));
constraint.where.And( WhereClause("(track.name like ? or track.release_name like ? or track.artist_name like ? or track.genre_list like ?)").bind(bindText).bind(bindText).bind(bindText).bind(bindText));
}
// else no constraint!
+1 -1
View File
@@ -87,7 +87,7 @@ PlayQueue::addTracks(const std::vector<Database::Track::id_type>& trackIds)
_model->setData(dataRow, 0, track.id());
_model->setData(dataRow, 1, dataRow + 1);
_model->setData(dataRow, 2, track->getArtist()->getName() + " - " + track->getName());
_model->setData(dataRow, 2, track->getArtistName() + " - " + track->getName());
_model->setData(dataRow, 3, track->getDuration());
}
}
+10 -19
View File
@@ -32,9 +32,9 @@ _db(db),
_table(table),
_field(field)
{
_queryModel.setQuery( _db.getSession().query< ResultType >("select " + _table + "." + _field + ",count(DISTINCT track.id),0 as ORDERBY from track,artist,release,genre,track_genre WHERE track.artist_id = artist.id and track.release_id = release.id and track_genre.track_id = track.id and genre.id = track_genre.genre_id GROUP BY " + _table + "." + _field + " UNION select '<All>',0,1 AS ORDERBY").orderBy("ORDERBY DESC," + _table + "." + _field));
_queryModel.addColumn( _table + "." + _field, table);
_queryModel.addColumn( "count(DISTINCT track.id)", "Tracks");
_queryModel.setQuery( _db.getSession().query< ResultType >("select track." + _field + ", COUNT(DISTINCT track.id) from track GROUP BY track." + _field).orderBy("track." + _field));
_queryModel.addColumn( "track." + _field, field);
_queryModel.addColumn( "COUNT(DISTINCT track.id)", "tracks");
this->setSelectionMode(Wt::ExtendedSelection);
this->setSortingEnabled(false);
@@ -67,19 +67,16 @@ TableFilter::refresh(const Constraint& constraint)
{
SqlQuery sqlQuery;
sqlQuery.select(_table + "." + _field + ",count(DISTINCT track.id),0 as ORDERBY");
sqlQuery.from().And( FromClause("artist,release,track,genre,track_genre")) ;
sqlQuery.select("track." + _field + ", COUNT(DISTINCT track.id)");
sqlQuery.from().And( FromClause("track")) ;
sqlQuery.where().And(constraint.where); // Add constraint made by other filters
sqlQuery.groupBy().And( _table + "." + _field); // Add constraint made by other filters
sqlQuery.groupBy().And( "track." + _field); // Add constraint made by other filters
SqlQuery AllSqlQuery;
AllSqlQuery.select("'<All>',0,1 AS ORDERBY");
LMS_LOG(MOD_UI, SEV_DEBUG) << _table << ", generated query = '" << sqlQuery.get() << "'";
LMS_LOG(MOD_UI, SEV_DEBUG) << _table << ", generated query = '" << sqlQuery.get() + " UNION " + AllSqlQuery.get() << "'";
Wt::Dbo::Query<ResultType> query = _db.getSession().query<ResultType>( sqlQuery.get() );
Wt::Dbo::Query<ResultType> query = _db.getSession().query<ResultType>( sqlQuery.get() + " UNION " + AllSqlQuery.get() );
query.orderBy("ORDERBY DESC," + _table + "." + _field);
query.orderBy(_table + "." + _field);
BOOST_FOREACH(const std::string& bindArg, sqlQuery.where().getBindArgs()) {
LMS_LOG(MOD_UI, SEV_DEBUG) << "Binding value '" << bindArg << "'";
@@ -108,13 +105,7 @@ TableFilter::getConstraint(Constraint& constraint)
const ResultType& result = _queryModel.resultRow( index.row() );
// Get the track part
std::string name( result.get<0>() );
bool isAll( result.get<2>() ) ;
if (isAll) {
// no constraint, just return
return;
}
std::string name(result.get<0>());
clause.Or(_table + "." + _field + " = ?").bind(name);
}
+1 -1
View File
@@ -50,7 +50,7 @@ class TableFilter : public Wt::WTableView, public Filter
const std::string _field;
// Name, track count, special value that means 'all' if set to 1
typedef boost::tuple<std::string, int, int> ResultType;
typedef boost::tuple<std::string, int> ResultType;
Wt::Dbo::QueryModel< ResultType > _queryModel;
};
+119
View File
@@ -0,0 +1,119 @@
/*
* Copyright (C) 2013 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 <boost/foreach.hpp>
#include "logger/Logger.hpp"
#include "TableFilterGenre.hpp"
namespace UserInterface {
TableFilterGenre::TableFilterGenre(Database::Handler& db, Wt::WContainerWidget* parent)
: Wt::WTableView( parent ),
Filter(),
_db(db)
{
_queryModel.setQuery( _db.getSession().query< ResultType >("select genre.name, COUNT(DISTINCT track.id) from genre INNER JOIN track_genre ON track_genre.genre_id = genre.id INNER JOIN track ON track.id = track_genre.track_id").orderBy("genre.name").groupBy("genre.name").orderBy("genre.name"));
_queryModel.addColumn("genre.name", "Genre");
_queryModel.addColumn("COUNT(DISTINCT track.id)", "tracks");
this->setSelectionMode(Wt::ExtendedSelection);
this->setSortingEnabled(false);
this->setAlternatingRowColors(true);
this->setModel(&_queryModel);
this->selectionChanged().connect(this, &TableFilterGenre::emitUpdate);
setLayoutSizeAware(true);
_queryModel.setBatchSize(100);
}
void
TableFilterGenre::layoutSizeChanged (int width, int height)
{
LMS_LOG(MOD_UI, SEV_DEBUG) << "LAYOUT CHANGED!";
/* TODO
std::size_t trackColumnSize = this->columnWidth(1).toPixels() + 30 ;
// Set the remaining size for the name column
this->setColumnWidth(0, width - 7 - trackColumnSize);
*/
}
// Set constraints on this filter
void
TableFilterGenre::refresh(const Constraint& constraint)
{
SqlQuery sqlQuery;
sqlQuery.select("genre.name, COUNT(DISTINCT track.id)");
sqlQuery.from().And( std::string("genre INNER JOIN track_genre ON track_genre.genre_id = genre.id INNER JOIN track ON track.id = track_genre.track_id") );
sqlQuery.where().And(constraint.where); // Add constraint made by other filters
sqlQuery.groupBy().And( std::string("genre.name") );
LMS_LOG(MOD_UI, SEV_DEBUG) << "genre, generated query = '" << sqlQuery.get() << "'";
Wt::Dbo::Query<ResultType> query = _db.getSession().query<ResultType>( sqlQuery.get() );
query.orderBy("genre.name");
BOOST_FOREACH(const std::string& bindArg, sqlQuery.where().getBindArgs()) {
LMS_LOG(MOD_UI, SEV_DEBUG) << "Binding value '" << bindArg << "'";
query.bind(bindArg);
}
_queryModel.setQuery( query, true );
}
// Get constraint created by this filter
void
TableFilterGenre::getConstraint(Constraint& constraint)
{
Wt::WModelIndexSet indexSet = this->selectedIndexes();
// WHERE statement
WhereClause clause;
BOOST_FOREACH(Wt::WModelIndex index, indexSet) {
if (!index.isValid())
continue;
const ResultType& result = _queryModel.resultRow( index.row() );
// Get the track part
std::string name(result.get<0>());
if (name == "<None>")
clause.Or( std::string("track.genre_list = ?") ).bind("");
else
clause.Or( std::string("track.genre_list LIKE ?") ).bind("%%" + name + "%%");
}
// Adding our WHERE clause
constraint.where.And( clause );
}
} // namespace UserInterface
+59
View File
@@ -0,0 +1,59 @@
/*
* Copyright (C) 2013 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/>.
*/
#ifndef TABLE_FILTER_GENRE_HPP
#define TABLE_FILTER_GENRE_HPP
#include <Wt/Dbo/QueryModel>
#include <Wt/WTableView>
#include "Filter.hpp"
#include "database/DatabaseHandler.hpp"
namespace UserInterface {
class TableFilterGenre : public Wt::WTableView, public Filter
{
public:
TableFilterGenre(Database::Handler& db, Wt::WContainerWidget* parent = 0);
// Set constraints on this filter
void refresh(const Constraint& constraint);
// Get constraints created by this filter
void getConstraint(Constraint& constraint);
void layoutSizeChanged (int width, int height);
protected:
Database::Handler& _db;
// genre_name, count
typedef boost::tuple<std::string, int> ResultType;
Wt::Dbo::QueryModel< ResultType > _queryModel;
};
} // namespace UserInterface
#endif
+6 -28
View File
@@ -33,9 +33,9 @@ TrackView::TrackView( Database::Handler& db, Wt::WContainerWidget* parent)
: Wt::WTableView( parent ),
_db(db)
{
_queryModel.setQuery(_db.getSession().query<ResultType>("select track,release,artist from track,release,artist where track.release_id = release.id and track.artist_id = artist.id" ).orderBy("artist.name,release.name,track.disc_number,track.track_number"));
_queryModel.addColumn( "artist.name", "Artist" );
_queryModel.addColumn( "release.name", "Album" );
_queryModel.setQuery(_db.getSession().query<ResultType>("select track from track").orderBy("track.artist_name,track.date,track.release_name,track.disc_number,track.track_number"));
_queryModel.addColumn( "track.artist_name", "Artist" );
_queryModel.addColumn( "track.release_name", "Album" );
_queryModel.addColumn( "track.disc_number", "Disc #" );
_queryModel.addColumn( "track.track_number", "Track #" );
_queryModel.addColumn( "track.name", "Track" );
@@ -107,15 +107,15 @@ TrackView::refresh(const Constraint& constraint)
SqlQuery sqlQuery;
sqlQuery.select( "track,release,artist" );
sqlQuery.from().And( FromClause("artist,release,track,genre,track_genre"));
sqlQuery.select( "track" );
sqlQuery.from().And( FromClause("track"));
sqlQuery.where().And(constraint.where);
LMS_LOG(MOD_UI, SEV_DEBUG) << "TRACK REQ = '" << sqlQuery.get() << "'";
Wt::Dbo::Query<ResultType> query = _db.getSession().query<ResultType>( sqlQuery.get() );
query.groupBy("track").orderBy("artist.name,track.date,release.name,track.disc_number,track.track_number");
query.groupBy("track").orderBy("track.artist_name,track.date,track.release_name,track.disc_number,track.track_number");
BOOST_FOREACH(const std::string& bindArg, sqlQuery.where().getBindArgs()) {
LMS_LOG(MOD_UI, SEV_DEBUG) << "Binding value '" << bindArg << "'";
@@ -126,28 +126,6 @@ TrackView::refresh(const Constraint& constraint)
}
/*
void
TrackView::handleTrackSelected(void)
{
Wt::WModelIndexSet indexSet = this->selectedIndexes();
if (!indexSet.empty()) {
Wt::WModelIndex currentIndex( *indexSet.begin() );
// Check there are remainin tracks!
if (currentIndex.isValid())
{
ResultType result = _queryModel.resultRow( currentIndex.row());
// Get the track part
Wt::Dbo::ptr<Database::Track> track ( result.get<0>() );
_trackSelected.emit( track->getPath() );
}
}
}
*/
void
TrackView::getSelectedTracks(std::vector<Database::Track::id_type>& track_ids)
{
+1 -1
View File
@@ -60,7 +60,7 @@ class TrackView : public Wt::WTableView, public Filter
Database::Handler& _db;
typedef boost::tuple<Database::Track::pointer, Database::Release::pointer, Database::Artist::pointer> ResultType;
typedef boost::tuple<Database::Track::pointer> ResultType;
Wt::Dbo::QueryModel< ResultType > _queryModel;
Wt::WTableView* _tableView;