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 } |
