Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ target_include_directories(
"$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/source>"
)

target_compile_features(spotify_volume_controller_lib PUBLIC cxx_std_20)
target_compile_features(spotify_volume_controller_lib PUBLIC cxx_std_23)
target_compile_definitions(spotify_volume_controller_lib PRIVATE _SILENCE_ALL_MS_EXT_DEPRECATION_WARNINGS=1)
target_link_libraries(spotify_volume_controller_lib PRIVATE nlohmann_json::nlohmann_json fmt::fmt cpr::cpr httplib::httplib)

Expand Down
7 changes: 5 additions & 2 deletions source/Client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,11 @@ std::optional<volume> Client::get_current_playing_volume()

void Client::print_error_message(const cpr::Response& response)
{
std::cerr << "Error message: " << response.error.message << '\n';
std::cerr << "Reason: " << response.reason << '\n';
fmt::println(stderr, "Error when requesting {} [{}]", response.url.str(), response.status_code);
if (!response.error.message.empty()) {
fmt::println("Error message: {}", response.error.message);
}
fmt::println("Reason: {}", response.reason);
}

} // namespace spotify_volume_controller
28 changes: 14 additions & 14 deletions source/Config.cpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#include <algorithm>
#include <chrono>
#include <cstdint>
#include <cstdio>
#include <filesystem>
#include <format>
#include <fstream>
#include <iostream>
#include <stdexcept>
Expand All @@ -27,12 +27,12 @@ constexpr std::string_view volume_increment_key = "volume_increment";
constexpr std::string_view volume_up_key = "volume_up";
constexpr std::string_view volume_down_key = "volume_down";
constexpr std::string_view batch_delay_key = "batch_delay_ms";
constexpr std::string_view poll_rate_key = "poll_rate_ms";
constexpr std::string_view fetch_cooldown_key = "fetch_cooldown_ms";

constexpr std::chrono::milliseconds default_batch_delay {100};
constexpr uint32_t default_volume_increment = 1;
constexpr std::chrono::milliseconds default_poll_rate {250};
constexpr std::chrono::milliseconds min_poll_rate {100};
constexpr std::chrono::milliseconds default_fetch_cooldown {2000};
constexpr std::chrono::milliseconds min_fetch_cooldown {100};
constexpr std::string_view default_callback_url = "http://127.0.0.1:5000/callback";

Config::Config()
Expand All @@ -53,13 +53,12 @@ void Config::parse_config_file(const std::filesystem::path& path)
try {
m_config = json::parse(config_file);
} catch (const json::exception& e) {
std::cout << "Invalid config file." << '\n';
std::cerr << e.what() << '\n';
fmt::println(stderr, "Invalid config file: {}", e.what());
m_config = json {};
}
return;
}
std::cout << "No config file found, creating." << '\n';
fmt::println("No config file found, creating.");
std::ofstream new_config_file(m_directory / "config.json");
std::string input {};

Expand Down Expand Up @@ -93,7 +92,7 @@ void Config::parse_config_file(const std::filesystem::path& path)

m_config[volume_increment_key] = default_volume_increment;
m_config[batch_delay_key] = default_batch_delay.count();
m_config[poll_rate_key] = default_poll_rate.count();
m_config[fetch_cooldown_key] = default_fetch_cooldown.count();

new_config_file << m_config;
new_config_file.close();
Expand All @@ -120,7 +119,7 @@ bool Config::should_print_keys() const
keycode Config::get_volume_up() const
{
if (!m_config.contains(volume_up_key)) {
throw std::runtime_error(std::format("Missing {} config", volume_up_key));
throw std::runtime_error(fmt::format("Missing {} config", volume_up_key));
}
json const v_up = m_config.at(volume_down_key);
if (!v_up.is_number_integer()) {
Expand All @@ -137,7 +136,7 @@ keycode Config::get_volume_down() const
json const v_down = m_config.at(volume_down_key);

if (!v_down.is_number_integer()) {
throw std::runtime_error(std::format("{} config is not a valid keycode", volume_down_key));
throw std::runtime_error(fmt::format("{} config is not a valid keycode", volume_down_key));
}
return v_down.template get<keycode>();
}
Expand Down Expand Up @@ -193,18 +192,19 @@ std::chrono::milliseconds Config::batch_delay() const
return std::chrono::milliseconds(m_config.value(batch_delay_key, default_batch_delay.count()));
}

