Simplified db schema
This commit is contained in:
+1
-2
@@ -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 \
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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!
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user