Add code to add track to DB and remove old ones that are gone. Added fingerprint lib to link for id-ing tracks. Make sure DB is logical after these changes. Change music path due to complaints I don't quite understand.

This commit is contained in:
Kevin Whitaker
2017-01-30 22:48:13 -05:00
parent 50f30d494f
commit 83efc750eb
6 changed files with 99 additions and 7 deletions

View File

@@ -9,6 +9,6 @@ find_package(Wt REQUIRED)
find_package(Groove REQUIRED)
add_executable(arbitrateor src/main.cpp src/WebInterface.cpp src/GroovePlayer.cpp src/ui/LoginInterface.cpp src/ui/PlayerInterface.cpp)
target_link_libraries(arbitrateor ${Wt_LIBRARIES} ${GROOVE_LIBRARY} pthread stdc++fs) #TODO get threading links based on platform. Remove gcc experimental fs when official c++17 exists.
target_link_libraries(arbitrateor ${Wt_LIBRARIES} ${GROOVE_LIBRARY} ${GROOVE_FINGERPRINT_LIBRARY} pthread stdc++fs) #TODO get threading links based on platform. Remove gcc experimental fs when official c++17 exists.
install(TARGETS arbitrateor RUNTIME DESTINATION bin)

View File

@@ -5,12 +5,14 @@
# GROOVE_FOUND
# GROOVE_INCLUDE_DIR
# GROOVE_LIBRARY
# GROOVE_FINGERPRINT_LIBRARY
find_path(GROOVE_INCLUDE_DIR NAMES groove/groove.h)
find_library(GROOVE_LIBRARY NAMES groove)
find_library(GROOVE_FINGERPRINT_LIBRARY NAMES groovefingerprinter)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(GROOVE DEFAULT_MSG GROOVE_LIBRARY GROOVE_INCLUDE_DIR)
find_package_handle_standard_args(GROOVE DEFAULT_MSG GROOVE_LIBRARY GROOVE_FINGERPRINT_LIBRARY GROOVE_INCLUDE_DIR)
mark_as_advanced(GROOVE_INCLUDE_DIR GROOVE_LIBRARY)
mark_as_advanced(GROOVE_INCLUDE_DIR GROOVE_LIBRARY GROOVE_FINGERPRINT_LIBRARY)

View File

