Skip to content

Commit

Permalink
Convert ashuffle.cc and main.cc to new libmpdclient wrapper
Browse files Browse the repository at this point in the history
Also converts all ashuffle tests to use the wrapper.
  • Loading branch information
joshkunz committed Apr 25, 2020
1 parent 2258dfb commit e8ac3df
Show file tree
Hide file tree
Showing 12 changed files with 786 additions and 737 deletions.
334 changes: 98 additions & 236 deletions src/ashuffle.cc

Large diffs are not rendered by default.

30 changes: 14 additions & 16 deletions src/ashuffle.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@
#define __ASHUFFLE_ASHUFFLE_H__

#include <cstdio>
#include <functional>
#include <string>
#include <vector>

#include <mpd/client.h>

#include "args.h"
#include "mpd.h"
#include "rule.h"
#include "shuffle.h"

Expand All @@ -19,33 +21,29 @@ extern const int WINDOW_SIZE;
// be found in MPD_HOST, then `getpass_f' will be used to prompt the user
// for a password. If `getpass_f' is NULL, the a default password prompt
// (based on getpass) will be used.
struct mpd_connection* ashuffle_connect(const Options& options,
std::string (*getpass_f)());
std::unique_ptr<mpd::MPD> ashuffle_connect(
const mpd::Dialer& d, const Options& options,
std::function<std::string()>& getpass_f);

// Build a `shuffle_chain` of songs from URIs in the given file.
int build_songs_file(struct mpd_connection* mpd,
const std::vector<Rule>& ruleset, FILE* input,
ShuffleChain* songs, bool check);
void build_songs_file(mpd::MPD* mpd, const std::vector<Rule>& ruleset,
FILE* input, ShuffleChain* songs, bool check);

// Build a `shuffle_chain` of songs, by querying the given MPD instance.
int build_songs_mpd(struct mpd_connection* mpd,
const std::vector<Rule>& ruleset, ShuffleChain* songs);
void build_songs_mpd(mpd::MPD* mpd, const std::vector<Rule>& ruleset,
ShuffleChain* songs);

// Add a single random song from the given shuffle chain to the given MPD
// instance.
void shuffle_single(struct mpd_connection* mpd, ShuffleChain* songs);

struct shuffle_test_delegate {
bool skip_init;
bool (*until_f)();
struct TestDelegate {
bool skip_init = false;
bool (*until_f)() = nullptr;
};

// Use the MPD `idle` command to queue songs random songs when the current
// queue finishes playing. This is the core loop of `ashuffle`. The tests
// delegate is used during tests to observe loop effects. It should be set to
// NULL during normal operations.
int shuffle_loop(struct mpd_connection* mpd, ShuffleChain* songs,
const Options& options, struct shuffle_test_delegate*);
void shuffle_loop(mpd::MPD* mpd, ShuffleChain* songs, const Options& options,
TestDelegate d = TestDelegate());

} // namespace ashuffle

Expand Down
21 changes: 13 additions & 8 deletions src/main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include <stdlib.h>
#include <time.h>
#include <cassert>
#include <functional>
#include <iostream>
#include <string>
#include <variant>
Expand All @@ -11,6 +12,7 @@

#include "args.h"
#include "ashuffle.h"
#include "getpass.h"
#include "mpd_client.h"
#include "shuffle.h"

