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 }