@@ -25,6 +25,8 @@
#include "WebInterface.h"
#include <locale>
#include <Wt/WLogger>
#include <Wt/Dbo/Transaction>
#include <groovefingerprinter/fingerprinter.h>
std::filesystem::path GroovePlayer::musicScanDir = "";
@@ -42,6 +44,8 @@ GroovePlayer::GroovePlayer(std::string dbFile) : sqliteConnection(dbFile)
Wt::log("info") << "Using Existing DB.";
}
groove_init();
grooveAudioScanner = new std::thread(grooveAudioScannerLoop);
grooveEvents = new std::thread(grooveEventLoop);
}
@@ -59,11 +63,85 @@ void GroovePlayer::grooveEventLoop()
bool GroovePlayer::addFileToTrackDBIfTagged(std::filesystem::path file)
{
//Now check if tags exist and put into DB.
struct GrooveFile* gfile = groove_file_open(file.c_str());
struct GrooveTag* artist_tag = groove_file_metadata_get(gfile, "artist", nullptr, 0);
struct GrooveTag* album_tag = groove_file_metadata_get(gfile, "album", nullptr, 0);
struct GrooveTag* name_tag = groove_file_metadata_get(gfile, "title", nullptr, 0);
struct GrooveTag* genre_tag = groove_file_metadata_get(gfile, "genre", nullptr, 0);
if(artist_tag == nullptr || album_tag == nullptr || name_tag == nullptr || genre_tag == nullptr)
{
//Only accept song with all metadata for DB.
groove_file_close(gfile);
return false;
}
//Take fingerprint and compare to DB.
struct GrooveFingerprinter* fingerprinter = groove_fingerprinter_create();
struct GrooveFingerprinterInfo info;
struct GroovePlaylist* playlist = groove_playlist_create();
groove_playlist_insert(playlist,gfile,1.0,1.0,nullptr);
groove_fingerprinter_attach(fingerprinter, playlist);
groove_fingerprinter_info_get(fingerprinter, &info, 1);
double trackLen = info.duration;
char* encodedFP;
groove_fingerprinter_encode(info.fingerprint,info.fingerprint_size,&encodedFP);
std::string fingerprint = std::string(encodedFP);
groove_fingerprinter_dealloc(encodedFP);
groove_fingerprinter_free_info(&info);
groove_fingerprinter_detach(fingerprinter);
groove_fingerprinter_destroy(fingerprinter);
groove_playlist_destroy(playlist);
Wt::Dbo::Transaction transaction(getInstance()->sqlSession);
int existing = getInstance()->sqlSession.query<int>("select count(fingerprint) from tracks").where("fingerprint = ?").bind(fingerprint);
if(existing >0)
{
//This track has a duplicate already.
groove_file_close(gfile);
return false;
}
//Add to DB.
AudioTrack* newTrack = new AudioTrack();
newTrack->trackName = groove_tag_value(name_tag);
newTrack->trackAlbumName = groove_tag_value(album_tag);
newTrack->trackArtistName = groove_tag_value(artist_tag);
newTrack->trackGenre = groove_tag_value(genre_tag);
newTrack->trackLengthSeconds = trackLen;
newTrack->trackFingerprint = fingerprint;
newTrack->trackPath = file.string();
getInstance()->sqlSession.add(newTrack);
groove_file_close(gfile);
return true;
}
void GroovePlayer::removeOrphanedTracks()
{
Wt::Dbo::Transaction transaction(getInstance()->sqlSession);
Wt::Dbo::collection<Wt::Dbo::ptr<AudioTrack>> tracks = getInstance()->sqlSession.find<AudioTrack>();
for(Wt::Dbo::collection<Wt::Dbo::ptr<AudioTrack>>::const_iterator i = tracks.begin(); i != tracks.end(); ++i)
{
if(!std::filesystem::exists(std::filesystem::path((*i)->trackPath)))
{
Wt::Dbo::ptr<AudioTrack> item = getInstance()->sqlSession.find<AudioTrack>().where("fingerprint = ?").bind((*i)->trackFingerprint);
Wt::log("info") << (*i)->trackPath + " not found in filesystem. Removed from DB.";
item.remove();
}
}
}
void GroovePlayer::grooveAudioScannerLoop()
{
if(!std::filesystem::exists(musicScanDir))
{
std::filesystem::create_directory(musicScanDir);
Wt::log("info") << "Directory "+std::filesystem::canonical(musicScanDir).string()+" didn't exist, so created.";
} else
{
Wt::log("info") << "Directory "+std::filesystem::canonical(musicScanDir).string()+" exists.";
}
for(std::filesystem::directory_entry p: std::filesystem::directory_iterator(musicScanDir))
{
std::string extensionLowered;
@@ -71,14 +149,20 @@ void GroovePlayer::grooveAudioScannerLoop()
{
extensionLowered.push_back(std::tolower(elem));
}
if(extensionLowered == "mp3") //TODO:think about supporting more than mp3s.
if(extensionLowered == ".mp3") //TODO:think about supporting more than mp3s.
{
if(addFileToTrackDBIfTagged(p.path()))
{
Wt::log("info") << p.path().string() << " was added to DB";
}
}
else
{
Wt::log("info") << p.path().string() + " was not an accepted audio file.";
}
}
//Now check for tracks in DB without a file.
removeOrphanedTracks();
}

View File

@@ -26,6 +26,7 @@
#include <thread>
#include "db/User.h"
#include "db/AudioTrack.h"
#include <groove/groove.h>
#include <experimental/filesystem> //TODO:Change to non-gcc way when officially using c++17
namespace std {
namespace filesystem = experimental::filesystem;
@@ -37,7 +38,7 @@ public:
static std::filesystem::path musicScanDir;
static GroovePlayer* getInstance() {
static GroovePlayer instance("music.db");
musicScanDir = std::filesystem::current_path().string()+"/music";
musicScanDir = std::filesystem::path("music");
return &instance;
};
GroovePlayer(GroovePlayer const&) = delete;
@@ -54,6 +55,7 @@ private:
std::thread* grooveAudioScanner;
static void grooveAudioScannerLoop();
static bool addFileToTrackDBIfTagged(std::filesystem::path file);
static void removeOrphanedTracks();
};

View File

@@ -29,6 +29,8 @@ public:
std::string trackArtistName;
std::string trackAlbumName;
std::string trackGenre;
double trackLengthSeconds;
std::string trackFingerprint;
std::string trackPath;
template<class Action>
@@ -37,6 +39,8 @@ public:
Wt::Dbo::field(a, trackName, "name");
Wt::Dbo::field(a, trackArtistName, "artist");
Wt::Dbo::field(a, trackAlbumName, "album");
Wt::Dbo::field(a, trackLengthSeconds, "length");
Wt::Dbo::field(a, trackFingerprint, "fingerprint");
Wt::Dbo::field(a, trackGenre, "genre");
Wt::Dbo::field(a, trackPath, "path");
}

View File

@@ -39,9 +39,9 @@ public:
template<class Action>
void persist(Action& a)
{
Wt::Dbo::belongsTo(a, user, "user");
Wt::Dbo::belongsTo(a, user, "user", Wt::Dbo::OnDeleteCascade);
Wt::Dbo::field(a, action, "action");
Wt::Dbo::belongsTo(a, trackInvolved, "track");
Wt::Dbo::belongsTo(a, trackInvolved, "track", Wt::Dbo::OnDeleteCascade);
Wt::Dbo::field(a, datetime, "datetime");
}