std::chrono::milliseconds Config::poll_rate() const
std::chrono::milliseconds Config::fetch_cooldown() const
{
return std::max(std::chrono::milliseconds(m_config.value(poll_rate_key, default_poll_rate.count())), min_poll_rate);
return std::max(std::chrono::milliseconds(m_config.value(fetch_cooldown_key, default_fetch_cooldown.count())),
min_fetch_cooldown);
}

void Config::get_user_input(const std::string_view prompt, std::string& input, bool not_empty)
{
input.clear();
std::cout << prompt << '\n';
fmt::println("{}", prompt);
std::getline(std::cin, input);
while (not_empty && input.empty()) {
std::cout << "Input can't be empty" << '\n';
fmt::println("Input can't be empty");
std::getline(std::cin, input);
}
}
Expand Down
2 changes: 1 addition & 1 deletion source/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class Config
keycode get_volume_down() const;
volume volume_increment() const;
std::chrono::milliseconds batch_delay() const;
std::chrono::milliseconds poll_rate() const;
std::chrono::milliseconds fetch_cooldown() const;

bool is_default_down() const;
bool is_default_up() const;
Expand Down
43 changes: 28 additions & 15 deletions source/VolumeController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

#include <cpr/response.h>
#include <cpr/status_codes.h>
#include <fmt/core.h>
#include <nlohmann/json_fwd.hpp>
#include <winuser.h>

Expand Down Expand Up @@ -49,9 +50,9 @@ void VolumeController::decrease_volume()
{
std::lock_guard const lock(m_volume_mutex);
// Assume that the API call will succeed
m_volume = m_volume - m_config.volume_increment();
m_volume_queue.push(m_volume);
m_volume_queue.push(volume_change::decrease);
}
m_update_current_volume_cv.notify_one();
if (m_config.batch_delay() > std::chrono::milliseconds(0)) {
m_notify_timer.start([this]() { m_volume_cv.notify_one(); }, m_config.batch_delay());
} else {
Expand All @@ -64,9 +65,9 @@ void VolumeController::increase_volume()
{
std::lock_guard const lock(m_volume_mutex);
// Assume that the API call will succeed
m_volume = m_volume + m_config.volume_increment();
m_volume_queue.push(m_volume);
m_volume_queue.push(volume_change::increase);
}
m_update_current_volume_cv.notify_one();
if (m_config.batch_delay() > std::chrono::milliseconds(0)) {
m_notify_timer.start([this]() { m_volume_cv.notify_one(); }, m_config.batch_delay());
} else {
Expand Down Expand Up @@ -118,11 +119,22 @@ void VolumeController::set_volume_loop()
while (true) {
std::unique_lock lock(m_volume_mutex);
m_volume_cv.wait(lock, [&] { return !m_volume_queue.empty(); });
volume new_volume {0};
if (m_updating_current_volume) {
std::unique_lock update_lock(m_update_current_volume_mutex);
m_updating_current_volume_cv.wait(update_lock, [&] { return !m_updating_current_volume; });
}
volume new_volume = m_volume;
while (!m_volume_queue.empty()) {
new_volume = m_volume_queue.front();
const volume_change change = m_volume_queue.front();
if (change == volume_change::increase) {
new_volume += m_config.volume_increment();
} else {
new_volume -= m_config.volume_increment();
}
m_volume_queue.pop();
}
m_volume = new_volume;
fmt::println("Setting volume to {}", new_volume.m_volume);
set_volume(new_volume);
}
}
Expand All @@ -131,17 +143,18 @@ void VolumeController::update_current_volume_loop()
{
while (true) {
{
std::lock_guard const lock(m_volume_mutex);
if (m_volume_queue.empty()) {
std::optional<volume> current_volume = m_client.get_current_playing_volume();
if (current_volume.has_value()) {
{
m_volume = current_volume.value();
}
}
std::unique_lock update_lock(m_update_current_volume_mutex);
m_update_current_volume_cv.wait(update_lock);
m_updating_current_volume = true;
fmt::println("Fetching current volume from Spotify API");
std::optional<volume> current_volume = m_client.get_current_playing_volume();
if (current_volume.has_value()) {
m_volume = current_volume.value();
}
m_updating_current_volume_cv.notify_all();
m_updating_current_volume = false;
}
std::this_thread::sleep_for(m_config.poll_rate());
std::this_thread::sleep_for(m_config.fetch_cooldown());
}
}

Expand Down
6 changes: 5 additions & 1 deletion source/VolumeController.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,11 @@ class VolumeController

std::mutex m_volume_mutex;
std::condition_variable m_volume_cv;
std::queue<volume> m_volume_queue;
std::queue<volume_change> m_volume_queue;
std::condition_variable m_update_current_volume_cv;
std::mutex m_update_current_volume_mutex;
std::atomic<bool> m_updating_current_volume;
std::condition_variable m_updating_current_volume_cv;
Timer m_notify_timer {};
};

Expand Down
17 changes: 17 additions & 0 deletions source/data_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ using volume_t = unsigned int;

constexpr volume_t max_volume {100};

enum class volume_change : std::uint8_t
{
increase,
decrease
};

struct volume
{
volume(const volume&) = default;
Expand Down Expand Up @@ -72,6 +78,17 @@ struct volume
return old;
}

volume& operator+=(const volume& other)
{
m_volume = (m_volume + other.m_volume > max_volume) ? max_volume : m_volume + other.m_volume;
return *this;
}

volume& operator-=(const volume& other)
{
m_volume = (m_volume < other.m_volume) ? 0 : m_volume - other.m_volume;
return *this;
}
explicit operator volume_t() const { return m_volume; }
};