Expand Down Expand Up @@ -41,18 +43,22 @@ int main(int argc, const char *argv[]) {

Options options = std::get<Options>(parse);

std::function<std::string()> pass_f = [] {
return GetPass(stdin, stdout, "mpd password: ");
};
/* attempt to connect to MPD */
struct mpd_connection *mpd = ashuffle_connect(options, NULL);
std::unique_ptr<mpd::MPD> mpd =
ashuffle_connect(*mpd::client::Dialer(), options, pass_f);

ShuffleChain songs(WINDOW_SIZE);

/* build the list of songs to shuffle through */
if (options.file_in != NULL) {
build_songs_file(mpd, options.ruleset, options.file_in, &songs,
build_songs_file(mpd.get(), options.ruleset, options.file_in, &songs,
options.check_uris);
fclose(options.file_in);
} else {
build_songs_mpd(mpd, options.ruleset, &songs);
build_songs_mpd(mpd.get(), options.ruleset, &songs);
}

// For integration testing, we sometimes just want to have ashuffle
Expand All @@ -66,8 +72,8 @@ int main(int argc, const char *argv[]) {
}

if (songs.Len() == 0) {
puts("Song pool is empty.");
return -1;
fputs("Song pool is empty.", stderr);
exit(EXIT_FAILURE);
}
printf("Picking random songs out of a pool of %u.\n", songs.Len());

Expand All @@ -77,13 +83,12 @@ int main(int argc, const char *argv[]) {
/* do the main action */
if (options.queue_only) {
for (unsigned i = 0; i < options.queue_only; i++) {
shuffle_single(mpd, &songs);
mpd->Add(songs.Pick());
}
printf("Added %u songs.\n", options.queue_only);
} else {
shuffle_loop(mpd, &songs, options, NULL);
shuffle_loop(mpd.get(), &songs, options);
}

mpd_connection_free(mpd);
return 0;
}
56 changes: 45 additions & 11 deletions src/mpd.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ class Status {
virtual std::optional<int> SongPosition() const = 0;

// Returns the current play state of the player.
virtual enum mpd_state State() const = 0;
virtual bool IsPlaying() const = 0;
};

// SongReader is a helper for iterating over a list of songs fetched from
Expand All @@ -73,7 +73,15 @@ class SongReader {
// signal to MPD what conditions trigger the end of an "idle" command.
struct IdleEventSet {
// Events is an integer representation of the bit-set.
int events;
int events = 0;

template <typename... Events>
IdleEventSet(Events... set_events) {
std::initializer_list<int> es = {set_events...};
for (int event : es) {
Add(static_cast<enum mpd_idle>(event));
}
}

// Add adds the given event to the set.
void Add(enum mpd_idle event) { events |= event; }
Expand All @@ -86,12 +94,6 @@ struct IdleEventSet {
};

// Address represents the dial address of a given MPD instance.
struct Address {
// host is the hostname of the MPD instance.
std::string host = "";
// Port is the TCP port the MPD instance is listening on.
unsigned port = 0;
};

// MPD represents a connection to an MPD instance.
class MPD {
Expand All @@ -108,7 +110,7 @@ class MPD {
virtual void PlayAt(unsigned position) = 0;

// Gets the current player/MPD status.
virtual std::unique_ptr<Status> GetStatus() = 0;
virtual std::unique_ptr<Status> CurrentStatus() = 0;

// Returns a song reader that can be used to list all songs stored in MPD's
// database.
Expand All @@ -117,7 +119,7 @@ class MPD {
// Searches MPD's DB for a particular song URI, and returns that song.
// Returns an empty optional if the song could not be found.
virtual std::optional<std::unique_ptr<Song>> Search(
const std::string& uri) = 0;
std::string_view uri) = 0;

// Blocks until one of the enum mpd_idle events in the event set happens.
// A new event set is returned, containing all events that occured during
Expand All @@ -136,12 +138,44 @@ class MPD {
// returned.
virtual PasswordStatus ApplyPassword(const std::string& password) = 0;

struct Authorization {
// Set to true if this connection is authorized to execute all
// requested commands.
bool authorized = false;
// If authorized is false, this will be filled with the missing
// commands.
std::vector<std::string> missing = {};
};

// CheckCommandsAllowed checks that the given commands are allowed on
// the MPD connection.
virtual bool CheckCommandsAllowed(
virtual Authorization CheckCommands(
const std::vector<std::string_view>& cmds) = 0;
};

struct Address {
// host is the hostname of the MPD instance.
std::string host = "";
// Port is the TCP port the MPD instance is listening on.
unsigned port = 0;
};

class Dialer {
public:
virtual ~Dialer(){};

constexpr static unsigned kDefaultTimeout = 25000; // 25 seconds.

typedef std::variant<std::unique_ptr<MPD>, std::string> result;

// Dial connects to the MPD instance at the given Address, optionally,
// with the given timeout. On success a variant with a unique_ptr to
// an MPD instance is returned. On failure, a string is returned with
// a human-readable description of the error.
virtual result Dial(const Address&,
unsigned timeout_ms = kDefaultTimeout) const = 0;
};

} // namespace mpd
} // namespace ashuffle

Expand Down
Loading

0 comments on commit e8ac3df

Please sign in to comment.