1 #include "lib/settings.hpp"
2 
3 using namespace lib;
4 
settings(const paths & paths)5 settings::settings(const paths &paths)
6 	: path(paths)
7 {
8 	load();
9 }
10 
file_name() const11 auto settings::file_name() const -> std::string
12 {
13 	return path.config_file();
14 }
15 
file_path() const16 auto settings::file_path() const -> std::string
17 {
18 	return ghc::filesystem::path(file_name()).parent_path().string();
19 }
20 
21 //region qt
22 
to_json(nlohmann::json & j,const qt & q)23 void lib::setting::to_json(nlohmann::json &j, const qt &q)
24 {
25 	j = nlohmann::json{
26 		{"system_title_bar", q.system_title_bar},
27 	};
28 }
29 
from_json(const nlohmann::json & j,qt & q)30 void lib::setting::from_json(const nlohmann::json &j, qt &q)
31 {
32 	if (!j.is_object())
33 	{
34 		return;
35 	}
36 
37 	lib::json::get(j, "system_title_bar", q.system_title_bar);
38 }
39 
qt()40 auto lib::settings::qt() -> setting::qt &
41 {
42 	return qt_settings;
43 }
44 
qt_const() const45 auto lib::settings::qt_const() const -> const setting::qt &
46 {
47 	return qt_settings;
48 }
49 
50 //endregion
51 
from_json(const nlohmann::json & json)52 void settings::from_json(const nlohmann::json &json)
53 {
54 	auto a = json.at("Account");
55 	auto g = json.at("General");
56 	auto s = json.at("Spotify");
57 
58 	// Account
59 	setValue(a, "access_token", account.access_token);
60 	setValue(a, "client_id", account.client_id);
61 	setValue(a, "client_secret", account.client_secret);
62 	setValue(a, "last_refresh", account.last_refresh);
63 	setValue(a, "refresh_token", account.refresh_token);
64 
65 	// General
66 	setValue(g, "custom_playlist_order", general.custom_playlist_order);
67 	setValue(g, "fallback_icons", general.fallback_icons);
68 	setValue(g, "fixed_width_time", general.fixed_width_time);
69 	setValue(g, "hidden_song_headers", general.hidden_song_headers);
70 	setValue(g, "last_device", general.last_device);
71 	setValue(g, "last_playlist", general.last_playlist);
72 	setValue(g, "last_version", general.last_version);
73 	setValue(g, "last_volume", general.last_volume);
74 	setValue(g, "media_controller", general.media_controller);
75 	setValue(g, "playlist_order", general.playlist_order);
76 	setValue(g, "pulse_volume", general.pulse_volume);
77 	setValue(g, "refresh_interval", general.refresh_interval);
78 	setValue(g, "relative_added", general.relative_added);
79 	setValue(g, "show_changelog", general.show_changelog);
80 	setValue(g, "song_header_sort_by", general.song_header_sort_by);
81 	setValue(g, "style", general.style);
82 	setValue(g, "style_palette", general.style_palette);
83 	setValue(g, "track_list_resize_mode", general.track_list_resize_mode);
84 	setValue(g, "track_numbers", general.track_numbers);
85 	setValue(g, "tray_album_art", general.tray_album_art);
86 	setValue(g, "tray_icon", general.tray_icon);
87 	setValue(g, "tray_light_icon", general.tray_light_icon);
88 	setValue(g, "tray_notifications", general.tray_notifications);
89 
90 	// Spotify
91 	setValue(s, "always_start", spotify.always_start);
92 	setValue(s, "backend", spotify.backend);
93 	setValue(s, "bitrate", spotify.bitrate);
94 	setValue(s, "disable_discovery", spotify.disable_discovery);
95 	setValue(s, "global_config", spotify.global_config);
96 	setValue(s, "keyring_password", spotify.keyring_password);
97 	setValue(s, "max_queue", spotify.max_queue);
98 	setValue(s, "path", spotify.path);
99 	setValue(s, "start_client", spotify.start_client);
100 	setValue(s, "username", spotify.username);
101 
102 	// Qt widgets
103 	// TODO: This should be loaded dynamically for non-Qt clients
104 	lib::json::get(json, "Qt", qt_settings);
105 
106 	// Validate
107 	auto errors = validate();
108 
109 	for (auto &error : errors)
110 	{
111 		log::error("One or more invalid {} settings found, restoring defaults",
112 			error.first);
113 	}
114 	if (errors.find("General") != errors.end())
115 	{
116 		general = setting::general{};
117 	}
118 	if (errors.find("Spotify") != errors.end())
119 	{
120 		spotify = setting::spotify{};
121 	}
122 }
123 
load()124 void settings::load()
125 {
126 	auto name = file_name();
127 
128 	std::ifstream file(name);
129 	if (!file.is_open())
130 	{
131 		log::warn("Failed to load settings: \"{}\" not found", name);
132 		return;
133 	}
134 
135 	try
136 	{
137 		nlohmann::json json;
138 		file >> json;
139 		from_json(json);
140 	}
141 	catch (const nlohmann::json::exception &e)
142 	{
143 		log::error("Failed to load settings: {}", e.what());
144 	}
145 
146 	file.close();
147 }
148 
to_json() const149 auto settings::to_json() const -> nlohmann::json
150 {
151 	return {
152 		{"Account", {
153 			{"access_token", account.access_token},
154 			{"client_id", account.client_id},
155 			{"client_secret", account.client_secret},
156 			{"last_refresh", account.last_refresh},
157 			{"refresh_token", account.refresh_token},
158 		}},
159 		{"General", {
160 			{"custom_playlist_order", general.custom_playlist_order},
161 			{"fallback_icons", general.fallback_icons},
162 			{"fixed_width_time", general.fixed_width_time},
163 			{"hidden_song_headers", general.hidden_song_headers},
164 			{"last_device", general.last_device},
165 			{"last_playlist", general.last_playlist},
166 			{"last_version", general.last_version},
167 			{"last_volume", general.last_volume},
168 			{"media_controller", general.media_controller},
169 			{"playlist_order", general.playlist_order},
170 			{"pulse_volume", general.pulse_volume},
171 			{"refresh_interval", general.refresh_interval},
172 			{"relative_added", general.relative_added},
173 			{"show_changelog", general.show_changelog},
174 			{"song_header_sort_by", general.song_header_sort_by},
175 			{"style", general.style},
176 			{"style_palette", general.style_palette},
177 			{"track_list_resize_mode", general.track_list_resize_mode},
178 			{"track_numbers", general.track_numbers},
179 			{"tray_album_art", general.tray_album_art},
180 			{"tray_icon", general.tray_icon},
181 			{"tray_light_icon", general.tray_light_icon},
182 			{"tray_notifications", general.tray_notifications},
183 		}},
184 		{"Spotify", {
185 			{"always_start", spotify.always_start},
186 			{"backend", spotify.backend},
187 			{"bitrate", spotify.bitrate},
188 			{"disable_discovery", spotify.disable_discovery},
189 			{"global_config", spotify.global_config},
190 			{"keyring_password", spotify.keyring_password},
191 			{"max_queue", spotify.max_queue},
192 			{"path", spotify.path},
193 			{"start_client", spotify.start_client},
194 			{"username", spotify.username},
195 		}},
196 		{"Qt", qt_settings},
197 	};
198 }
199 
save()200 void settings::save()
201 {
202 	std::lock_guard<std::mutex> lock(mutex);
203 
204 	auto file_dir = file_path();
205 	if (!ghc::filesystem::exists(file_dir))
206 	{
207 		ghc::filesystem::create_directories(file_dir);
208 	}
209 
210 	std::ofstream file(file_name());
211 	file << std::setw(4) << to_json();
212 	file.close();
213 }
214 
remove_client()215 void settings::remove_client()
216 {
217 	account.client_id = std::string();
218 	account.client_secret = std::string();
219 }
220 
remove_tokens()221 void settings::remove_tokens()
222 {
223 	account.access_token = std::string();
224 	account.refresh_token = std::string();
225 }
226 
get_dark_theme() const227 auto settings::get_dark_theme() const -> bool
228 {
229 	return general.style_palette == palette_dark;
230 }
231 
set_dark_theme(bool value)232 void settings::set_dark_theme(bool value)
233 {
234 	general.style_palette = value ? palette_dark : palette_app;
235 }
236 
validate() const237 auto settings::validate() const -> std::map<std::string, std::vector<std::string>>
238 {
239 	std::map<std::string, std::vector<std::string>> errors;
240 
241 	// Refresh interval needs to be 1-60
242 	if (general.refresh_interval < 1 || general.refresh_interval > 60)
243 	{
244 		errors["General"] = {
245 			"refresh_interval"
246 		};
247 	}
248 
249 	// Bitrate needs to be 96/160/320
250 	if (spotify.bitrate != 96 && spotify.bitrate != 160 && spotify.bitrate != 320)
251 	{
252 		errors["Spotify"] = {
253 			"bitrate"
254 		};
255 	}
256 
257 	return errors;
258 }
259