Expand Down
4 changes: 2 additions & 2 deletions source/key_hooks.cpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
#include <bit>
#include <iostream>
#include <utility>

#include "key_hooks.h"

#include <fmt/core.h>
#include <libloaderapi.h>
#include <minwindef.h>
#include <windef.h>
Expand Down Expand Up @@ -44,7 +44,7 @@ LRESULT CALLBACK print_v_key(int n_code, WPARAM w_param, LPARAM l_param)
}
if (w_param == WM_KEYDOWN) {
auto* keyboard_struct = std::bit_cast<KBDLLHOOKSTRUCT*>(l_param);
std::cout << keyboard_struct->vkCode << '\n';
fmt::println("{}", keyboard_struct->vkCode);
}
return CallNextHookEx(nullptr, n_code, w_param, l_param);
}
Expand Down
15 changes: 8 additions & 7 deletions source/main.cpp
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
#include <cstdio>
#include <exception>
#include <iostream>
#include <optional>

#include <argparse/argparse.hpp>
#include <fmt/core.h>
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
#include <windows.h>
#include <wincon.h>

#include "Client.h"
#include "Config.h"
Expand All @@ -24,30 +25,30 @@ int main(int argc, char* argv[])
try {
program.parse_args(argc, argv);
} catch (const std::exception& err) {
std::cerr << err.what() << '\n';
std::cerr << program;
fmt::println(stderr, "{}", err.what());
fmt::println(stderr, "{}", program.usage());
return 1;
}

std::cout << "Starting..." << '\n';
fmt::println("Starting...");

spotify_volume_controller::Config const config = program.is_used("--config")
? spotify_volume_controller::Config(program.get("--config"))
: spotify_volume_controller::Config();
if (!config.is_valid()) {
std::cerr << "Failed to read config file." << '\n';
fmt::println(stderr, "Failed to read config file.");
std::cin.get();
return 1;
}
std::optional<spotify_volume_controller::token_t> token = spotify_volume_controller::oauth::get_token(config);
if (!token.has_value()) {
std::cout << "Failed to connect to spotify, exiting..." << '\n';
fmt::println(stderr, "Failed to connect to Spotify, exiting...");
std::cin.get();
return 1;
}
spotify_volume_controller::Client client(token.value(), config);

std::cout << "Connected to spotify successfully!" << '\n';
fmt::println("Connected to spotify successfully!");
if (config.hide_window()) {
FreeConsole();
}
Expand Down
Loading
Loading