/* * Copyright (C) 2017 Kevin Whitaker * * This program 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 2 of the License, or * (at your option) any later version. * * This program 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 this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ #include "GroovePlayer.h" #include "db/UserAction.h" #include #include #include #include "WebInterface.h" #include #include #include #include std::filesystem::path GroovePlayer::musicScanDir = ""; GroovePlayer::GroovePlayer(std::string dbFile) : sqliteConnection(dbFile) { sqlSession.setConnection(this->sqliteConnection); sqlSession.mapClass("user"); sqlSession.mapClass("tracks"); sqlSession.mapClass("actions"); try { sqlSession.createTables(); } catch(Wt::Dbo::Exception e) { Wt::log("info") << "Using Existing DB."; } groove_init(); grooveAudioScanner = new std::thread(grooveAudioScannerLoop); grooveEvents = new std::thread(grooveEventLoop); } std::vector GroovePlayer::getNextVoteBatch() { //TODO } void GroovePlayer::grooveEventLoop() { //TODO } 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("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> tracks = getInstance()->sqlSession.find(); for(Wt::Dbo::collection>::const_iterator i = tracks.begin(); i != tracks.end(); ++i) { if(!std::filesystem::exists(std::filesystem::path((*i)->trackPath))) { Wt::Dbo::ptr item = getInstance()->sqlSession.find().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; for(auto elem : p.path().extension().string()) { extensionLowered.push_back(std::tolower(elem)); } 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(); } //TODO:use this code when sending to UI. // Wt::WServer::instance()->postAll([]() { // Wt::WApplication* app = Wt::WApplication::instance(); // if(app != nullptr) // { // static_cast(app)->updateUIFromServer(); // } // } // );