[DB] Added detection for duplicated audio files (mbid or checksum)
This commit is contained in:
@@ -23,6 +23,7 @@
|
|||||||
#include <boost/asio/placeholders.hpp>
|
#include <boost/asio/placeholders.hpp>
|
||||||
|
|
||||||
#include "logger/Logger.hpp"
|
#include "logger/Logger.hpp"
|
||||||
|
#include "utils/Utils.hpp"
|
||||||
|
|
||||||
#include "database/Types.hpp"
|
#include "database/Types.hpp"
|
||||||
|
|
||||||
@@ -244,7 +245,9 @@ Updater::process(boost::system::error_code err)
|
|||||||
LMS_LOG(DBUPDATER, INFO) << "Processing root directory '" << rootDirectory.path << "' DONE";
|
LMS_LOG(DBUPDATER, INFO) << "Processing root directory '" << rootDirectory.path << "' DONE";
|
||||||
}
|
}
|
||||||
|
|
||||||
LMS_LOG(DBUPDATER, INFO) << "Scan complete. Changes = " << stats.nbChanges() << ", Errors = " << stats.nbScanErrors;
|
checkDuplicatedAudioFiles(stats);
|
||||||
|
|
||||||
|
LMS_LOG(DBUPDATER, INFO) << "Scan complete. Changes = " << stats.nbChanges() << "(added = " << stats.nbAdded << ", nbRemoved = " << stats.nbRemoved << ", nbModified = " << stats.nbModified << "), Errors = " << stats.nbScanErrors;
|
||||||
|
|
||||||
// Update database stats
|
// Update database stats
|
||||||
boost::posix_time::ptime now = boost::posix_time::second_clock::local_time();
|
boost::posix_time::ptime now = boost::posix_time::second_clock::local_time();
|
||||||
@@ -394,6 +397,9 @@ Updater::processAudioFile( const boost::filesystem::path& file, Stats& stats)
|
|||||||
if (!_metadataParser.parse(file, items))
|
if (!_metadataParser.parse(file, items))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
std::vector<unsigned char> checksum ;
|
||||||
|
computeCrc(file, checksum);
|
||||||
|
|
||||||
Wt::Dbo::Transaction transaction(_db.getSession());
|
Wt::Dbo::Transaction transaction(_db.getSession());
|
||||||
|
|
||||||
Wt::Dbo::ptr<Track> track = Track::getByPath(_db.getSession(), file);
|
Wt::Dbo::ptr<Track> track = Track::getByPath(_db.getSession(), file);
|
||||||
@@ -502,6 +508,7 @@ Updater::processAudioFile( const boost::filesystem::path& file, Stats& stats)
|
|||||||
|
|
||||||
assert(track);
|
assert(track);
|
||||||
|
|
||||||
|
track.modify()->setChecksum(checksum);
|
||||||
track.modify()->setArtist(artist);
|
track.modify()->setArtist(artist);
|
||||||
track.modify()->setRelease(release);
|
track.modify()->setRelease(release);
|
||||||
track.modify()->setLastWriteTime(lastWriteTime);
|
track.modify()->setLastWriteTime(lastWriteTime);
|
||||||
@@ -719,6 +726,29 @@ Updater::checkAudioFiles( Stats& stats )
|
|||||||
LMS_LOG(DBUPDATER, INFO) << "Check audio files done!";
|
LMS_LOG(DBUPDATER, INFO) << "Check audio files done!";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Updater::checkDuplicatedAudioFiles(Stats& stats)
|
||||||
|
{
|
||||||
|
LMS_LOG(DBUPDATER, INFO) << "Checking duplicated audio files";
|
||||||
|
|
||||||
|
Wt::Dbo::Transaction transaction(_db.getSession());
|
||||||
|
|
||||||
|
std::vector<Track::pointer> tracks = Database::Track::getMBIDDuplicates(_db.getSession());
|
||||||
|
for (Track::pointer track : tracks)
|
||||||
|
{
|
||||||
|
LMS_LOG(DBUPDATER, INFO) << "Found duplicated MBID [" << track->getMBID() << "], file: " << track->getPath() << " - " << track->getArtist()->getName() << " - " << track->getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
tracks = Database::Track::getChecksumDuplicates(_db.getSession());
|
||||||
|
for (Track::pointer track : tracks)
|
||||||
|
{
|
||||||
|
LMS_LOG(DBUPDATER, INFO) << "Found duplicated checksum [" << bufferToString(track->getChecksum()) << "], file: " << track->getPath() << " - " << track->getArtist()->getName() << " - " << track->getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LMS_LOG(DBUPDATER, INFO) << "Checking duplicated audio files done!";
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Updater::checkVideoFiles( Stats& stats )
|
Updater::checkVideoFiles( Stats& stats )
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ class Updater
|
|||||||
|
|
||||||
// Audio
|
// Audio
|
||||||
void checkAudioFiles( Stats& stats );
|
void checkAudioFiles( Stats& stats );
|
||||||
|
void checkDuplicatedAudioFiles( Stats& stats );
|
||||||
void processAudioFile( const boost::filesystem::path& file, Stats& stats);
|
void processAudioFile( const boost::filesystem::path& file, Stats& stats);
|
||||||
|
|
||||||
// Video
|
// Video
|
||||||
|
|||||||
@@ -87,6 +87,20 @@ Track::getAllPaths(Wt::Dbo::Session& session)
|
|||||||
return std::vector<boost::filesystem::path>(res.begin(), res.end());
|
return std::vector<boost::filesystem::path>(res.begin(), res.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<Track::pointer>
|
||||||
|
Track::getMBIDDuplicates(Wt::Dbo::Session& session)
|
||||||
|
{
|
||||||
|
Wt::Dbo::collection<pointer> res = session.query<pointer>( "SELECT track FROM track WHERE mbid in (SELECT mbid FROM track WHERE mbid <> '' GROUP BY mbid HAVING COUNT (*) > 1)").orderBy("track.mbid");
|
||||||
|
return std::vector<pointer>(res.begin(), res.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Track::pointer>
|
||||||
|
Track::getChecksumDuplicates(Wt::Dbo::Session& session)
|
||||||
|
{
|
||||||
|
Wt::Dbo::collection<pointer> res = session.query<pointer>( "SELECT track FROM track WHERE checksum in (SELECT checksum FROM track WHERE Length(checksum) > 0 GROUP BY checksum HAVING COUNT(*) > 1)").orderBy("track.checksum");
|
||||||
|
return std::vector<pointer>(res.begin(), res.end());
|
||||||
|
}
|
||||||
|
|
||||||
std::vector< Genre::pointer >
|
std::vector< Genre::pointer >
|
||||||
Track::getGenres(void) const
|
Track::getGenres(void) const
|
||||||
{
|
{
|
||||||
@@ -167,6 +181,7 @@ Track::updateUIQueryModel(Wt::Dbo::Session& session, Wt::Dbo::QueryModel< UIQuer
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Genre::Genre()
|
Genre::Genre()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ class Genre
|
|||||||
static pointer getNone(Wt::Dbo::Session& session);
|
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 std::vector<pointer> getByFilter(Wt::Dbo::Session& session, SearchFilter filter, int offset = -1, int size = -1);
|
||||||
static Wt::Dbo::collection<Genre::pointer> getAll(Wt::Dbo::Session& session);
|
static Wt::Dbo::collection<Genre::pointer> getAll(Wt::Dbo::Session& session);
|
||||||
|
|
||||||
// MVC models for the user interface
|
// MVC models for the user interface
|
||||||
// Genre ID, name, track count
|
// Genre ID, name, track count
|
||||||
typedef boost::tuple<id_type, std::string, int> UIQueryResult;
|
typedef boost::tuple<id_type, std::string, int> UIQueryResult;
|
||||||
@@ -106,6 +107,8 @@ class Track
|
|||||||
static std::vector<pointer> getByFilter(Wt::Dbo::Session& session, SearchFilter filter, int offset = -1, int size = -1);
|
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 > getAll(Wt::Dbo::Session& session);
|
||||||
static std::vector<boost::filesystem::path> getAllPaths(Wt::Dbo::Session& session);
|
static std::vector<boost::filesystem::path> getAllPaths(Wt::Dbo::Session& session);
|
||||||
|
static std::vector<pointer> getMBIDDuplicates(Wt::Dbo::Session& session);
|
||||||
|
static std::vector<pointer> getChecksumDuplicates(Wt::Dbo::Session& session);
|
||||||
|
|
||||||
// Utility fonctions
|
// Utility fonctions
|
||||||
// MVC models for the user interface
|
// MVC models for the user interface
|
||||||
|
|||||||
@@ -118,4 +118,16 @@ stringToUTF8(const std::string& str)
|
|||||||
return boost::locale::conv::to_utf<char>(str, "UTF-8");
|
return boost::locale::conv::to_utf<char>(str, "UTF-8");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
bufferToString(const std::vector<unsigned char>& data)
|
||||||
|
{
|
||||||
|
std::ostringstream oss;
|
||||||
|
|
||||||
|
for (unsigned char c : data)
|
||||||
|
{
|
||||||
|
oss << std::setw(2) << std::setfill('0') << std::hex << (int)c;
|
||||||
|
}
|
||||||
|
|
||||||
|
return oss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -44,6 +44,9 @@ stringTrim(const std::string& str, const std::string& whitespace = " \t");
|
|||||||
std::string
|
std::string
|
||||||
stringToUTF8(const std::string& str);
|
stringToUTF8(const std::string& str);
|
||||||
|
|
||||||
|
std::string
|
||||||
|
bufferToString(const std::vector<unsigned char>& data);
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
static inline bool readAs(const std::string& str, T& data)
|
static inline bool readAs(const std::string& str, T& data)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user