Mercurial > minori
comparison src/core/config.cc @ 318:3b355fa948c7
config: use TOML instead of INI
unfortunately, INI is not enough, and causes some paths including
semicolons to break with our current storage of the library folders.
so, I decided to switch to TOML which does support real arrays...
author | Paper <paper@paper.us.eu.org> |
---|---|
date | Wed, 12 Jun 2024 05:25:41 -0400 |
parents | b1f4d1867ab1 |
children | 1b5c04268d6a |
comparison
equal
deleted
inserted
replaced
317:b1f4d1867ab1 | 318:3b355fa948c7 |
---|---|
1 /** | |
2 * config.cpp: | |
3 * parses the config... lol | |
4 **/ | |
5 #include "core/config.h" | 1 #include "core/config.h" |
6 #include "core/anime.h" | 2 #include "core/anime.h" |
7 #include "core/filesystem.h" | 3 #include "core/filesystem.h" |
8 #include "core/ini.h" | |
9 #include "core/json.h" | 4 #include "core/json.h" |
10 #include "core/strings.h" | 5 #include "core/strings.h" |
11 #include "gui/translate/anime.h" | 6 #include "gui/translate/anime.h" |
12 #include "gui/translate/config.h" | 7 #include "gui/translate/config.h" |
13 | 8 |
18 #include <cstring> | 13 #include <cstring> |
19 #include <filesystem> | 14 #include <filesystem> |
20 #include <fstream> | 15 #include <fstream> |
21 #include <limits.h> | 16 #include <limits.h> |
22 | 17 |
18 #include <toml11/toml.hpp> | |
19 | |
23 #include <QFile> | 20 #include <QFile> |
24 #include <QTextStream> | 21 #include <QTextStream> |
25 | 22 |
26 #include <iostream> | 23 #include <iostream> |
27 | 24 |
28 /* I'll use an INI-based config file instead of using an | 25 /* NOTE: This config file is prone to breakage, as Minori is alpha software and |
29 * XML file like Taiga. | 26 * as such nothing is constant. */ |
30 * | |
31 * It technically isn't to spec, because I'm making these case-sensitive. | |
32 * Boohoo. | |
33 */ | |
34 | 27 |
35 int Config::Load() { | 28 int Config::Load() { |
36 std::filesystem::path cfg_path = Filesystem::GetConfigPath(); | 29 std::filesystem::path cfg_path = Filesystem::GetConfigPath(); |
37 | 30 |
38 mINI::INIFile file(cfg_path.u8string()); | 31 std::ifstream ifs(cfg_path, std::ios_base::binary); |
39 mINI::INIStructure ini; | 32 if (!ifs.good()) |
40 file.read(ini); | 33 return 0; |
41 | 34 |
42 service = Translate::ToService(INI::GetIniString(ini, "General", "Service", "None")); | 35 toml::value data; |
43 | 36 |
44 anime_list.score_format = | 37 try { |
45 Translate::ToScoreFormat(INI::GetIniString(ini, "Anime List", "Score format", "POINT_100")); | 38 data = toml::parse(ifs); |
46 anime_list.language = | 39 } catch (const std::exception& ex) { |
47 Translate::ToLanguage(INI::GetIniString(ini, "Anime List", "Title language", "Romaji")); | 40 std::cerr << "config: failed to parse toml with error " << ex.what() << std::endl; |
48 anime_list.display_aired_episodes = INI::GetIniBool(ini, "Anime List", "Display only aired episodes", true); | 41 return 0; |
49 anime_list.display_available_episodes = | 42 } |
50 INI::GetIniBool(ini, "Anime List", "Display only available episodes in library", true); | |
51 anime_list.highlight_anime_if_available = | |
52 INI::GetIniBool(ini, "Anime List", "Highlight anime if available", true); | |
53 | 43 |
54 if (anime_list.highlight_anime_if_available) // sanity check | 44 service = Translate::ToService(toml::find_or(data, "General", "Service", "None")); |
55 anime_list.highlighted_anime_above_others = | 45 locale.RefreshAvailableLocales(); |
56 INI::GetIniBool(ini, "Anime List", "Display highlighted anime above others", false); | 46 locale.SetActiveLocale(QLocale(Strings::ToQString(toml::find_or(data, "General", "Locale", "en_US")))); |
57 else | |
58 anime_list.highlighted_anime_above_others = false; | |
59 | 47 |
60 auth.anilist.auth_token = INI::GetIniString(ini, "Authentication/AniList", "Auth Token", ""); | |
61 auth.anilist.user_id = INI::GetIniInteger<int>(ini, "Authentication/AniList", "User ID", 0); | |
62 | 48 |
63 auth.kitsu.access_token = INI::GetIniString(ini, "Authentication/Kitsu", "Access Token", ""); | 49 anime_list.score_format = Translate::ToScoreFormat(toml::find_or(data, "Anime List", "Score format", "100-point")); |
64 auth.kitsu.access_token_expiration = INI::GetIniInteger<Time::Timestamp>(ini, "Authentication/Kitsu", "Access Token Expiration", 0); | 50 anime_list.language = Translate::ToLanguage(toml::find_or(data, "Anime List", "Title language", "Romaji")); |
65 auth.kitsu.refresh_token = INI::GetIniString(ini, "Authentication/Kitsu", "Refresh Token", ""); | 51 anime_list.display_aired_episodes = toml::find_or<bool>(data, "Anime List", "Display only aired episodes", true); |
66 auth.kitsu.user_id = INI::GetIniString(ini, "Authentication/Kitsu", "User ID", ""); | 52 anime_list.display_available_episodes = toml::find_or<bool>(data, "Anime List", "Display only available episodes in library", true); |
53 anime_list.highlight_anime_if_available = toml::find_or<bool>(data, "Anime List", "Highlight anime if available", true); | |
54 anime_list.highlighted_anime_above_others = | |
55 (anime_list.highlight_anime_if_available) | |
56 ? toml::find_or<bool>(data, "Anime List", "Display highlighted anime above others", false) | |
57 : false; | |
67 | 58 |
68 torrents.feed_link = INI::GetIniString(ini, "Torrents", "RSS feed", | 59 auth.anilist.auth_token = toml::find_or(data, "Authentication/AniList", "Auth Token", ""); |
69 "https://www.tokyotosho.info/rss.php?filter=1,11&zwnj=0"); | 60 auth.anilist.user_id = toml::find_or<int>(data, "Authentication/AniList", "User ID", 0); |
70 | 61 |
71 recognition.detect_media_players = INI::GetIniBool(ini, "Recognition", "Detect media players", true); | 62 auth.kitsu.access_token = toml::find_or(data, "Authentication/Kitsu", "Access Token", ""); |
63 auth.kitsu.access_token_expiration = toml::find_or(data, "Authentication/Kitsu", "Access Token Expiration", 0LL); | |
64 auth.kitsu.refresh_token = toml::find_or(data, "Authentication/Kitsu", "Refresh Token", ""); | |
65 auth.kitsu.user_id = toml::find_or(data, "Authentication/Kitsu", "User ID", ""); | |
72 | 66 |
73 /* lots of dumb logic to import the player data */ | 67 torrents.feed_link = toml::find_or(data, "Torrents", "RSS feed", "https://www.tokyotosho.info/rss.php?filter=1,11&zwnj=0"); |
68 | |
69 recognition.detect_media_players = toml::find_or(data, "Recognition", "Detect media players", true); | |
70 | |
74 { | 71 { |
75 /* load the player data */ | |
76 QFile f(":/players.anisthesia"); | 72 QFile f(":/players.anisthesia"); |
77 if (!f.exists()) | 73 if (!f.exists()) |
78 return false; | 74 return false; |
79 | 75 |
80 f.open(QFile::ReadOnly | QFile::Text); | 76 f.open(QFile::ReadOnly | QFile::Text); |
92 | 88 |
93 for (auto& [enabled, player] : recognition.players) { | 89 for (auto& [enabled, player] : recognition.players) { |
94 switch (player.type) { | 90 switch (player.type) { |
95 default: | 91 default: |
96 case animone::PlayerType::Default: | 92 case animone::PlayerType::Default: |
97 enabled = INI::GetIniBool(ini, "Recognition/Players", player.name, true); | 93 enabled = toml::find_or<bool>(data, "Recognition/Players", player.name, true); |
98 break; | 94 break; |
99 case animone::PlayerType::WebBrowser: | 95 case animone::PlayerType::WebBrowser: |
100 enabled = INI::GetIniBool(ini, "Recognition/Browsers", player.name, true); | 96 enabled = toml::find_or<bool>(data, "Recognition/Browsers", player.name, true); |
101 break; | 97 break; |
102 } | 98 } |
103 } | 99 } |
104 | 100 |
105 locale.RefreshAvailableLocales(); | |
106 locale.SetActiveLocale( | |
107 QLocale(Strings::ToQString(INI::GetIniString(ini, "General", "Locale", "en_US")))); | |
108 | 101 |
109 theme.SetTheme(Translate::ToTheme(INI::GetIniString(ini, "Appearance", "Theme", "Default"))); | 102 theme.SetTheme(Translate::ToTheme(toml::find_or(data, "Appearance", "Theme", "Default"))); |
110 | 103 |
111 { | 104 if (data.contains("Library") && data["Library"].contains("Folders")) { |
112 std::vector<std::string> v = Strings::Split(INI::GetIniString(ini, "Library", "Folders", ""), ";"); | 105 const toml::value& folders = toml::find(data, "Library", "Folders"); |
106 std::vector<std::string> v = toml::get_or<std::vector<std::string>>(folders, {}); | |
113 for (const auto& s : v) | 107 for (const auto& s : v) |
114 if (!library.paths.count(s)) | 108 if (!library.paths.count(s)) |
115 library.paths.insert(s); | 109 library.paths.insert(s); |
116 } | 110 } |
117 | 111 |
118 library.real_time_monitor = INI::GetIniBool(ini, "Library", "Real-time monitor", true); | 112 library.real_time_monitor = toml::find_or(data, "Library", "Real-time monitor", true); |
119 | 113 |
120 return 0; | 114 return 0; |
121 } | 115 } |
122 | 116 |
123 int Config::Save() { | 117 int Config::Save() { |
124 std::filesystem::path cfg_path = Filesystem::GetConfigPath(); | 118 std::filesystem::path cfg_path = Filesystem::GetConfigPath(); |
125 Filesystem::CreateDirectories(cfg_path); | 119 Filesystem::CreateDirectories(cfg_path); |
126 | 120 |
127 mINI::INIFile file(cfg_path.string()); | 121 std::ofstream file(cfg_path); |
128 mINI::INIStructure ini; | 122 if (!file.good()) |
123 return 0; | |
129 | 124 |
130 ini["General"]["Service"] = Translate::ToString(service); | 125 toml::value data; |
131 ini["General"]["Locale"] = Strings::ToUtf8String(locale.GetLocale().name()); | |
132 | 126 |
133 ini["Anime List"]["Score format"] = Translate::ToString(anime_list.score_format); | 127 data["Library"]["Folders"] = library.paths; |
134 ini["Anime List"]["Title language"] = Translate::ToString(anime_list.language); | 128 data["Library"]["Real-time monitor"] = library.real_time_monitor; |
135 ini["Anime List"]["Display only aired episodes"] = Strings::ToUtf8String(anime_list.display_aired_episodes); | |
136 ini["Anime List"]["Display only available episodes in library"] = Strings::ToUtf8String(anime_list.display_available_episodes); | |
137 ini["Anime List"]["Highlight anime if available"] = Strings::ToUtf8String(anime_list.highlight_anime_if_available); | |
138 ini["Anime List"]["Display highlighted anime above others"] = Strings::ToUtf8String(anime_list.highlighted_anime_above_others); | |
139 | |
140 ini["Authentication/AniList"]["Auth Token"] = auth.anilist.auth_token; | |
141 ini["Authentication/AniList"]["User ID"] = Strings::ToUtf8String(auth.anilist.user_id); | |
142 | |
143 ini["Authentication/Kitsu"]["Access Token"] = auth.kitsu.access_token; | |
144 ini["Authentication/Kitsu"]["Access Token Expiration"] = Strings::ToUtf8String(auth.kitsu.access_token_expiration); | |
145 ini["Authentication/Kitsu"]["Refresh Token"] = auth.kitsu.refresh_token; | |
146 ini["Authentication/Kitsu"]["User ID"] = auth.kitsu.user_id; | |
147 | |
148 ini["Appearance"]["Theme"] = Translate::ToString(theme.GetTheme()); | |
149 | |
150 ini["Torrents"]["RSS feed"] = torrents.feed_link; | |
151 | |
152 ini["Recognition"]["Detect media players"] = Strings::ToUtf8String(recognition.detect_media_players); | |
153 | 129 |
154 for (const auto& [enabled, player] : recognition.players) { | 130 for (const auto& [enabled, player] : recognition.players) { |
155 const std::string section = (player.type == animone::PlayerType::WebBrowser) ? "Recognition/Players" : "Recognition/Browsers"; | 131 const std::string section = (player.type == animone::PlayerType::WebBrowser) ? "Recognition/Players" : "Recognition/Browsers"; |
156 ini[section][player.name] = Strings::ToUtf8String(enabled); | 132 data[section][player.name] = enabled; |
157 } | 133 } |
158 | 134 |
159 ini["Library"]["Folders"] = Strings::Implode(library.paths, ";"); | 135 data["Recognition"]["Detect media players"] = recognition.detect_media_players; |
160 ini["Library"]["Real-time monitor"] = Strings::ToUtf8String(library.real_time_monitor); | |
161 | 136 |
162 file.write(ini); | 137 data["Torrents"]["RSS feed"] = torrents.feed_link; |
138 | |
139 data["Authentication/Kitsu"]["Access Token"] = auth.kitsu.access_token; | |
140 data["Authentication/Kitsu"]["Access Token Expiration"] = auth.kitsu.access_token_expiration; | |
141 data["Authentication/Kitsu"]["Refresh Token"] = auth.kitsu.refresh_token; | |
142 data["Authentication/Kitsu"]["User ID"] = auth.kitsu.user_id; | |
143 | |
144 data["Authentication/AniList"]["Auth Token"] = auth.anilist.auth_token; | |
145 data["Authentication/AniList"]["User ID"] = auth.anilist.user_id; | |
146 | |
147 data["Anime List"]["Score format"] = Translate::ToString(anime_list.score_format); | |
148 data["Anime List"]["Title language"] = Translate::ToString(anime_list.language); | |
149 data["Anime List"]["Display only aired episodes"] = anime_list.display_aired_episodes; | |
150 data["Anime List"]["Display only available episodes in library"] = anime_list.display_available_episodes; | |
151 data["Anime List"]["Highlight anime if available"] = anime_list.highlight_anime_if_available; | |
152 data["Anime List"]["Display highlighted anime above others"] = anime_list.highlighted_anime_above_others; | |
153 | |
154 data["Appearance"]["Theme"] = Translate::ToString(theme.GetTheme()); | |
155 | |
156 data["General"]["Service"] = Translate::ToString(service); | |
157 data["General"]["Locale"] = Strings::ToUtf8String(locale.GetLocale().name()); | |
158 | |
159 file << std::setw(0) << data; | |
163 | 160 |
164 return 0; | 161 return 0; |
165 } | 162 } |