1 /*GRB*
2 
3     Gerbera - https://gerbera.io/
4 
5     config_load.cc - this file is part of Gerbera.
6 
7     Copyright (C) 2020-2021 Gerbera Contributors
8 
9     Gerbera is free software; you can redistribute it and/or modify
10     it under the terms of the GNU General Public License version 2
11     as published by the Free Software Foundation.
12 
13     Gerbera is distributed in the hope that it will be useful,
14     but WITHOUT ANY WARRANTY; without even the implied warranty of
15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16     GNU General Public License for more details.
17 
18     You should have received a copy of the GNU General Public License
19     along with Gerbera.  If not, see <http://www.gnu.org/licenses/>.
20 
21     $Id$
22 */
23 
24 /// \file config_load.cc
25 
26 #include "pages.h" // API
27 
28 #include <fmt/chrono.h>
29 #include <numeric>
30 
31 #include "config/client_config.h"
32 #include "config/config_definition.h"
33 #include "config/config_setup.h"
34 #include "config/directory_tweak.h"
35 #include "config/dynamic_content.h"
36 #include "content/autoscan.h"
37 #include "content/content_manager.h"
38 #include "database/database.h"
39 #include "metadata/metadata_handler.h"
40 #include "transcoding/transcoding.h"
41 #include "upnp_xml.h"
42 #include "util/upnp_clients.h"
43 
ConfigLoad(std::shared_ptr<ContentManager> content)44 Web::ConfigLoad::ConfigLoad(std::shared_ptr<ContentManager> content)
45     : WebRequestHandler(std::move(content))
46 {
47     try {
48         if (this->database) {
49             dbEntries = this->database->getConfigValues();
50         } else {
51             log_error("configLoad database missing");
52         }
53     } catch (const std::runtime_error& e) {
54         log_error("configLoad {}", e.what());
55     }
56 }
57 
addTypeMeta(pugi::xml_node & meta,const std::shared_ptr<ConfigSetup> & cs)58 void Web::ConfigLoad::addTypeMeta(pugi::xml_node& meta, const std::shared_ptr<ConfigSetup>& cs)
59 {
60     auto info = meta.append_child("item");
61     info.append_attribute("item") = cs->getUniquePath().c_str();
62     info.append_attribute("id") = fmt::format("{:03d}", cs->option).c_str();
63     info.append_attribute("type") = cs->getTypeString().c_str();
64     info.append_attribute("value") = cs->getDefaultValue().c_str();
65     info.append_attribute("help") = cs->getHelp();
66 }
67 
createItem(pugi::xml_node & item,const std::string & name,config_option_t id,config_option_t aid,const std::shared_ptr<ConfigSetup> & cs)68 void Web::ConfigLoad::createItem(pugi::xml_node& item, const std::string& name, config_option_t id, config_option_t aid, const std::shared_ptr<ConfigSetup>& cs)
69 {
70     allItems[name] = &item;
71     item.append_attribute("item") = name.c_str();
72     item.append_attribute("id") = fmt::format("{:03d}", id).c_str();
73     item.append_attribute("aid") = fmt::format("{:03d}", aid).c_str();
74     if (std::any_of(dbEntries.begin(), dbEntries.end(), [=](auto&& s) { return s.item == name; })) {
75         item.append_attribute("status") = "unchanged";
76         item.append_attribute("source") = "database";
77     } else {
78         item.append_attribute("status") = !cs || !cs->isDefaultValueUsed() ? "unchanged" : "default";
79         item.append_attribute("source") = !cs || !cs->isDefaultValueUsed() ? "config.xml" : "default";
80     }
81     item.append_attribute("origValue") = config->getOrigValue(name).c_str();
82     item.append_attribute("defaultValue") = cs ? cs->getDefaultValue().c_str() : "";
83 }
84 
85 template <typename T>
setValue(pugi::xml_node & item,const T & value)86 void Web::ConfigLoad::setValue(pugi::xml_node& item, const T& value)
87 {
88     static_assert(fmt::has_formatter<T, fmt::format_context>::value, "T must be formattable");
89     item.append_attribute("value") = fmt::to_string(value).c_str();
90 }
91 
92 template <>
setValue(pugi::xml_node & item,const std::string & value)93 void Web::ConfigLoad::setValue(pugi::xml_node& item, const std::string& value)
94 {
95     item.append_attribute("value") = value.c_str();
96 }
97 
98 template <>
setValue(pugi::xml_node & item,const std::string_view & value)99 void Web::ConfigLoad::setValue(pugi::xml_node& item, const std::string_view& value)
100 {
101     item.append_attribute("value") = value.data();
102 }
103 
104 template <>
setValue(pugi::xml_node & item,const fs::path & value)105 void Web::ConfigLoad::setValue(pugi::xml_node& item, const fs::path& value)
106 {
107     item.append_attribute("value") = value.c_str();
108 }
109 
110 /// \brief: process config_load request
process()111 void Web::ConfigLoad::process()
112 {
113     check_request();
114     auto root = xmlDoc->document_element();
115     auto values = root.append_child("values");
116 
117     // set handling of json properties
118     xml2JsonHints->setArrayName(values, "item");
119     xml2JsonHints->setFieldType("item", "string");
120     xml2JsonHints->setFieldType("id", "string");
121     xml2JsonHints->setFieldType("aid", "string");
122     xml2JsonHints->setFieldType("value", "string");
123     xml2JsonHints->setFieldType("origValue", "string");
124 
125     log_debug("Sending Config to web!");
126 
127     // generate meta info for ui
128     auto meta = root.append_child("types");
129     xml2JsonHints->setArrayName(meta, "item");
130     for (auto&& cs : ConfigDefinition::getOptionList()) {
131         addTypeMeta(meta, cs);
132     }
133 
134     // write database status
135     {
136         auto item = values.append_child("item");
137         createItem(item, "/status/attribute::total", CFG_MAX, CFG_MAX);
138         setValue(item, database->getTotalFiles());
139         item = values.append_child("item");
140         createItem(item, "/status/attribute::virtual", CFG_MAX, CFG_MAX);
141         setValue(item, database->getTotalFiles(true));
142 
143         item = values.append_child("item");
144         createItem(item, "/status/attribute::audio", CFG_MAX, CFG_MAX);
145         setValue(item, database->getTotalFiles(false, "audio"));
146         item = values.append_child("item");
147         createItem(item, "/status/attribute::video", CFG_MAX, CFG_MAX);
148         setValue(item, database->getTotalFiles(false, "video"));
149         item = values.append_child("item");
150         createItem(item, "/status/attribute::image", CFG_MAX, CFG_MAX);
151         setValue(item, database->getTotalFiles(false, "image"));
152 
153         item = values.append_child("item");
154         createItem(item, "/status/attribute::audioVirtual", CFG_MAX, CFG_MAX);
155         setValue(item, database->getTotalFiles(true, "audio"));
156         item = values.append_child("item");
157         createItem(item, "/status/attribute::videoVirtual", CFG_MAX, CFG_MAX);
158         setValue(item, database->getTotalFiles(true, "video"));
159         item = values.append_child("item");
160         createItem(item, "/status/attribute::imageVirtual", CFG_MAX, CFG_MAX);
161         setValue(item, database->getTotalFiles(true, "image"));
162     }
163 
164     // write all values with simple type (string, int, bool)
165     for (auto&& option : ConfigOptionIterator()) {
166         try {
167             auto scs = ConfigDefinition::findConfigSetup(option);
168             auto item = values.append_child("item");
169             createItem(item, scs->getItemPath(ITEM_PATH_ROOT), option, option, scs);
170 
171             log_debug("    Option {:03d} {} = {}", option, scs->getItemPath(), scs->getCurrentValue().c_str());
172             setValue(item, scs->getCurrentValue());
173         } catch (const std::runtime_error& e) {
174             log_warning("Option {:03d} {}", option, e.what());
175         }
176     }
177 
178     // write client configuration
179     auto cs = ConfigDefinition::findConfigSetup(CFG_CLIENTS_LIST);
180     auto clientConfig = cs->getValue()->getClientConfigListOption();
181     for (std::size_t i = 0; i < clientConfig->size(); i++) {
182         auto client = clientConfig->get(i);
183 
184         auto item = values.append_child("item");
185         createItem(item, cs->getItemPath(i, ATTR_CLIENTS_CLIENT_FLAGS), cs->option, ATTR_CLIENTS_CLIENT_FLAGS, cs);
186         setValue(item, ClientConfig::mapFlags(client->getFlags()));
187 
188         item = values.append_child("item");
189         createItem(item, cs->getItemPath(i, ATTR_CLIENTS_CLIENT_IP), cs->option, ATTR_CLIENTS_CLIENT_IP, cs);
190         setValue(item, client->getIp());
191 
192         item = values.append_child("item");
193         createItem(item, cs->getItemPath(i, ATTR_CLIENTS_CLIENT_USERAGENT), cs->option, ATTR_CLIENTS_CLIENT_USERAGENT, cs);
194         setValue(item, client->getUserAgent());
195     }
196     if (clientConfig->size() == 0) {
197         auto item = values.append_child("item");
198         createItem(item, cs->getItemPath(ITEM_PATH_NEW, ATTR_CLIENTS_CLIENT_FLAGS), cs->option, ATTR_CLIENTS_CLIENT_FLAGS, ConfigDefinition::findConfigSetup(ATTR_CLIENTS_CLIENT_FLAGS));
199 
200         item = values.append_child("item");
201         createItem(item, cs->getItemPath(ITEM_PATH_NEW, ATTR_CLIENTS_CLIENT_IP), cs->option, ATTR_CLIENTS_CLIENT_IP, ConfigDefinition::findConfigSetup(ATTR_CLIENTS_CLIENT_IP));
202 
203         item = values.append_child("item");
204         createItem(item, cs->getItemPath(ITEM_PATH_NEW, ATTR_CLIENTS_CLIENT_USERAGENT), cs->option, ATTR_CLIENTS_CLIENT_USERAGENT, ConfigDefinition::findConfigSetup(ATTR_CLIENTS_CLIENT_USERAGENT));
205     }
206 
207     // write import tweaks
208     cs = ConfigDefinition::findConfigSetup(CFG_IMPORT_DIRECTORIES_LIST);
209     auto directoryConfig = cs->getValue()->getDirectoryTweakOption();
210     for (std::size_t i = 0; i < directoryConfig->size(); i++) {
211         auto dir = directoryConfig->get(i);
212 
213         auto item = values.append_child("item");
214         createItem(item, cs->getItemPath(i, ATTR_DIRECTORIES_TWEAK_LOCATION), cs->option, ATTR_DIRECTORIES_TWEAK_LOCATION);
215         setValue(item, dir->getLocation());
216 
217         item = values.append_child("item");
218         createItem(item, cs->getItemPath(i, ATTR_DIRECTORIES_TWEAK_INHERIT), cs->option, ATTR_DIRECTORIES_TWEAK_INHERIT);
219         setValue(item, dir->getInherit());
220 
221         item = values.append_child("item");
222         createItem(item, cs->getItemPath(i, ATTR_DIRECTORIES_TWEAK_RECURSIVE), cs->option, ATTR_DIRECTORIES_TWEAK_RECURSIVE);
223         setValue(item, dir->getRecursive());
224 
225         item = values.append_child("item");
226         createItem(item, cs->getItemPath(i, ATTR_DIRECTORIES_TWEAK_HIDDEN), cs->option, ATTR_DIRECTORIES_TWEAK_HIDDEN);
227         setValue(item, dir->getHidden());
228 
229         item = values.append_child("item");
230         createItem(item, cs->getItemPath(i, ATTR_DIRECTORIES_TWEAK_CASE_SENSITIVE), cs->option, ATTR_DIRECTORIES_TWEAK_CASE_SENSITIVE);
231         setValue(item, dir->getCaseSensitive());
232 
233         item = values.append_child("item");
234         createItem(item, cs->getItemPath(i, ATTR_DIRECTORIES_TWEAK_FOLLOW_SYMLINKS), cs->option, ATTR_DIRECTORIES_TWEAK_FOLLOW_SYMLINKS);
235         setValue(item, dir->getFollowSymlinks());
236 
237         item = values.append_child("item");
238         createItem(item, cs->getItemPath(i, ATTR_DIRECTORIES_TWEAK_META_CHARSET), cs->option, ATTR_DIRECTORIES_TWEAK_META_CHARSET);
239         setValue(item, dir->hasMetaCharset() ? dir->getMetaCharset() : "");
240 
241         item = values.append_child("item");
242         createItem(item, cs->getItemPath(i, ATTR_DIRECTORIES_TWEAK_FANART_FILE), cs->option, ATTR_DIRECTORIES_TWEAK_FANART_FILE);
243         setValue(item, dir->hasFanArtFile() ? dir->getFanArtFile() : "");
244 
245         item = values.append_child("item");
246         createItem(item, cs->getItemPath(i, ATTR_DIRECTORIES_TWEAK_RESOURCE_FILE), cs->option, ATTR_DIRECTORIES_TWEAK_RESOURCE_FILE);
247         setValue(item, dir->hasResourceFile() ? dir->getResourceFile() : "");
248 
249         item = values.append_child("item");
250         createItem(item, cs->getItemPath(i, ATTR_DIRECTORIES_TWEAK_SUBTILTE_FILE), cs->option, ATTR_DIRECTORIES_TWEAK_SUBTILTE_FILE);
251         setValue(item, dir->hasSubTitleFile() ? dir->getSubTitleFile() : "");
252     }
253 
254     // write dynamic content
255     cs = ConfigDefinition::findConfigSetup(CFG_SERVER_DYNAMIC_CONTENT_LIST);
256     auto dynContent = cs->getValue()->getDynamicContentListOption();
257     for (std::size_t i = 0; i < dynContent->size(); i++) {
258         auto cont = dynContent->get(i);
259 
260         auto item = values.append_child("item");
261         createItem(item, cs->getItemPath(i, ATTR_DYNAMIC_CONTAINER_LOCATION), cs->option, ATTR_DYNAMIC_CONTAINER_LOCATION);
262         setValue(item, cont->getLocation());
263 
264         item = values.append_child("item");
265         createItem(item, cs->getItemPath(i, ATTR_DYNAMIC_CONTAINER_IMAGE), cs->option, ATTR_DYNAMIC_CONTAINER_IMAGE);
266         setValue(item, cont->getImage());
267 
268         item = values.append_child("item");
269         createItem(item, cs->getItemPath(i, ATTR_DYNAMIC_CONTAINER_TITLE), cs->option, ATTR_DYNAMIC_CONTAINER_TITLE);
270         setValue(item, cont->getTitle());
271 
272         item = values.append_child("item");
273         createItem(item, cs->getItemPath(i, ATTR_DYNAMIC_CONTAINER_FILTER), cs->option, ATTR_DYNAMIC_CONTAINER_FILTER);
274         setValue(item, cont->getFilter());
275 
276         item = values.append_child("item");
277         createItem(item, cs->getItemPath(i, ATTR_DYNAMIC_CONTAINER_SORT), cs->option, ATTR_DYNAMIC_CONTAINER_SORT);
278         setValue(item, cont->getSort());
279     }
280     if (dynContent->size() == 0) {
281         auto item = values.append_child("item");
282         createItem(item, cs->getItemPath(ITEM_PATH_NEW, ATTR_DYNAMIC_CONTAINER_LOCATION), cs->option, ATTR_DYNAMIC_CONTAINER_LOCATION, ConfigDefinition::findConfigSetup(ATTR_DYNAMIC_CONTAINER_LOCATION));
283 
284         item = values.append_child("item");
285         createItem(item, cs->getItemPath(ITEM_PATH_NEW, ATTR_DYNAMIC_CONTAINER_IMAGE), cs->option, ATTR_DYNAMIC_CONTAINER_IMAGE, ConfigDefinition::findConfigSetup(ATTR_DYNAMIC_CONTAINER_IMAGE));
286 
287         item = values.append_child("item");
288         createItem(item, cs->getItemPath(ITEM_PATH_NEW, ATTR_DYNAMIC_CONTAINER_TITLE), cs->option, ATTR_DYNAMIC_CONTAINER_TITLE, ConfigDefinition::findConfigSetup(ATTR_DYNAMIC_CONTAINER_TITLE));
289 
290         item = values.append_child("item");
291         createItem(item, cs->getItemPath(ITEM_PATH_NEW, ATTR_DYNAMIC_CONTAINER_FILTER), cs->option, ATTR_DYNAMIC_CONTAINER_FILTER, ConfigDefinition::findConfigSetup(ATTR_DYNAMIC_CONTAINER_FILTER));
292 
293         item = values.append_child("item");
294         createItem(item, cs->getItemPath(ITEM_PATH_NEW, ATTR_DYNAMIC_CONTAINER_SORT), cs->option, ATTR_DYNAMIC_CONTAINER_SORT, ConfigDefinition::findConfigSetup(ATTR_DYNAMIC_CONTAINER_SORT));
295     }
296 
297     // write transconding configuration
298     cs = ConfigDefinition::findConfigSetup(CFG_TRANSCODING_PROFILE_LIST);
299     auto transcoding = cs->getValue()->getTranscodingProfileListOption();
300     int pr = 0;
301     std::map<std::string, int> profiles;
302     for (auto&& [key, val] : transcoding->getList()) {
303         for (auto&& [a, name] : *val) {
304             auto item = values.append_child("item");
305             createItem(item, cs->getItemPath(pr, ATTR_TRANSCODING_MIMETYPE_PROF_MAP, ATTR_TRANSCODING_MIMETYPE_PROF_MAP_TRANSCODE, ATTR_TRANSCODING_MIMETYPE_PROF_MAP_MIMETYPE), cs->option, ATTR_TRANSCODING_MIMETYPE_PROF_MAP_MIMETYPE, cs);
306             setValue(item, key);
307 
308             item = values.append_child("item");
309             createItem(item, cs->getItemPath(pr, ATTR_TRANSCODING_MIMETYPE_PROF_MAP, ATTR_TRANSCODING_MIMETYPE_PROF_MAP_TRANSCODE, ATTR_TRANSCODING_MIMETYPE_PROF_MAP_USING), cs->option, ATTR_TRANSCODING_MIMETYPE_PROF_MAP_USING, cs);
310             setValue(item, name->getName());
311             profiles.emplace(name->getName(), pr);
312 
313             pr++;
314         }
315     }
316 
317     pr = 0;
318     for (auto&& [key, val] : profiles) {
319         auto entry = transcoding->getByName(key, true);
320         auto item = values.append_child("item");
321         createItem(item, cs->getItemPath(pr, ATTR_TRANSCODING_PROFILES_PROFLE, ATTR_TRANSCODING_PROFILES_PROFLE_NAME), cs->option, ATTR_TRANSCODING_PROFILES_PROFLE_NAME);
322         setValue(item, entry->getName());
323 
324         item = values.append_child("item");
325         createItem(item, cs->getItemPath(pr, ATTR_TRANSCODING_PROFILES_PROFLE, ATTR_TRANSCODING_PROFILES_PROFLE_CLIENTFLAGS), cs->option, ATTR_TRANSCODING_PROFILES_PROFLE_CLIENTFLAGS);
326         setValue(item, ClientConfig::mapFlags(entry->getClientFlags()));
327 
328         item = values.append_child("item");
329         createItem(item, cs->getItemPath(pr, ATTR_TRANSCODING_PROFILES_PROFLE, ATTR_TRANSCODING_PROFILES_PROFLE_ENABLED), cs->option, ATTR_TRANSCODING_PROFILES_PROFLE_ENABLED);
330         setValue(item, entry->getEnabled());
331 
332         item = values.append_child("item");
333         createItem(item, cs->getItemPath(pr, ATTR_TRANSCODING_PROFILES_PROFLE, ATTR_TRANSCODING_PROFILES_PROFLE_TYPE), cs->option, ATTR_TRANSCODING_PROFILES_PROFLE_TYPE);
334         setValue(item, (entry->getType() == TR_External ? "external" : "none"));
335 
336         item = values.append_child("item");
337         createItem(item, cs->getItemPath(pr, ATTR_TRANSCODING_PROFILES_PROFLE, ATTR_TRANSCODING_PROFILES_PROFLE_MIMETYPE), cs->option, ATTR_TRANSCODING_PROFILES_PROFLE_MIMETYPE);
338         setValue(item, entry->getTargetMimeType());
339 
340         item = values.append_child("item");
341         createItem(item, cs->getItemPath(pr, ATTR_TRANSCODING_PROFILES_PROFLE, ATTR_TRANSCODING_PROFILES_PROFLE_RES), cs->option, ATTR_TRANSCODING_PROFILES_PROFLE_RES);
342         setValue(item, entry->getAttributes()[MetadataHandler::getResAttrName(R_RESOLUTION)]);
343 
344         item = values.append_child("item");
345         createItem(item, cs->getItemPath(pr, ATTR_TRANSCODING_PROFILES_PROFLE, ATTR_TRANSCODING_PROFILES_PROFLE_ACCURL), cs->option, ATTR_TRANSCODING_PROFILES_PROFLE_ACCURL);
346         setValue(item, entry->acceptURL());
347 
348         item = values.append_child("item");
349         createItem(item, cs->getItemPath(pr, ATTR_TRANSCODING_PROFILES_PROFLE, ATTR_TRANSCODING_PROFILES_PROFLE_SAMPFREQ), cs->option, ATTR_TRANSCODING_PROFILES_PROFLE_SAMPFREQ);
350         setValue(item, entry->getSampleFreq());
351 
352         item = values.append_child("item");
353         createItem(item, cs->getItemPath(pr, ATTR_TRANSCODING_PROFILES_PROFLE, ATTR_TRANSCODING_PROFILES_PROFLE_NRCHAN), cs->option, ATTR_TRANSCODING_PROFILES_PROFLE_NRCHAN);
354         setValue(item, entry->getNumChannels());
355 
356         item = values.append_child("item");
357         createItem(item, cs->getItemPath(pr, ATTR_TRANSCODING_PROFILES_PROFLE, ATTR_TRANSCODING_PROFILES_PROFLE_HIDEORIG), cs->option, ATTR_TRANSCODING_PROFILES_PROFLE_HIDEORIG);
358         setValue(item, entry->hideOriginalResource());
359 
360         item = values.append_child("item");
361         createItem(item, cs->getItemPath(pr, ATTR_TRANSCODING_PROFILES_PROFLE, ATTR_TRANSCODING_PROFILES_PROFLE_THUMB), cs->option, ATTR_TRANSCODING_PROFILES_PROFLE_THUMB);
362         setValue(item, entry->isThumbnail());
363 
364         item = values.append_child("item");
365         createItem(item, cs->getItemPath(pr, ATTR_TRANSCODING_PROFILES_PROFLE, ATTR_TRANSCODING_PROFILES_PROFLE_FIRST), cs->option, ATTR_TRANSCODING_PROFILES_PROFLE_FIRST);
366         setValue(item, entry->firstResource());
367 
368         item = values.append_child("item");
369         createItem(item, cs->getItemPath(pr, ATTR_TRANSCODING_PROFILES_PROFLE, ATTR_TRANSCODING_PROFILES_PROFLE_ACCOGG), cs->option, ATTR_TRANSCODING_PROFILES_PROFLE_ACCOGG);
370         setValue(item, entry->isTheora());
371 
372         item = values.append_child("item");
373         createItem(item, cs->getItemPath(pr, ATTR_TRANSCODING_PROFILES_PROFLE, ATTR_TRANSCODING_PROFILES_PROFLE_AGENT, ATTR_TRANSCODING_PROFILES_PROFLE_AGENT_COMMAND), cs->option, ATTR_TRANSCODING_PROFILES_PROFLE_AGENT_COMMAND);
374         setValue(item, entry->getCommand());
375 
376         item = values.append_child("item");
377         createItem(item, cs->getItemPath(pr, ATTR_TRANSCODING_PROFILES_PROFLE, ATTR_TRANSCODING_PROFILES_PROFLE_AGENT, ATTR_TRANSCODING_PROFILES_PROFLE_AGENT_ARGS), cs->option, ATTR_TRANSCODING_PROFILES_PROFLE_AGENT_ARGS);
378         setValue(item, entry->getArguments());
379 
380         item = values.append_child("item");
381         createItem(item, cs->getItemPath(pr, ATTR_TRANSCODING_PROFILES_PROFLE, ATTR_TRANSCODING_PROFILES_PROFLE_BUFFER, ATTR_TRANSCODING_PROFILES_PROFLE_BUFFER_SIZE), cs->option, ATTR_TRANSCODING_PROFILES_PROFLE_BUFFER_SIZE);
382         setValue(item, entry->getBufferSize());
383 
384         item = values.append_child("item");
385         createItem(item, cs->getItemPath(pr, ATTR_TRANSCODING_PROFILES_PROFLE, ATTR_TRANSCODING_PROFILES_PROFLE_BUFFER, ATTR_TRANSCODING_PROFILES_PROFLE_BUFFER_CHUNK), cs->option, ATTR_TRANSCODING_PROFILES_PROFLE_BUFFER_CHUNK);
386         setValue(item, entry->getBufferChunkSize());
387 
388         item = values.append_child("item");
389         createItem(item, cs->getItemPath(pr, ATTR_TRANSCODING_PROFILES_PROFLE, ATTR_TRANSCODING_PROFILES_PROFLE_BUFFER, ATTR_TRANSCODING_PROFILES_PROFLE_BUFFER_FILL), cs->option, ATTR_TRANSCODING_PROFILES_PROFLE_BUFFER_FILL);
390         setValue(item, entry->getBufferInitialFillSize());
391 
392         auto fourCCMode = entry->getAVIFourCCListMode();
393         if (fourCCMode != FCC_None) {
394             item = values.append_child("item");
395             createItem(item, cs->getItemPath(pr, ATTR_TRANSCODING_PROFILES_PROFLE, ATTR_TRANSCODING_PROFILES_PROFLE_AVI4CC, ATTR_TRANSCODING_PROFILES_PROFLE_AVI4CC_MODE), cs->option, ATTR_TRANSCODING_PROFILES_PROFLE_AVI4CC_MODE);
396             setValue(item, TranscodingProfile::mapFourCcMode(fourCCMode));
397 
398             const auto fourCCList = entry->getAVIFourCCList();
399             if (!fourCCList.empty()) {
400                 item = values.append_child("item");
401                 createItem(item, cs->getItemPath(pr, ATTR_TRANSCODING_PROFILES_PROFLE, ATTR_TRANSCODING_PROFILES_PROFLE_AVI4CC, ATTR_TRANSCODING_PROFILES_PROFLE_AVI4CC_4CC), cs->option, ATTR_TRANSCODING_PROFILES_PROFLE_AVI4CC_4CC);
402                 setValue(item, std::accumulate(next(fourCCList.begin()), fourCCList.end(), fourCCList[0], [](auto&& a, auto&& b) { return fmt::format("{}, {}", a.c_str(), b.c_str()); }));
403             }
404         }
405         pr++;
406     }
407 
408     // write autoscan configuration
409     for (auto&& ascs : ConfigDefinition::getConfigSetupList<ConfigAutoscanSetup>()) {
410         auto autoscan = ascs->getValue()->getAutoscanListOption();
411         for (std::size_t i = 0; i < autoscan->size(); i++) {
412             auto&& entry = autoscan->get(i);
413             auto&& adir = content->getAutoscanDirectory(entry->getLocation());
414             auto item = values.append_child("item");
415             createItem(item, ascs->getItemPath(i, ATTR_AUTOSCAN_DIRECTORY_LOCATION), ascs->option, ATTR_AUTOSCAN_DIRECTORY_LOCATION);
416             setValue(item, adir->getLocation());
417 
418             item = values.append_child("item");
419             createItem(item, ascs->getItemPath(i, ATTR_AUTOSCAN_DIRECTORY_MODE), ascs->option, ATTR_AUTOSCAN_DIRECTORY_MODE);
420             setValue(item, AutoscanDirectory::mapScanmode(adir->getScanMode()));
421 
422             item = values.append_child("item");
423             createItem(item, ascs->getItemPath(i, ATTR_AUTOSCAN_DIRECTORY_INTERVAL), ascs->option, ATTR_AUTOSCAN_DIRECTORY_INTERVAL);
424             setValue(item, adir->getInterval());
425 
426             item = values.append_child("item");
427             createItem(item, ascs->getItemPath(i, ATTR_AUTOSCAN_DIRECTORY_RECURSIVE), ascs->option, ATTR_AUTOSCAN_DIRECTORY_RECURSIVE);
428             setValue(item, adir->getRecursive());
429 
430             item = values.append_child("item");
431             createItem(item, ascs->getItemPath(i, ATTR_AUTOSCAN_DIRECTORY_HIDDENFILES), ascs->option, ATTR_AUTOSCAN_DIRECTORY_HIDDENFILES);
432             setValue(item, adir->getHidden());
433 
434             item = values.append_child("item");
435             createItem(item, ascs->getItemPath(i, ATTR_AUTOSCAN_DIRECTORY_SCANCOUNT), ascs->option, ATTR_AUTOSCAN_DIRECTORY_SCANCOUNT);
436             setValue(item, adir->getActiveScanCount());
437 
438             item = values.append_child("item");
439             createItem(item, ascs->getItemPath(i, ATTR_AUTOSCAN_DIRECTORY_TASKCOUNT), ascs->option, ATTR_AUTOSCAN_DIRECTORY_TASKCOUNT);
440             setValue(item, adir->getTaskCount());
441 
442             item = values.append_child("item");
443             createItem(item, ascs->getItemPath(i, ATTR_AUTOSCAN_DIRECTORY_LMT), ascs->option, ATTR_AUTOSCAN_DIRECTORY_LMT);
444             setValue(item, fmt::format("{:%Y-%m-%d %H:%M:%S}", fmt::localtime(adir->getPreviousLMT().count())));
445         }
446         if (autoscan->size() == 0) {
447             auto item = values.append_child("item");
448             createItem(item, ascs->getItemPath(ITEM_PATH_NEW, ATTR_AUTOSCAN_DIRECTORY_LOCATION), ascs->option, ATTR_AUTOSCAN_DIRECTORY_LOCATION, ConfigDefinition::findConfigSetup(ATTR_AUTOSCAN_DIRECTORY_LOCATION));
449 
450             item = values.append_child("item");
451             createItem(item, ascs->getItemPath(ITEM_PATH_NEW, ATTR_AUTOSCAN_DIRECTORY_MODE), ascs->option, ATTR_AUTOSCAN_DIRECTORY_MODE, ConfigDefinition::findConfigSetup(ATTR_AUTOSCAN_DIRECTORY_MODE));
452 
453             item = values.append_child("item");
454             createItem(item, ascs->getItemPath(ITEM_PATH_NEW, ATTR_AUTOSCAN_DIRECTORY_INTERVAL), ascs->option, ATTR_AUTOSCAN_DIRECTORY_INTERVAL, ConfigDefinition::findConfigSetup(ATTR_AUTOSCAN_DIRECTORY_INTERVAL));
455 
456             item = values.append_child("item");
457             createItem(item, ascs->getItemPath(ITEM_PATH_NEW, ATTR_AUTOSCAN_DIRECTORY_RECURSIVE), ascs->option, ATTR_AUTOSCAN_DIRECTORY_RECURSIVE, ConfigDefinition::findConfigSetup(ATTR_AUTOSCAN_DIRECTORY_RECURSIVE));
458 
459             item = values.append_child("item");
460             createItem(item, ascs->getItemPath(ITEM_PATH_NEW, ATTR_AUTOSCAN_DIRECTORY_HIDDENFILES), ascs->option, ATTR_AUTOSCAN_DIRECTORY_HIDDENFILES, ConfigDefinition::findConfigSetup(ATTR_AUTOSCAN_DIRECTORY_HIDDENFILES));
461 
462             item = values.append_child("item");
463             createItem(item, ascs->getItemPath(ITEM_PATH_NEW, ATTR_AUTOSCAN_DIRECTORY_SCANCOUNT), ascs->option, ATTR_AUTOSCAN_DIRECTORY_SCANCOUNT, ConfigDefinition::findConfigSetup(ATTR_AUTOSCAN_DIRECTORY_SCANCOUNT));
464 
465             item = values.append_child("item");
466             createItem(item, ascs->getItemPath(ITEM_PATH_NEW, ATTR_AUTOSCAN_DIRECTORY_TASKCOUNT), ascs->option, ATTR_AUTOSCAN_DIRECTORY_TASKCOUNT, ConfigDefinition::findConfigSetup(ATTR_AUTOSCAN_DIRECTORY_TASKCOUNT));
467 
468             item = values.append_child("item");
469             createItem(item, ascs->getItemPath(ITEM_PATH_NEW, ATTR_AUTOSCAN_DIRECTORY_LMT), ascs->option, ATTR_AUTOSCAN_DIRECTORY_LMT, ConfigDefinition::findConfigSetup(ATTR_AUTOSCAN_DIRECTORY_LMT));
470         }
471     }
472 
473     // write content of all dictionaries
474     for (auto&& dcs : ConfigDefinition::getConfigSetupList<ConfigDictionarySetup>()) {
475         int i = 0;
476         auto dictionary = dcs->getValue()->getDictionaryOption(true);
477         for (auto&& [key, val] : dictionary) {
478             auto item = values.append_child("item");
479             createItem(item, dcs->getItemPath(i, dcs->keyOption), dcs->option, dcs->keyOption, dcs);
480             setValue(item, key.substr(5));
481 
482             item = values.append_child("item");
483             createItem(item, dcs->getItemPath(i, dcs->valOption), dcs->option, dcs->valOption, dcs);
484             setValue(item, val);
485             i++;
486         }
487     }
488 
489     // write content of all arrays
490     for (auto&& acs : ConfigDefinition::getConfigSetupList<ConfigArraySetup>()) {
491         auto array = acs->getValue()->getArrayOption(true);
492         for (std::size_t i = 0; i < array.size(); i++) {
493             auto entry = array[i];
494             auto item = values.append_child("item");
495             createItem(item, acs->getItemPath(i), acs->option, acs->attrOption != CFG_MAX ? acs->attrOption : acs->nodeOption, acs);
496             setValue(item, entry);
497         }
498     }
499 
500     // update entries with datebase values
501     for (auto&& entry : dbEntries) {
502         auto exItem = allItems.find(entry.item);
503         if (exItem != allItems.end()) {
504             auto item = exItem->second;
505             item->attribute("source") = "database";
506             item->attribute("status") = entry.status.c_str();
507         } else {
508             auto cs = ConfigDefinition::findConfigSetupByPath(entry.item, true);
509             auto acs = ConfigDefinition::findConfigSetupByPath(entry.item, true, cs);
510             if (cs) {
511                 auto item = values.append_child("item");
512                 createItem(item, entry.item, cs->option, acs ? acs->option : CFG_MAX);
513                 setValue(item, entry.value);
514                 item.attribute("status") = entry.status.c_str();
515                 item.attribute("origValue") = config->getOrigValue(entry.item).c_str();
516                 item.attribute("source") = "database";
517             }
518         }
519     }
520 }
521