Stop using pointers from DB as storage mechanism. Start storing copies of the object itself. Use commits after methods are done with transaction to hopefully help with DB issues. Don't use map because it requires too much about the key object I don't need. Add boolean that prevents vote end from being called constantly during it's 3 second window.

This commit is contained in:
Kevin Whitaker
2017-02-05 18:20:03 -05:00
parent 75c87c41f8
commit cb5b2f7ad6
4 changed files with 71 additions and 61 deletions

View File

@@ -64,7 +64,7 @@ GroovePlayerMgr::GroovePlayerMgr (std::string dbFile) : sqliteConnection(dbFile)
//}
}
std::list<const AudioTrack*> GroovePlayerMgr::getNextVoteBatch(Wt::Dbo::Session* session)
std::list<AudioTrack> GroovePlayerMgr::getNextVoteBatch(Wt::Dbo::Session* session)
{
/**
* This method will attempt to pick 3 tracks that will be up for selection next.
@@ -78,7 +78,7 @@ std::list<const AudioTrack*> GroovePlayerMgr::getNextVoteBatch(Wt::Dbo::Session*
* The third track will be replaced by a recent request item if one exists.
*/
std::list<const AudioTrack*> selectedTracks;
std::list<AudioTrack> selectedTracks;
Wt::Dbo::Transaction transaction(*session);
//First make sure there are at least 3 tracks to suggest.
@@ -90,33 +90,33 @@ std::list<const AudioTrack*> GroovePlayerMgr::getNextVoteBatch(Wt::Dbo::Session*
}
//Determine first track
int trackAlbumCount = session->query<int>("select count(fingerprint) from tracks").where("album = ?").bind(getCurrentTrack(session)->trackAlbumName);
int trackArtistCount = session->query<int>("select count(fingerprint) from tracks").where("artist = ?").bind(getCurrentTrack(session)->trackArtistName);
int trackAlbumCount = session->query<int>("select count(fingerprint) from tracks").where("album = ?").bind(getCurrentTrack(session).trackAlbumName);
int trackArtistCount = session->query<int>("select count(fingerprint) from tracks").where("artist = ?").bind(getCurrentTrack(session).trackArtistName);
int computerSlightOfHand = rand() % 10 + 1;
if(trackAlbumCount > 0 && computerSlightOfHand > 3)
{
//Pick item from album
Wt::Dbo::ptr<AudioTrack> eligibleTrack = session->find<AudioTrack>().where("album = ?").limit(1).offset(rand() % trackAlbumCount).bind(getCurrentTrack(session)->trackAlbumName);
selectedTracks.push_back(eligibleTrack.get());
Wt::Dbo::ptr<AudioTrack> eligibleTrack = session->find<AudioTrack>().where("album = ?").limit(1).offset(rand() % trackAlbumCount).bind(getCurrentTrack(session).trackAlbumName);
selectedTracks.push_back(*eligibleTrack);
}
else if(trackArtistCount > 0)
{
//Pick item from artist
Wt::Dbo::ptr<AudioTrack> eligibleTrack = session->find<AudioTrack>().where("artist = ?").limit(1).offset(rand() % trackArtistCount).bind(getCurrentTrack(session)->trackArtistName);
selectedTracks.push_back(eligibleTrack.get());
Wt::Dbo::ptr<AudioTrack> eligibleTrack = session->find<AudioTrack>().where("artist = ?").limit(1).offset(rand() % trackArtistCount).bind(getCurrentTrack(session).trackArtistName);
selectedTracks.push_back(*eligibleTrack);
}
//Determine second track
int trackGenreCount = session->query<int>("select count(fingerprint) from tracks").where("genre = ?").bind(getCurrentTrack(session)->trackGenre);
int trackGenreCount = session->query<int>("select count(fingerprint) from tracks").where("genre = ?").bind(getCurrentTrack(session).trackGenre);
if(trackGenreCount > 0)
{
//Pick item from genre
Wt::Dbo::ptr<AudioTrack> eligibleTrack = session->find<AudioTrack>().where("genre = ?").limit(1).offset(rand() % trackGenreCount).bind(getCurrentTrack(session)->trackGenre);
selectedTracks.push_back(eligibleTrack.get());
Wt::Dbo::ptr<AudioTrack> eligibleTrack = session->find<AudioTrack>().where("genre = ?").limit(1).offset(rand() % trackGenreCount).bind(getCurrentTrack(session).trackGenre);
selectedTracks.push_back(*eligibleTrack);
}
//Determine third track
int trackNotGenreCount = session->query<int>("select count(fingerprint) from tracks").where("genre != ?").bind(getCurrentTrack(session)->trackGenre);
int trackNotGenreCount = session->query<int>("select count(fingerprint) from tracks").where("genre != ?").bind(getCurrentTrack(session).trackGenre);
if(requestQueue.size() > 0)
{
//There's a request. Pick one up front and put as third item.
@@ -125,15 +125,17 @@ std::list<const AudioTrack*> GroovePlayerMgr::getNextVoteBatch(Wt::Dbo::Session*
else if(trackNotGenreCount > 0)
{
//Pick from other genre
Wt::Dbo::ptr<AudioTrack> eligibleTrack = session->find<AudioTrack>().where("genre != ?").limit(1).offset(rand() % trackNotGenreCount).bind(getCurrentTrack(session)->trackGenre);
selectedTracks.push_back(eligibleTrack.get());
Wt::Dbo::ptr<AudioTrack> eligibleTrack = session->find<AudioTrack>().where("genre != ?").limit(1).offset(rand() % trackNotGenreCount).bind(getCurrentTrack(session).trackGenre);
selectedTracks.push_back(*eligibleTrack);
}
else
{
//Pick randomly
Wt::Dbo::ptr<AudioTrack> eligibleTrack = session->find<AudioTrack>().limit(1).offset(rand() % trackCount);
selectedTracks.push_back(eligibleTrack.get());
selectedTracks.push_back(*eligibleTrack);
}
transaction.commit();
Wt::log("info") << "Next set of tracks to vote picked: 1=>[" << (*selectedTracks.begin()).trackName << "] 2=>[" << (*std::next(selectedTracks.begin(),1)).trackName << "] 3=>[" << (*std::next(selectedTracks.begin(),2)).trackName << "]";
return selectedTracks;
}
@@ -160,23 +162,24 @@ void GroovePlayerMgr::grooveEventLoop()
//Pick initial track to bootstrap.
int tracksPlayedBefore = sqlSession.query<int>("select count(fingerprint) from tracks as t join actions on (actions.action = 3 or actions.action = 2) and actions.track_id = t.id");
const AudioTrack* selectedTrack;
AudioTrack selectedTrack;
if (tracksPlayedBefore > 0)
{
selectedTrack = sqlSession.query<Wt::Dbo::ptr<AudioTrack>>("select t from tracks as t join actions on (actions.action = 3 or actions.action = 2) and actions.track_id = t.id").limit(1).offset(rand() % tracksPlayedBefore).resultValue().get();
selectedTrack = *sqlSession.query<Wt::Dbo::ptr<AudioTrack>>("select t from tracks as t join actions on (actions.action = 3 or actions.action = 2) and actions.track_id = t.id").limit(1).offset(rand() % tracksPlayedBefore).resultValue();
}
else
{
int trackCount = sqlSession.query<int>("select count(fingerprint) from tracks");
selectedTrack = sqlSession.find<AudioTrack>().limit(1).offset(rand() % trackCount).resultValue().get();
selectedTrack = *sqlSession.find<AudioTrack>().limit(1).offset(rand() % trackCount).resultValue();
}
transaction.commit();
struct GroovePlaylist* playlist = groove_playlist_create();
currentPlaylist = playlist;
struct GroovePlayer* player = groove_player_create();
if(!player) {return;}
currentPlayer = player;
groove_playlist_insert(playlist, groove_file_open(selectedTrack->trackPath.c_str()),1.0,1.0,nullptr);
groove_playlist_insert(playlist, groove_file_open(selectedTrack.trackPath.c_str()),1.0,1.0,nullptr);
//Now boostrap player with initial data
groove_player_attach(player, playlist);
@@ -185,7 +188,7 @@ void GroovePlayerMgr::grooveEventLoop()
Wt::WApplication* app = Wt::WApplication::instance();
if(app != nullptr)
{
static_cast<WebInterface*>(app)->songChangedFromServer(getCurrentTrack(&sqlSession).get());
static_cast<WebInterface*>(app)->songChangedFromServer(getCurrentTrack(&sqlSession));
}
}
);
@@ -203,51 +206,52 @@ void GroovePlayerMgr::grooveEventLoop()
{
//Pick new batch of tracks to vote on and inform UI of update.
currentVoteBatch = getNextVoteBatch(&sqlSession);
currentVoteStatus = std::map<const AudioTrack*, int>();
std::for_each(currentVoteBatch.begin(),currentVoteBatch.end(),[&](const AudioTrack* item){
currentVoteStatus.insert(std::pair<const AudioTrack*, int>(item,0));
});
currentVoteStatus = std::list<std::pair<AudioTrack, int>>();
for(auto track : currentVoteBatch)
{
currentVoteStatus.push_back(std::pair<AudioTrack, int>(track,0));
}
Wt::WServer::instance()->postAll([&]() {
Wt::WApplication* app = Wt::WApplication::instance();
if(app != nullptr)
{
static_cast<WebInterface*>(app)->songChangedFromServer(getCurrentTrack(&sqlSession).get());
static_cast<WebInterface*>(app)->songChangedFromServer(getCurrentTrack(&sqlSession));
static_cast<WebInterface*>(app)->voteTracksUpdatedFromServer(currentVoteBatch);
}
}
);
Wt::log("info") << "Track playing changed to: " << getCurrentTrack(&sqlSession).get()->trackName;
Wt::log("info") << "Track playing changed to: " << getCurrentTrack(&sqlSession).trackName;
}
else if(event == VOTING_ENDED)
{
//Look at votes and add correct track to end of playlist.
//If more than one track has highest votes, pick random. TODO:maybe base off of other play data instead.
std::list<std::pair<const AudioTrack*,int>> trackWinners;
for(std::map<const AudioTrack*,int>::iterator track = currentVoteStatus.begin(); track != currentVoteStatus.end(); track++)
std::list<std::pair<AudioTrack,int>> trackWinners;
for(auto track : currentVoteStatus)
{
//There nothing yet and there was at least a vote is an initial winner.
if(trackWinners.size() == 0 && track->second > 0)
if(trackWinners.size() == 0 && track.second > 0)
{
trackWinners.push_back(std::pair<const AudioTrack*, int>(track->first,track->second));
trackWinners.push_back(track);
continue;
}
//If there is a winner. Check if higher. If so, clear the list and add this one. If equal, add to list.
if(trackWinners.size() > 0)
{
if(trackWinners.front().second < track->second)
if(trackWinners.front().second < track.second)
{
trackWinners.clear();
trackWinners.push_back(std::pair<const AudioTrack*,int>(track->first, track->second));
trackWinners.push_back(std::pair<AudioTrack,int>(track.first, track.second));
}
else if(trackWinners.front().second == track->second)
else if(trackWinners.front().second == track.second)
{
trackWinners.push_back(std::pair<const AudioTrack*, int>(track->first, track->second));
trackWinners.push_back(std::pair<AudioTrack, int>(track.first, track.second));
}
}
}
const AudioTrack* winner;
AudioTrack winner;
//Randomly pick from winners(in case of tie), if there are any.
if(trackWinners.size() > 0)
{
@@ -259,7 +263,7 @@ void GroovePlayerMgr::grooveEventLoop()
if(requestQueue.size() > 0)
{
//If winning track was a request, now you can remove it from request queue.
if(requestQueue.front()->trackFingerprint == winner->trackFingerprint)
if(requestQueue.front().trackFingerprint == winner.trackFingerprint)
{
requestQueue.pop_front();
}
@@ -296,12 +300,12 @@ void GroovePlayerMgr::grooveEventLoop()
else
{
//Third track wins
winner = (*currentVoteBatch.begin()+=2);
winner = (*(currentVoteBatch.begin()++)++);
}
}
}
groove_playlist_insert(currentPlaylist,groove_file_open(winner->trackPath.c_str()),1.0,1.0,nullptr);
Wt::log("info")<< "Voting has ended. Next track is: " << winner->trackName;
groove_playlist_insert(currentPlaylist,groove_file_open(winner.trackPath.c_str()),1.0,1.0,nullptr);
Wt::log("info")<< "Voting has ended. Next track is: " << winner.trackName;
}
else if(event == VOTE_CAST)
{
@@ -350,8 +354,9 @@ GroovePlayerMgr::PlayerEvents GroovePlayerMgr::getNextPlayerEvent(Wt::Dbo::Sessi
{
double timeIntoTrack;
groove_player_position(currentPlayer,nullptr,&timeIntoTrack);
if(getCurrentTrack(session).get() != nullptr && getCurrentTrack(session)->trackLengthSeconds-timeIntoTrack < 3)
if(!voteEndedButNotNextTrackYet && getCurrentTrack(session).trackName != std::string() && getCurrentTrack(session).trackLengthSeconds-timeIntoTrack < 3)
{
voteEndedButNotNextTrackYet = true;
return VOTING_ENDED;
}
GroovePlayerEvent event;
@@ -359,6 +364,7 @@ GroovePlayerMgr::PlayerEvents GroovePlayerMgr::getNextPlayerEvent(Wt::Dbo::Sessi
{
if(event.type == GROOVE_EVENT_NOWPLAYING)
{
voteEndedButNotNextTrackYet = false;
return GROOVE_NOWPLAYING;
}
}
@@ -371,17 +377,19 @@ GroovePlayerMgr::PlayerEvents GroovePlayerMgr::getNextPlayerEvent(Wt::Dbo::Sessi
return NOTHING;
}
Wt::Dbo::ptr<AudioTrack> GroovePlayerMgr::getCurrentTrack(Wt::Dbo::Session* session)
AudioTrack GroovePlayerMgr::getCurrentTrack(Wt::Dbo::Session* session)
{
Wt::Dbo::Transaction transaction(*session);
GroovePlaylistItem* currentItem;
groove_player_position(this->currentPlayer,&currentItem,nullptr);
if(currentItem != nullptr)
{
Wt::Dbo::ptr<AudioTrack> track = session->find<AudioTrack>().where("path = ?").bind(currentItem->file->filename);
AudioTrack track = *session->find<AudioTrack>().where("path = ?").bind(currentItem->file->filename).resultValue();
transaction.commit();
return track;
}
return Wt::Dbo::ptr<AudioTrack>();
transaction.commit();
return AudioTrack();
}
@@ -436,6 +444,7 @@ bool GroovePlayerMgr::addFileToTrackDBIfTagged(Wt::Dbo::Session* session, std::f
newTrack->trackFingerprint = fingerprint;
newTrack->trackPath = file.string();
session->add(newTrack);
transaction.commit();
groove_file_close(gfile);
return true;
}
@@ -453,6 +462,7 @@ void GroovePlayerMgr::removeOrphanedTracks(Wt::Dbo::Session* session)
item.remove();
}
}
transaction.commit();
}

View File

@@ -24,7 +24,6 @@
#include <Wt/Dbo/backend/Sqlite3>
#include <Wt/Dbo/FixedSqlConnectionPool>
#include <list>
#include <map>
#include <thread>
#include "db/User.h"
#include "db/AudioTrack.h"
@@ -48,8 +47,9 @@ public:
struct GroovePlaylist* currentPlaylist;
struct GroovePlayer* currentPlayer;
std::list<const AudioTrack*> currentVoteBatch;
std::map<const AudioTrack*, int> currentVoteStatus;
std::list<AudioTrack> currentVoteBatch;
std::list<std::pair<AudioTrack, int>> currentVoteStatus;
bool voteEndedButNotNextTrackYet = false;
private:
enum PlayerEvents {NOTHING, GROOVE_NOWPLAYING, VOTING_ENDED, VOTE_CAST, PLAYING_PAUSED, PLAYING_RESUMED, SKIP_REQUESTED, SKIP_VOTE_CAST, SKIP_VOTING_ENDED, ADMIN_FORCE_SKIP};
@@ -59,19 +59,19 @@ private:
bool continueEventLoop = true;
bool finishedLaunchScan = false;
std::list<const AudioTrack*> requestQueue;
std::list<AudioTrack> requestQueue;
std::list<PlayerEvents> lastInternalEvents;
std::thread* grooveEvents;
void grooveEventLoop();
std::list<const AudioTrack*> getNextVoteBatch(Wt::Dbo::Session* session);
std::list<AudioTrack> getNextVoteBatch(Wt::Dbo::Session* session);
std::thread* grooveAudioScanner;
void grooveAudioScannerLoop();
PlayerEvents getNextPlayerEvent(Wt::Dbo::Session* session);
bool addFileToTrackDBIfTagged(Wt::Dbo::Session* session, std::filesystem::path file);
void removeOrphanedTracks(Wt::Dbo::Session* session);
Wt::Dbo::ptr<AudioTrack> getCurrentTrack(Wt::Dbo::Session* session);
AudioTrack getCurrentTrack(Wt::Dbo::Session* session);
};

