From 83efc750eb4464ef57ad3762d189292ee864f1f8 Mon Sep 17 00:00:00 2001 From: Kevin Whitaker <eyecreate@gmail.com> Date: Mon, 30 Jan 2017 22:48:13 -0500 Subject: [PATCH] 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. --- CMakeLists.txt | 2 +- cmake/FindGroove.cmake | 6 ++- src/GroovePlayer.cpp | 86 +++++++++++++++++++++++++++++++++++++++++- src/GroovePlayer.h | 4 +- src/db/AudioTrack.h | 4 ++ src/db/UserAction.h | 4 +- 6 files changed, 99 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 38cd3ff..b6e38ba 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/cmake/FindGroove.cmake b/cmake/FindGroove.cmake index 144c77e..21f15c3 100644 --- a/cmake/FindGroove.cmake +++ b/cmake/FindGroove.cmake @@ -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) diff --git a/src/GroovePlayer.cpp b/src/GroovePlayer.cpp index 5783cac..6cef18d 100644 --- a/src/GroovePlayer.cpp +++ b/src/GroovePlayer.cpp @@ -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(); } diff --git a/src/GroovePlayer.h b/src/GroovePlayer.h index 85493e5..ac66b5f 100644 --- a/src/GroovePlayer.h +++ b/src/GroovePlayer.h @@ -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(); }; diff --git a/src/db/AudioTrack.h b/src/db/AudioTrack.h index 8d45990..4a10aad 100644 --- a/src/db/AudioTrack.h +++ b/src/db/AudioTrack.h @@ -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"); } diff --git a/src/db/UserAction.h b/src/db/UserAction.h index e720e86..3ff381e 100644 --- a/src/db/UserAction.h +++ b/src/db/UserAction.h @@ -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"); } -- GitLab