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