View File

@@ -57,31 +57,31 @@ void WebInterface::loginCompleted()
//TODO: request current player state.
}
void WebInterface::playPauseActionFromServer(User* userPausing)
void WebInterface::playPauseActionFromServer(User userPausing)
{
priv_int->playerUI->tempText->setText(priv_int->playerUI->tempText->text()+"play");
triggerUpdate();
}
void WebInterface::songChangedFromServer(const AudioTrack* nextTrack)
void WebInterface::songChangedFromServer(AudioTrack nextTrack)
{
priv_int->playerUI->tempText->setText(priv_int->playerUI->tempText->text()+"change");
triggerUpdate();
}
void WebInterface::skipVotedFromServer(User* userRequestingToSkipCurrentTrack)
void WebInterface::skipVotedFromServer(User userRequestingToSkipCurrentTrack)
{
priv_int->playerUI->tempText->setText(priv_int->playerUI->tempText->text()+"skip");
triggerUpdate();
}
void WebInterface::voteUpdateFromServer(User* userVoting, bool forSkip)
void WebInterface::voteUpdateFromServer(User userVoting, bool forSkip)
{
priv_int->playerUI->tempText->setText(priv_int->playerUI->tempText->text()+"vote");
triggerUpdate();
}
void WebInterface::voteNextSongFromServer(User* userVoting, const AudioTrack* trackVoted)
void WebInterface::voteNextSongFromServer(User userVoting, AudioTrack trackVoted)
{
priv_int->playerUI->tempText->setText(priv_int->playerUI->tempText->text()+"nextvote");
triggerUpdate();
@@ -93,7 +93,7 @@ void WebInterface::voteNextPollClosedFromServer()
triggerUpdate();
}
void WebInterface::voteTracksUpdatedFromServer(std::list<const AudioTrack *> voteableTracks)
void WebInterface::voteTracksUpdatedFromServer(std::list<AudioTrack> voteableTracks)
{
priv_int->playerUI->tempText->setText(priv_int->playerUI->tempText->text()+"votechanged");
triggerUpdate();

View File

@@ -31,12 +31,12 @@ public:
WebInterface(const Wt::WEnvironment& env);
~WebInterface();
void loginCompleted();
void playPauseActionFromServer(User* userPausing);
void songChangedFromServer(const AudioTrack* nextTrack);
void voteTracksUpdatedFromServer(std::list<const AudioTrack*> voteableTracks);
void skipVotedFromServer(User* userRequestingToSkipCurrentTrack);
void voteUpdateFromServer(User* userVoting, bool forSkip);
void voteNextSongFromServer(User* userVoting, const AudioTrack* trackVoted);
void playPauseActionFromServer(User userPausing);
void songChangedFromServer(AudioTrack nextTrack);
void voteTracksUpdatedFromServer(std::list<AudioTrack> voteableTracks);
void skipVotedFromServer(User userRequestingToSkipCurrentTrack);
void voteUpdateFromServer(User userVoting, bool forSkip);
void voteNextSongFromServer(User userVoting, AudioTrack trackVoted);
void voteNextPollClosedFromServer();
private:
struct internal;