[DB] Switched from ffmpeg to TagLib in order to get tags
This commit is contained in:
@@ -34,41 +34,44 @@ Classifier::Classifier(Wt::Dbo::SqlConnectionPool& connectionPool)
|
||||
{}
|
||||
|
||||
void
|
||||
Classifier::processTrackUpdate(bool added, Track::id_type trackId)
|
||||
Classifier::processTrackUpdate(bool added, Track::id_type trackId, std::string mbid, boost::filesystem::path path)
|
||||
{
|
||||
if (!added)
|
||||
return;
|
||||
|
||||
LMS_LOG(DBUPDATER, DEBUG) << "Processing track id " << trackId;
|
||||
|
||||
boost::filesystem::path path;
|
||||
|
||||
if (mbid.empty())
|
||||
{
|
||||
Wt::Dbo::Transaction transaction(_db.getSession());
|
||||
|
||||
Track::pointer track = Database::Track::getById(_db.getSession(), trackId);
|
||||
if (!track)
|
||||
return;
|
||||
|
||||
path = track->getPath();
|
||||
|
||||
// Remove outdated features
|
||||
for (auto feature : track->getFeatures())
|
||||
feature.remove();
|
||||
// TODO compute from file
|
||||
LMS_LOG(CLASSIFICATION, INFO) << "File '" << path << "' has no MBID: skipping feature extraction";
|
||||
return;
|
||||
}
|
||||
|
||||
boost::property_tree::ptree pt;
|
||||
if (!::Feature::Extractor::getLowLevel(pt, path))
|
||||
return;
|
||||
|
||||
std::ostringstream oss;
|
||||
boost::property_tree::write_json(oss, pt);
|
||||
|
||||
if (::Feature::Extractor::getLowLevel(pt, mbid))
|
||||
{
|
||||
Wt::Dbo::Transaction transaction(_db.getSession());
|
||||
std::ostringstream oss;
|
||||
boost::property_tree::write_json(oss, pt);
|
||||
|
||||
Track::pointer track = Database::Track::getById(_db.getSession(), trackId);
|
||||
Feature::pointer feature = Feature::create( _db.getSession(), track, "low_level", oss.str());
|
||||
{
|
||||
Wt::Dbo::Transaction transaction(_db.getSession());
|
||||
|
||||
Track::pointer track = Database::Track::getById(_db.getSession(), trackId);
|
||||
Feature::create( _db.getSession(), track, "low_level", oss.str());
|
||||
}
|
||||
}
|
||||
|
||||
pt.clear();
|
||||
if (::Feature::Extractor::getHighLevel(pt, mbid))
|
||||
{
|
||||
std::ostringstream oss;
|
||||
boost::property_tree::write_json(oss, pt);
|
||||
|
||||
{
|
||||
Wt::Dbo::Transaction transaction(_db.getSession());
|
||||
|
||||
Track::pointer track = Database::Track::getById(_db.getSession(), trackId);
|
||||
Feature::create( _db.getSession(), track, "high_level", oss.str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -816,11 +819,39 @@ Classifier::processDatabaseUpdate(Updater::Stats stats)
|
||||
trackClusters[coordinates.first][coordinates.second].push_back({trackIds[id], entry, maxValue});
|
||||
}
|
||||
|
||||
// Create clusters
|
||||
LMS_LOG(DBUPDATER, DEBUG) << "Erasing old clusters";
|
||||
{
|
||||
Wt::Dbo::Transaction transaction(_db.getSession());
|
||||
Cluster::removeByType(_db.getSession(), "similarity");
|
||||
}
|
||||
|
||||
LMS_LOG(DBUPDATER, DEBUG) << "Creating new cluster...";
|
||||
for (std::size_t i = 0; i < nbRows; i++)
|
||||
{
|
||||
for (std::size_t j = 0; j < nbColumns; j++)
|
||||
{
|
||||
LMS_LOG(DBUPDATER, DEBUG) << "Creating cluster " << i << " " << j;
|
||||
|
||||
Wt::Dbo::Transaction transaction(_db.getSession());
|
||||
|
||||
Cluster::pointer cluster = Cluster::create(_db.getSession(), "similarity", "cluster_" + std::to_string(i) + "_" + std::to_string(j));
|
||||
|
||||
for (auto track : trackClusters[i][j])
|
||||
{
|
||||
Track::pointer t = Database::Track::getById(_db.getSession(), track.trackId);
|
||||
cluster.modify()->addTrack(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (std::size_t i = 0; i < nbRows; i++)
|
||||
{
|
||||
for (std::size_t j = 0; j < nbColumns; j++)
|
||||
{
|
||||
|
||||
|
||||
std::cout << "Cluster [" << i << "," << j << "] - ";
|
||||
|
||||
// Display the neuron for this cluster
|
||||
|
||||
@@ -42,7 +42,7 @@ class Classifier
|
||||
public:
|
||||
Classifier(Wt::Dbo::SqlConnectionPool& connectionPool);
|
||||
|
||||
void processTrackUpdate(bool added, Track::id_type trackId);
|
||||
void processTrackUpdate(bool added, Track::id_type trackId, std::string mbid, boost::filesystem::path p);
|
||||
void processDatabaseUpdate(Updater::Stats stats);
|
||||
|
||||
private:
|
||||
|
||||
@@ -103,9 +103,11 @@ Handler::Handler(Wt::Dbo::SqlConnectionPool& connectionPool)
|
||||
_session.execute("CREATE INDEX artist_name_idx ON artist(name)");
|
||||
_session.execute("CREATE INDEX cluster_name_idx ON cluster(name)");
|
||||
_session.execute("CREATE INDEX cluster_type_idx ON cluster(type)");
|
||||
_session.execute("CREATE INDEX cluster_name_type_idx ON cluster(name, type)");
|
||||
_session.execute("CREATE INDEX release_name_idx ON release(name)");
|
||||
_session.execute("CREATE INDEX track_name_idx ON track(name)");
|
||||
_session.execute("CREATE INDEX feature_type_idx ON feature(type)");
|
||||
_session.execute("CREATE INDEX feature_track_type_idx ON feature(track_id,type)");
|
||||
}
|
||||
catch(std::exception& e) {
|
||||
LMS_LOG(DB, ERROR) << "Cannot create tables: " << e.what();
|
||||
|
||||
@@ -537,6 +537,14 @@ Updater::processAudioFile( const boost::filesystem::path& file, Stats& stats)
|
||||
else
|
||||
{
|
||||
LMS_LOG(DBUPDATER, INFO) << "Updating '" << file << "'";
|
||||
|
||||
// TODO Remove the songs from its clusters
|
||||
// TODO Remove the features of this song
|
||||
track.remove();
|
||||
track.flush();
|
||||
|
||||
track = Track::create(_db->getSession(), file);
|
||||
|
||||
stats.nbModified++;
|
||||
}
|
||||
|
||||
@@ -589,9 +597,9 @@ Updater::processAudioFile( const boost::filesystem::path& file, Stats& stats)
|
||||
track.modify()->setDate( boost::any_cast<boost::posix_time::ptime>(items[MetaData::Type::OriginalDate]) );
|
||||
}
|
||||
|
||||
if (items.find(MetaData::Type::MusicBrainzTrackID) != items.end())
|
||||
if (items.find(MetaData::Type::MusicBrainzRecordingID) != items.end())
|
||||
{
|
||||
track.modify()->setMBID( boost::any_cast<std::string>(items[MetaData::Type::MusicBrainzTrackID]) );
|
||||
track.modify()->setMBID( boost::any_cast<std::string>(items[MetaData::Type::MusicBrainzRecordingID]) );
|
||||
}
|
||||
|
||||
if (items.find(MetaData::Type::HasCover) != items.end())
|
||||
@@ -603,7 +611,7 @@ Updater::processAudioFile( const boost::filesystem::path& file, Stats& stats)
|
||||
|
||||
transaction.commit();
|
||||
|
||||
_sigTrackChanged.emit(true, track.id());
|
||||
_sigTrackChanged.emit(true, track.id(), track->getMBID(), track->getPath());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -19,14 +19,14 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Wt/WIOService>
|
||||
#include <Wt/WSignal>
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include <boost/asio/deadline_timer.hpp>
|
||||
|
||||
#include "metadata/AvFormat.hpp"
|
||||
#include <Wt/WIOService>
|
||||
#include <Wt/WSignal>
|
||||
|
||||
#include "metadata/TagLibParser.hpp"
|
||||
|
||||
#include "database/DatabaseHandler.hpp"
|
||||
|
||||
@@ -65,7 +65,11 @@ class Updater
|
||||
|
||||
// Emitted when a track changed
|
||||
// true -> added or modified, false -> to be deleted
|
||||
Wt::Signal<bool, Track::id_type>& trackChanged() { return _sigTrackChanged; }
|
||||
// id of the track
|
||||
// musicbrainz recordid
|
||||
// path of the track
|
||||
typedef Wt::Signal<bool, Track::id_type, std::string, boost::filesystem::path> SigTrackChanged;
|
||||
SigTrackChanged& trackChanged() { return _sigTrackChanged; }
|
||||
|
||||
std::mutex& getMutex(void) { return _mutex; }
|
||||
|
||||
@@ -117,7 +121,7 @@ class Updater
|
||||
Wt::Signal<Stats> _sigScanComplete;
|
||||
Wt::Signal<bool, Artist::id_type> _sigArtistChanged;
|
||||
Wt::Signal<bool, Release::id_type> _sigReleaseChanged;
|
||||
Wt::Signal<bool, Track::id_type> _sigTrackChanged;
|
||||
SigTrackChanged _sigTrackChanged;
|
||||
std::mutex _mutex;
|
||||
|
||||
boost::asio::deadline_timer _scheduleTimer;
|
||||
@@ -127,7 +131,7 @@ class Updater
|
||||
std::vector<boost::filesystem::path> _audioFileExtensions;
|
||||
std::vector<boost::filesystem::path> _videoFileExtensions;
|
||||
|
||||
MetaData::AvFormat _metadataParser;
|
||||
MetaData::TagLibParser _metadataParser;
|
||||
|
||||
|
||||
}; // class Updater
|
||||
|
||||
@@ -247,6 +247,13 @@ Cluster::create(Wt::Dbo::Session& session, std::string type, std::string name)
|
||||
return session.add(new Cluster(type, name));
|
||||
}
|
||||
|
||||
void
|
||||
Cluster::removeByType(Wt::Dbo::Session& session, std::string type)
|
||||
{
|
||||
session.execute( "DELETE FROM cluster WHERE type = ?").bind(type);
|
||||
}
|
||||
|
||||
|
||||
Wt::Dbo::Query<Cluster::pointer>
|
||||
Cluster::getQuery(Wt::Dbo::Session& session, SearchFilter filter)
|
||||
{
|
||||
@@ -318,6 +325,5 @@ Feature::getByTrack(Wt::Dbo::Session& session, Track::id_type trackId, const std
|
||||
return std::vector<pointer>(res.begin(), res.end());
|
||||
}
|
||||
|
||||
|
||||
} // namespace Database
|
||||
|
||||
|
||||
@@ -55,6 +55,7 @@ class Cluster
|
||||
static pointer getNone(Wt::Dbo::Session& session);
|
||||
static std::vector<pointer> getByFilter(Wt::Dbo::Session& session, SearchFilter filter, int offset = -1, int size = -1);
|
||||
static Wt::Dbo::collection<pointer> getAll(Wt::Dbo::Session& session);
|
||||
static Wt::Dbo::collection<pointer> getByType(Wt::Dbo::Session& session, std::stirng type);
|
||||
|
||||
// MVC models for the user interface
|
||||
// ClusterID, type, name, track count
|
||||
@@ -65,12 +66,16 @@ class Cluster
|
||||
// Create utility
|
||||
static pointer create(Wt::Dbo::Session& session, std::string type, std::string name);
|
||||
|
||||
// Remove utility
|
||||
static void removeByType(Wt::Dbo::Session& session, std::string type);
|
||||
|
||||
// Accessors
|
||||
const std::string& getName(void) const { return _name; }
|
||||
const std::string& getType(void) const { return _type; }
|
||||
bool isNone(void) const;
|
||||
const Wt::Dbo::collection< Wt::Dbo::ptr<Track> >& getTracks() const { return _tracks;}
|
||||
|
||||
void addTrack(Wt::Dbo::dbo_traits<Track>::IdType trackId);
|
||||
void addTrack(Wt::Dbo::ptr<Track> track) { _tracks.insert(track); }
|
||||
|
||||
template<class Action>
|
||||
|
||||
Reference in New Issue
Block a user