1 /*
2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
8
9 #include "AdvancedSettings.h"
10
11 #include "AppParamParser.h"
12 #include "Application.h"
13 #include "LangInfo.h"
14 #include "ServiceBroker.h"
15 #include "filesystem/File.h"
16 #include "filesystem/SpecialProtocol.h"
17 #include "network/DNSNameCache.h"
18 #include "profiles/ProfileManager.h"
19 #include "settings/SettingUtils.h"
20 #include "settings/Settings.h"
21 #include "settings/SettingsComponent.h"
22 #include "settings/lib/Setting.h"
23 #include "settings/lib/SettingDefinitions.h"
24 #include "settings/lib/SettingsManager.h"
25 #include "utils/LangCodeExpander.h"
26 #include "utils/StringUtils.h"
27 #include "utils/SystemInfo.h"
28 #include "utils/URIUtils.h"
29 #include "utils/Variant.h"
30 #include "utils/XMLUtils.h"
31 #include "utils/log.h"
32
33 #include <algorithm>
34 #include <climits>
35 #include <regex>
36 #include <string>
37 #include <vector>
38
39 using namespace ADDON;
40 using namespace XFILE;
41
CAdvancedSettings()42 CAdvancedSettings::CAdvancedSettings()
43 {
44 m_initialized = false;
45 m_fullScreen = false;
46 }
47
OnSettingsLoaded()48 void CAdvancedSettings::OnSettingsLoaded()
49 {
50 const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager();
51
52 // load advanced settings
53 Load(*profileManager);
54
55 // default players?
56 CLog::Log(LOGINFO, "Default Video Player: %s", m_videoDefaultPlayer.c_str());
57 CLog::Log(LOGINFO, "Default Audio Player: %s", m_audioDefaultPlayer.c_str());
58
59 // setup any logging...
60 const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings();
61 if (settings->GetBool(CSettings::SETTING_DEBUG_SHOWLOGINFO))
62 {
63 m_logLevel = std::max(m_logLevelHint, LOG_LEVEL_DEBUG_FREEMEM);
64 CLog::Log(LOGINFO, "Enabled debug logging due to GUI setting (%d)", m_logLevel);
65 }
66 else
67 {
68 m_logLevel = std::min(m_logLevelHint, LOG_LEVEL_DEBUG/*LOG_LEVEL_NORMAL*/);
69 CLog::Log(LOGINFO, "Disabled debug logging due to GUI setting. Level %d.", m_logLevel);
70 }
71 CServiceBroker::GetLogging().SetLogLevel(m_logLevel);
72 }
73
OnSettingsUnloaded()74 void CAdvancedSettings::OnSettingsUnloaded()
75 {
76 m_initialized = false;
77 }
78
OnSettingChanged(const std::shared_ptr<const CSetting> & setting)79 void CAdvancedSettings::OnSettingChanged(const std::shared_ptr<const CSetting>& setting)
80 {
81 if (setting == NULL)
82 return;
83
84 const std::string &settingId = setting->GetId();
85 if (settingId == CSettings::SETTING_DEBUG_SHOWLOGINFO)
86 SetDebugMode(std::static_pointer_cast<const CSettingBool>(setting)->GetValue());
87 }
88
Initialize(const CAppParamParser & params,CSettingsManager & settingsMgr)89 void CAdvancedSettings::Initialize(const CAppParamParser ¶ms, CSettingsManager& settingsMgr)
90 {
91 Initialize();
92
93 params.SetAdvancedSettings(*this);
94
95 settingsMgr.RegisterSettingsHandler(this, true);
96 std::set<std::string> settingSet;
97 settingSet.insert(CSettings::SETTING_DEBUG_SHOWLOGINFO);
98 settingsMgr.RegisterCallback(this, settingSet);
99 }
100
Uninitialize(CSettingsManager & settingsMgr)101 void CAdvancedSettings::Uninitialize(CSettingsManager& settingsMgr)
102 {
103 settingsMgr.UnregisterCallback(this);
104 settingsMgr.UnregisterSettingsHandler(this);
105 settingsMgr.UnregisterSettingOptionsFiller("loggingcomponents");
106
107 Clear();
108
109 m_initialized = false;
110 }
111
Initialize()112 void CAdvancedSettings::Initialize()
113 {
114 if (m_initialized)
115 return;
116
117 m_audioApplyDrc = -1.0f;
118 m_VideoPlayerIgnoreDTSinWAV = false;
119
120 //default hold time of 25 ms, this allows a 20 hertz sine to pass undistorted
121 m_limiterHold = 0.025f;
122 m_limiterRelease = 0.1f;
123
124 m_seekSteps = { 10, 30, 60, 180, 300, 600, 1800 };
125
126 m_audioDefaultPlayer = "paplayer";
127 m_audioPlayCountMinimumPercent = 90.0f;
128
129 m_videoSubsDelayRange = 60;
130 m_videoAudioDelayRange = 10;
131 m_videoUseTimeSeeking = true;
132 m_videoTimeSeekForward = 30;
133 m_videoTimeSeekBackward = -30;
134 m_videoTimeSeekForwardBig = 600;
135 m_videoTimeSeekBackwardBig = -600;
136 m_videoPercentSeekForward = 2;
137 m_videoPercentSeekBackward = -2;
138 m_videoPercentSeekForwardBig = 10;
139 m_videoPercentSeekBackwardBig = -10;
140
141 m_videoPPFFmpegPostProc = "ha:128:7,va,dr";
142 m_videoDefaultPlayer = "VideoPlayer";
143 m_videoIgnoreSecondsAtStart = 3*60;
144 m_videoIgnorePercentAtEnd = 8.0f;
145 m_videoPlayCountMinimumPercent = 90.0f;
146 m_videoVDPAUScaling = -1;
147 m_videoNonLinStretchRatio = 0.5f;
148 m_videoEnableHighQualityHwScalers = false;
149 m_videoAutoScaleMaxFps = 30.0f;
150 m_videoCaptureUseOcclusionQuery = -1; //-1 is auto detect
151 m_videoVDPAUtelecine = false;
152 m_videoVDPAUdeintSkipChromaHD = false;
153 m_DXVACheckCompatibility = false;
154 m_DXVACheckCompatibilityPresent = false;
155 m_videoFpsDetect = 1;
156 m_maxTempo = 1.55f;
157 m_videoPreferStereoStream = false;
158
159 m_videoDefaultLatency = 0.0;
160
161 m_musicUseTimeSeeking = true;
162 m_musicTimeSeekForward = 10;
163 m_musicTimeSeekBackward = -10;
164 m_musicTimeSeekForwardBig = 60;
165 m_musicTimeSeekBackwardBig = -60;
166 m_musicPercentSeekForward = 1;
167 m_musicPercentSeekBackward = -1;
168 m_musicPercentSeekForwardBig = 10;
169 m_musicPercentSeekBackwardBig = -10;
170
171 m_slideshowPanAmount = 2.5f;
172 m_slideshowZoomAmount = 5.0f;
173 m_slideshowBlackBarCompensation = 20.0f;
174
175 m_songInfoDuration = 10;
176
177 m_cddbAddress = "gnudb.gnudb.org";
178 m_addSourceOnTop = false;
179
180 m_handleMounting = g_application.IsStandAlone();
181
182 m_fullScreenOnMovieStart = true;
183 m_cachePath = "special://temp/";
184
185 m_videoCleanDateTimeRegExp = "(.*[^ _\\,\\.\\(\\)\\[\\]\\-])[ _\\.\\(\\)\\[\\]\\-]+(19[0-9][0-9]|20[0-9][0-9])([ _\\,\\.\\(\\)\\[\\]\\-]|[^0-9]$)?";
186
187 m_videoCleanStringRegExps.clear();
188 m_videoCleanStringRegExps.emplace_back("[ _\\,\\.\\(\\)\\[\\]\\-](aka|ac3|dts|custom|dc|remastered|divx|divx5|dsr|dsrip|dutch|dvd|dvd5|dvd9|dvdrip|dvdscr|dvdscreener|screener|dvdivx|cam|fragment|fs|hdtv|hdrip|hdtvrip|internal|limited|multisubs|ntsc|ogg|ogm|pal|pdtv|proper|repack|rerip|retail|r3|r5|bd5|se|svcd|swedish|german|read.nfo|nfofix|unrated|extended|ws|telesync|ts|telecine|tc|brrip|bdrip|480p|480i|576p|576i|720p|720i|1080p|1080i|3d|hrhd|hrhdtv|hddvd|bluray|x264|h264|xvid|xvidvd|xxx|www.www|cd[1-9]|\\[.*\\])([ _\\,\\.\\(\\)\\[\\]\\-]|$)");
189 m_videoCleanStringRegExps.emplace_back("(\\[.*\\])");
190
191 // this vector will be inserted at the end to
192 // m_moviesExcludeFromScanRegExps, m_tvshowExcludeFromScanRegExps and
193 // m_audioExcludeFromScanRegExps
194 m_allExcludeFromScanRegExps.clear();
195 m_allExcludeFromScanRegExps.emplace_back("[\\/].+\\.ite[\\/]"); // ignore itunes extras dir
196 m_allExcludeFromScanRegExps.emplace_back("[\\/]\\.\\_");
197 m_allExcludeFromScanRegExps.emplace_back("\\.DS_Store");
198 m_allExcludeFromScanRegExps.emplace_back("\\.AppleDouble");
199
200 m_moviesExcludeFromScanRegExps.clear();
201 m_moviesExcludeFromScanRegExps.emplace_back("-trailer");
202 m_moviesExcludeFromScanRegExps.emplace_back("[!-._ \\\\/]sample[-._ \\\\/]");
203 m_moviesExcludeFromScanRegExps.emplace_back("[\\/](proof|subs)[\\/]");
204 m_moviesExcludeFromScanRegExps.insert(m_moviesExcludeFromScanRegExps.end(),
205 m_allExcludeFromScanRegExps.begin(),
206 m_allExcludeFromScanRegExps.end());
207
208
209 m_tvshowExcludeFromScanRegExps.clear();
210 m_tvshowExcludeFromScanRegExps.emplace_back("[!-._ \\\\/]sample[-._ \\\\/]");
211 m_tvshowExcludeFromScanRegExps.insert(m_tvshowExcludeFromScanRegExps.end(),
212 m_allExcludeFromScanRegExps.begin(),
213 m_allExcludeFromScanRegExps.end());
214
215
216 m_audioExcludeFromScanRegExps.clear();
217 m_audioExcludeFromScanRegExps.insert(m_audioExcludeFromScanRegExps.end(),
218 m_allExcludeFromScanRegExps.begin(),
219 m_allExcludeFromScanRegExps.end());
220
221 m_folderStackRegExps.clear();
222 m_folderStackRegExps.emplace_back("((cd|dvd|dis[ck])[0-9]+)$");
223
224 m_videoStackRegExps.clear();
225 m_videoStackRegExps.emplace_back("(.*?)([ _.-]*(?:cd|dvd|p(?:(?:ar)?t)|dis[ck])[ _.-]*[0-9]+)(.*?)(\\.[^.]+)$");
226 m_videoStackRegExps.emplace_back("(.*?)([ _.-]*(?:cd|dvd|p(?:(?:ar)?t)|dis[ck])[ _.-]*[a-d])(.*?)(\\.[^.]+)$");
227 m_videoStackRegExps.emplace_back("(.*?)([ ._-]*[a-d])(.*?)(\\.[^.]+)$");
228 // This one is a bit too greedy to enable by default. It will stack sequels
229 // in a flat dir structure, but is perfectly safe in a dir-per-vid one.
230 //m_videoStackRegExps.push_back("(.*?)([ ._-]*[0-9])(.*?)(\\.[^.]+)$");
231
232 m_tvshowEnumRegExps.clear();
233 // foo.s01.e01, foo.s01_e01, S01E02 foo, S01 - E02, S01xE02
234 m_tvshowEnumRegExps.push_back(TVShowRegexp(false,"s([0-9]+)[ ._x-]*e([0-9]+(?:(?:[a-i]|\\.[1-9])(?![0-9]))?)([^\\\\/]*)$"));
235 // foo.ep01, foo.EP_01, foo.E01
236 m_tvshowEnumRegExps.push_back(TVShowRegexp(
237 false, "[\\._ -]?()e(?:p[ ._-]?)?([0-9]+(?:(?:[a-i]|\\.[1-9])(?![0-9]))?)([^\\\\/]*)$"));
238 // foo.yyyy.mm.dd.* (byDate=true)
239 m_tvshowEnumRegExps.push_back(TVShowRegexp(true,"([0-9]{4})[\\.-]([0-9]{2})[\\.-]([0-9]{2})"));
240 // foo.mm.dd.yyyy.* (byDate=true)
241 m_tvshowEnumRegExps.push_back(TVShowRegexp(true,"([0-9]{2})[\\.-]([0-9]{2})[\\.-]([0-9]{4})"));
242 // foo.1x09* or just /1x09*
243 m_tvshowEnumRegExps.push_back(TVShowRegexp(false,"[\\\\/\\._ \\[\\(-]([0-9]+)x([0-9]+(?:(?:[a-i]|\\.[1-9])(?![0-9]))?)([^\\\\/]*)$"));
244 // Part I, Pt.VI, Part 1
245 m_tvshowEnumRegExps.push_back(TVShowRegexp(false,"[\\/._ -]p(?:ar)?t[_. -]()([ivx]+|[0-9]+)([._ -][^\\/]*)$"));
246 // foo.103*, 103 foo
247 // XXX: This regex is greedy and will match years in show names. It should always be last.
248 m_tvshowEnumRegExps.push_back(TVShowRegexp(false,"[\\\\/\\._ -]([0-9]+)([0-9][0-9](?:(?:[a-i]|\\.[1-9])(?![0-9]))?)([\\._ -][^\\\\/]*)$"));
249
250 m_tvshowMultiPartEnumRegExp = "^[-_ex]+([0-9]+(?:(?:[a-i]|\\.[1-9])(?![0-9]))?)";
251
252 m_remoteDelay = 3;
253 m_bScanIRServer = true;
254
255 m_playlistAsFolders = true;
256 m_detectAsUdf = false;
257
258 m_fanartRes = 1080;
259 m_imageRes = 720;
260 m_imageScalingAlgorithm = CPictureScalingAlgorithm::Default;
261
262 m_sambaclienttimeout = 30;
263 m_sambadoscodepage = "";
264 m_sambastatfiles = true;
265
266 m_bHTTPDirectoryStatFilesize = false;
267
268 m_bFTPThumbs = false;
269
270 m_bShoutcastArt = true;
271
272 m_musicThumbs = "folder.jpg|Folder.jpg|folder.JPG|Folder.JPG|cover.jpg|Cover.jpg|cover.jpeg|thumb.jpg|Thumb.jpg|thumb.JPG|Thumb.JPG";
273 m_musicArtistExtraArt = { };
274 m_musicAlbumExtraArt = {};
275
276 m_bMusicLibraryAllItemsOnBottom = false;
277 m_bMusicLibraryCleanOnUpdate = false;
278 m_bMusicLibraryArtistSortOnUpdate = false;
279 m_iMusicLibraryRecentlyAddedItems = 25;
280 m_strMusicLibraryAlbumFormat = "";
281 m_prioritiseAPEv2tags = false;
282 m_musicItemSeparator = " / ";
283 m_musicArtistSeparators = { ";", " feat. ", " ft. " };
284 m_videoItemSeparator = " / ";
285 m_iMusicLibraryDateAdded = 1; // prefer mtime over ctime and current time
286 m_bMusicLibraryUseISODates = false;
287
288 m_bVideoLibraryAllItemsOnBottom = false;
289 m_iVideoLibraryRecentlyAddedItems = 25;
290 m_bVideoLibraryCleanOnUpdate = false;
291 m_bVideoLibraryUseFastHash = true;
292 m_bVideoLibraryImportWatchedState = false;
293 m_bVideoLibraryImportResumePoint = false;
294 m_bVideoScannerIgnoreErrors = false;
295 m_iVideoLibraryDateAdded = 1; // prefer mtime over ctime and current time
296
297 m_videoEpisodeExtraArt = {};
298 m_videoTvShowExtraArt = {};
299 m_videoTvSeasonExtraArt = {};
300 m_videoMovieExtraArt = {};
301 m_videoMovieSetExtraArt = {};
302 m_videoMusicVideoExtraArt = {};
303
304 m_iEpgUpdateCheckInterval = 300; /* Check every X seconds, if EPG data need to be updated. This does not mean that
305 every X seconds an EPG update is actually triggered, it's just the interval how
306 often to check whether an update should be triggered. If this value is greater
307 than GUI setting 'epg.epgupdate' value, then EPG updates will done with the value
308 specified for 'updatecheckinterval', effectively overriding the GUI setting's value. */
309 m_iEpgCleanupInterval = 900; /* Remove old entries from the EPG every X seconds */
310 m_iEpgActiveTagCheckInterval = 60; /* Check for updated active tags every X seconds */
311 m_iEpgRetryInterruptedUpdateInterval = 30; /* Retry an interrupted EPG update after X seconds */
312 m_iEpgUpdateEmptyTagsInterval = 7200; /* If a TV channel has no EPG data, try to obtain data for that channel every
313 X seconds. This overrides the GUI setting 'epg.epgupdate' value, but only
314 for channels without EPG data. If this value is less than 'updatecheckinterval'
315 value, then data update will be done with the interval specified by
316 'updatecheckinterval'.
317 Example 1: epg.epgupdate = 120 (minutes!), updatecheckinterval = 300,
318 updateemptytagsinterval = 60 => trigger an EPG update for every
319 channel without EPG data every 5 minutes and trigger an EPG update
320 for every channel with EPG data every 2 hours.
321 Example 2: epg.epgupdate = 120 (minutes!), updatecheckinterval = 300,
322 updateemptytagsinterval = 3600 => trigger an EPG update for every
323 channel without EPG data every 2 hours and trigger an EPG update
324 for every channel with EPG data every 1 hour. */
325 m_bEpgDisplayUpdatePopup = true; /* Display a progress popup while updating EPG data from clients */
326 m_bEpgDisplayIncrementalUpdatePopup = false; /* Display a progress popup while doing incremental EPG updates, but
327 only if 'displayupdatepopup' is also enabled. */
328
329 m_bEdlMergeShortCommBreaks = false; // Off by default
330 m_iEdlMaxCommBreakLength = 8 * 30 + 10; // Just over 8 * 30 second commercial break.
331 m_iEdlMinCommBreakLength = 3 * 30; // 3 * 30 second commercial breaks.
332 m_iEdlMaxCommBreakGap = 4 * 30; // 4 * 30 second commercial breaks.
333 m_iEdlMaxStartGap = 5 * 60; // 5 minutes.
334 m_iEdlCommBreakAutowait = 0; // Off by default
335 m_iEdlCommBreakAutowind = 0; // Off by default
336
337 m_curlconnecttimeout = 30;
338 m_curllowspeedtime = 20;
339 m_curlretries = 2;
340 m_curlKeepAliveInterval = 30;
341 m_curlDisableIPV6 = false; //Certain hardware/OS combinations have trouble
342 //with ipv6.
343 m_curlDisableHTTP2 = false;
344
345 #if defined(TARGET_DARWIN_EMBEDDED)
346 m_startFullScreen = true;
347 #else
348 m_startFullScreen = false;
349 #endif
350 m_showExitButton = true;
351 m_splashImage = true;
352
353 m_playlistRetries = 100;
354 m_playlistTimeout = 20; // 20 seconds timeout
355 m_GLRectangleHack = false;
356 m_iSkipLoopFilter = 0;
357 m_bVirtualShares = true;
358 m_bTry10bitOutput = false;
359
360 m_cpuTempCmd = "";
361 m_gpuTempCmd = "";
362 #if defined(TARGET_DARWIN)
363 // default for osx is fullscreen always on top
364 m_alwaysOnTop = true;
365 #else
366 // default for windows is not always on top
367 m_alwaysOnTop = false;
368 #endif
369
370 m_iPVRTimeCorrection = 0;
371 m_iPVRInfoToggleInterval = 3000;
372 m_bPVRChannelIconsAutoScan = true;
373 m_bPVRAutoScanIconsUserSet = false;
374 m_iPVRNumericChannelSwitchTimeout = 2000;
375 m_iPVRTimeshiftThreshold = 10;
376 m_bPVRTimeshiftSimpleOSD = true;
377 m_PVRDefaultSortOrder.sortBy = SortByDate;
378 m_PVRDefaultSortOrder.sortOrder = SortOrderDescending;
379
380 m_cacheMemSize = 1024 * 1024 * 20; // 20 MiB
381 m_cacheBufferMode = CACHE_BUFFER_MODE_NETWORK; // Default (buffer all network filesystems)
382 m_cacheChunkSize = 128 * 1024; // 128 KiB
383
384 // the following setting determines the readRate of a player data
385 // as multiply of the default data read rate
386 m_cacheReadFactor = 4.0f;
387
388 m_addonPackageFolderSize = 200;
389
390 m_jsonOutputCompact = true;
391 m_jsonTcpPort = 9090;
392
393 m_enableMultimediaKeys = false;
394
395 m_canWindowed = true;
396 m_guiVisualizeDirtyRegions = false;
397 m_guiAlgorithmDirtyRegions = 3;
398 m_guiSmartRedraw = false;
399 m_airTunesPort = 36666;
400 m_airPlayPort = 36667;
401
402 m_databaseMusic.Reset();
403 m_databaseVideo.Reset();
404
405 m_useLocaleCollation = true;
406
407 m_pictureExtensions = ".png|.jpg|.jpeg|.bmp|.gif|.ico|.tif|.tiff|.tga|.pcx|.cbz|.zip|.rss|.webp|.jp2|.apng";
408 m_musicExtensions = ".nsv|.m4a|.flac|.aac|.strm|.pls|.rm|.rma|.mpa|.wav|.wma|.ogg|.mp3|.mp2|.m3u|.gdm|.imf|.m15|.sfx|.uni|.ac3|.dts|.cue|.aif|.aiff|.wpl|.xspf|.ape|.mac|.mpc|.mp+|.mpp|.shn|.zip|.wv|.dsp|.xsp|.xwav|.waa|.wvs|.wam|.gcm|.idsp|.mpdsp|.mss|.spt|.rsd|.sap|.cmc|.cmr|.dmc|.mpt|.mpd|.rmt|.tmc|.tm8|.tm2|.oga|.url|.pxml|.tta|.rss|.wtv|.mka|.tak|.opus|.dff|.dsf|.m4b|.dtshd";
409 m_videoExtensions = ".m4v|.3g2|.3gp|.nsv|.tp|.ts|.ty|.strm|.pls|.rm|.rmvb|.mpd|.m3u|.m3u8|.ifo|.mov|.qt|.divx|.xvid|.bivx|.vob|.nrg|.img|.iso|.udf|.pva|.wmv|.asf|.asx|.ogm|.m2v|.avi|.bin|.dat|.mpg|.mpeg|.mp4|.mkv|.mk3d|.avc|.vp3|.svq3|.nuv|.viv|.dv|.fli|.flv|.001|.wpl|.xspf|.zip|.vdr|.dvr-ms|.xsp|.mts|.m2t|.m2ts|.evo|.ogv|.sdp|.avs|.rec|.url|.pxml|.vc1|.h264|.rcv|.rss|.mpls|.mpl|.webm|.bdmv|.bdm|.wtv|.trp|.f4v";
410 m_subtitlesExtensions = ".utf|.utf8|.utf-8|.sub|.srt|.smi|.rt|.txt|.ssa|.text|.ssa|.aqt|.jss|.ass|.idx|.ifo|.zip";
411 m_discStubExtensions = ".disc";
412 // internal music extensions
413 m_musicExtensions += "|.cdda";
414 // internal video extensions
415 m_videoExtensions += "|.pvr";
416
417 m_stereoscopicregex_3d = "[-. _]3d[-. _]";
418 m_stereoscopicregex_sbs = "[-. _]h?sbs[-. _]";
419 m_stereoscopicregex_tab = "[-. _]h?tab[-. _]";
420
421 m_videoAssFixedWorks = false;
422
423 m_logLevelHint = m_logLevel = LOG_LEVEL_NORMAL;
424
425 m_openGlDebugging = false;
426
427 m_userAgent = g_sysinfo.GetUserAgent();
428
429 m_nfsTimeout = 5;
430 m_nfsRetries = -1;
431
432 m_initialized = true;
433 }
434
Load(const CProfileManager & profileManager)435 bool CAdvancedSettings::Load(const CProfileManager &profileManager)
436 {
437 // NOTE: This routine should NOT set the default of any of these parameters
438 // it should instead use the versions of GetString/Integer/Float that
439 // don't take defaults in. Defaults are set in the constructor above
440 Initialize(); // In case of profile switch.
441 ParseSettingsFile("special://xbmc/system/advancedsettings.xml");
442 for (unsigned int i = 0; i < m_settingsFiles.size(); i++)
443 ParseSettingsFile(m_settingsFiles[i]);
444
445 ParseSettingsFile(profileManager.GetUserDataItem("advancedsettings.xml"));
446
447 // Add the list of disc stub extensions (if any) to the list of video extensions
448 if (!m_discStubExtensions.empty())
449 m_videoExtensions += "|" + m_discStubExtensions;
450
451 return true;
452 }
453
ParseSettingsFile(const std::string & file)454 void CAdvancedSettings::ParseSettingsFile(const std::string &file)
455 {
456 CXBMCTinyXML advancedXML;
457 if (!CFile::Exists(file))
458 {
459 CLog::Log(LOGINFO, "No settings file to load (%s)", file.c_str());
460 return;
461 }
462
463 if (!advancedXML.LoadFile(file))
464 {
465 CLog::Log(LOGERROR, "Error loading %s, Line %d\n%s", file.c_str(), advancedXML.ErrorRow(), advancedXML.ErrorDesc());
466 return;
467 }
468
469 TiXmlElement *pRootElement = advancedXML.RootElement();
470 if (!pRootElement || StringUtils::CompareNoCase(pRootElement->Value(), "advancedsettings") != 0)
471 {
472 CLog::Log(LOGERROR, "Error loading %s, no <advancedsettings> node", file.c_str());
473 return;
474 }
475
476 // succeeded - tell the user it worked
477 CLog::Log(LOGINFO, "Loaded settings file from %s", file.c_str());
478
479 //Make a copy of the AS.xml and hide advancedsettings passwords
480 CXBMCTinyXML advancedXMLCopy(advancedXML);
481 TiXmlNode *pRootElementCopy = advancedXMLCopy.RootElement();
482 for (const auto& dbname : { "videodatabase", "musicdatabase", "tvdatabase", "epgdatabase" })
483 {
484 TiXmlNode *db = pRootElementCopy->FirstChild(dbname);
485 if (db)
486 {
487 TiXmlNode *passTag = db->FirstChild("pass");
488 if (passTag)
489 {
490 TiXmlNode *pass = passTag->FirstChild();
491 if (pass)
492 {
493 passTag->RemoveChild(pass);
494 passTag->LinkEndChild(new TiXmlText("*****"));
495 }
496 }
497 }
498 }
499 TiXmlNode *network = pRootElementCopy->FirstChild("network");
500 if (network)
501 {
502 TiXmlNode *passTag = network->FirstChild("httpproxypassword");
503 if (passTag)
504 {
505 TiXmlNode *pass = passTag->FirstChild();
506 if (pass)
507 {
508 passTag->RemoveChild(pass);
509 passTag->LinkEndChild(new TiXmlText("*****"));
510 }
511 }
512 if (network->FirstChildElement("nfstimeout"))
513 {
514 #ifdef HAS_NFS_SET_TIMEOUT
515 XMLUtils::GetUInt(network, "nfstimeout", m_nfsTimeout, 0, 3600);
516 #else
517 CLog::Log(LOGWARNING, "nfstimeout unsupported");
518 #endif
519 }
520 if (network->FirstChildElement("nfsretries"))
521 {
522 XMLUtils::GetInt(network, "nfsretries", m_nfsRetries, -1, 30);
523 }
524 }
525
526 // Dump contents of copied AS.xml to debug log
527 TiXmlPrinter printer;
528 printer.SetLineBreak("\n");
529 printer.SetIndent(" ");
530 advancedXMLCopy.Accept(&printer);
531 // redact User/pass in URLs
532 std::regex redactRe("(\\w+://)\\S+:\\S+@");
533 CLog::Log(LOGINFO, "Contents of {} are...\n{}", file,
534 std::regex_replace(printer.CStr(), redactRe, "$1USERNAME:PASSWORD@"));
535
536 TiXmlElement *pElement = pRootElement->FirstChildElement("audio");
537 if (pElement)
538 {
539 XMLUtils::GetString(pElement, "defaultplayer", m_audioDefaultPlayer);
540 // 101 on purpose - can be used to never automark as watched
541 XMLUtils::GetFloat(pElement, "playcountminimumpercent", m_audioPlayCountMinimumPercent, 0.0f, 101.0f);
542
543 XMLUtils::GetBoolean(pElement, "usetimeseeking", m_musicUseTimeSeeking);
544 XMLUtils::GetInt(pElement, "timeseekforward", m_musicTimeSeekForward, 0, 6000);
545 XMLUtils::GetInt(pElement, "timeseekbackward", m_musicTimeSeekBackward, -6000, 0);
546 XMLUtils::GetInt(pElement, "timeseekforwardbig", m_musicTimeSeekForwardBig, 0, 6000);
547 XMLUtils::GetInt(pElement, "timeseekbackwardbig", m_musicTimeSeekBackwardBig, -6000, 0);
548
549 XMLUtils::GetInt(pElement, "percentseekforward", m_musicPercentSeekForward, 0, 100);
550 XMLUtils::GetInt(pElement, "percentseekbackward", m_musicPercentSeekBackward, -100, 0);
551 XMLUtils::GetInt(pElement, "percentseekforwardbig", m_musicPercentSeekForwardBig, 0, 100);
552 XMLUtils::GetInt(pElement, "percentseekbackwardbig", m_musicPercentSeekBackwardBig, -100, 0);
553
554 TiXmlElement* pAudioExcludes = pElement->FirstChildElement("excludefromlisting");
555 if (pAudioExcludes)
556 GetCustomRegexps(pAudioExcludes, m_audioExcludeFromListingRegExps);
557
558 pAudioExcludes = pElement->FirstChildElement("excludefromscan");
559 if (pAudioExcludes)
560 GetCustomRegexps(pAudioExcludes, m_audioExcludeFromScanRegExps);
561
562 XMLUtils::GetFloat(pElement, "applydrc", m_audioApplyDrc);
563 XMLUtils::GetBoolean(pElement, "VideoPlayerignoredtsinwav", m_VideoPlayerIgnoreDTSinWAV);
564
565 XMLUtils::GetFloat(pElement, "limiterhold", m_limiterHold, 0.0f, 100.0f);
566 XMLUtils::GetFloat(pElement, "limiterrelease", m_limiterRelease, 0.001f, 100.0f);
567 }
568
569 pElement = pRootElement->FirstChildElement("x11");
570 if (pElement)
571 {
572 XMLUtils::GetBoolean(pElement, "omlsync", m_omlSync);
573 }
574
575 pElement = pRootElement->FirstChildElement("video");
576 if (pElement)
577 {
578 XMLUtils::GetBoolean(pElement, "assfixedworks", m_videoAssFixedWorks);
579 XMLUtils::GetString(pElement, "stereoscopicregex3d", m_stereoscopicregex_3d);
580 XMLUtils::GetString(pElement, "stereoscopicregexsbs", m_stereoscopicregex_sbs);
581 XMLUtils::GetString(pElement, "stereoscopicregextab", m_stereoscopicregex_tab);
582 XMLUtils::GetFloat(pElement, "subsdelayrange", m_videoSubsDelayRange, 10, 600);
583 XMLUtils::GetFloat(pElement, "audiodelayrange", m_videoAudioDelayRange, 10, 600);
584 XMLUtils::GetString(pElement, "defaultplayer", m_videoDefaultPlayer);
585 XMLUtils::GetBoolean(pElement, "fullscreenonmoviestart", m_fullScreenOnMovieStart);
586 // 101 on purpose - can be used to never automark as watched
587 XMLUtils::GetFloat(pElement, "playcountminimumpercent", m_videoPlayCountMinimumPercent, 0.0f, 101.0f);
588 XMLUtils::GetInt(pElement, "ignoresecondsatstart", m_videoIgnoreSecondsAtStart, 0, 900);
589 XMLUtils::GetFloat(pElement, "ignorepercentatend", m_videoIgnorePercentAtEnd, 0, 100.0f);
590
591 XMLUtils::GetBoolean(pElement, "usetimeseeking", m_videoUseTimeSeeking);
592 XMLUtils::GetInt(pElement, "timeseekforward", m_videoTimeSeekForward, 0, 6000);
593 XMLUtils::GetInt(pElement, "timeseekbackward", m_videoTimeSeekBackward, -6000, 0);
594 XMLUtils::GetInt(pElement, "timeseekforwardbig", m_videoTimeSeekForwardBig, 0, 6000);
595 XMLUtils::GetInt(pElement, "timeseekbackwardbig", m_videoTimeSeekBackwardBig, -6000, 0);
596
597 XMLUtils::GetInt(pElement, "percentseekforward", m_videoPercentSeekForward, 0, 100);
598 XMLUtils::GetInt(pElement, "percentseekbackward", m_videoPercentSeekBackward, -100, 0);
599 XMLUtils::GetInt(pElement, "percentseekforwardbig", m_videoPercentSeekForwardBig, 0, 100);
600 XMLUtils::GetInt(pElement, "percentseekbackwardbig", m_videoPercentSeekBackwardBig, -100, 0);
601
602 TiXmlElement* pVideoExcludes = pElement->FirstChildElement("excludefromlisting");
603 if (pVideoExcludes)
604 GetCustomRegexps(pVideoExcludes, m_videoExcludeFromListingRegExps);
605
606 pVideoExcludes = pElement->FirstChildElement("excludefromscan");
607 if (pVideoExcludes)
608 GetCustomRegexps(pVideoExcludes, m_moviesExcludeFromScanRegExps);
609
610 pVideoExcludes = pElement->FirstChildElement("excludetvshowsfromscan");
611 if (pVideoExcludes)
612 GetCustomRegexps(pVideoExcludes, m_tvshowExcludeFromScanRegExps);
613
614 pVideoExcludes = pElement->FirstChildElement("cleanstrings");
615 if (pVideoExcludes)
616 GetCustomRegexps(pVideoExcludes, m_videoCleanStringRegExps);
617
618 XMLUtils::GetString(pElement,"cleandatetime", m_videoCleanDateTimeRegExp);
619 XMLUtils::GetString(pElement,"ppffmpegpostprocessing",m_videoPPFFmpegPostProc);
620 XMLUtils::GetInt(pElement,"vdpauscaling",m_videoVDPAUScaling);
621 XMLUtils::GetFloat(pElement, "nonlinearstretchratio", m_videoNonLinStretchRatio, 0.01f, 1.0f);
622 XMLUtils::GetBoolean(pElement,"enablehighqualityhwscalers", m_videoEnableHighQualityHwScalers);
623 XMLUtils::GetFloat(pElement,"autoscalemaxfps",m_videoAutoScaleMaxFps, 0.0f, 1000.0f);
624 XMLUtils::GetInt(pElement, "useocclusionquery", m_videoCaptureUseOcclusionQuery, -1, 1);
625 XMLUtils::GetBoolean(pElement,"vdpauInvTelecine",m_videoVDPAUtelecine);
626 XMLUtils::GetBoolean(pElement,"vdpauHDdeintSkipChroma",m_videoVDPAUdeintSkipChromaHD);
627
628 TiXmlElement* pAdjustRefreshrate = pElement->FirstChildElement("adjustrefreshrate");
629 if (pAdjustRefreshrate)
630 {
631 TiXmlElement* pRefreshOverride = pAdjustRefreshrate->FirstChildElement("override");
632 while (pRefreshOverride)
633 {
634 RefreshOverride override = {0};
635
636 float fps;
637 if (XMLUtils::GetFloat(pRefreshOverride, "fps", fps))
638 {
639 override.fpsmin = fps - 0.01f;
640 override.fpsmax = fps + 0.01f;
641 }
642
643 float fpsmin, fpsmax;
644 if (XMLUtils::GetFloat(pRefreshOverride, "fpsmin", fpsmin) &&
645 XMLUtils::GetFloat(pRefreshOverride, "fpsmax", fpsmax))
646 {
647 override.fpsmin = fpsmin;
648 override.fpsmax = fpsmax;
649 }
650
651 float refresh;
652 if (XMLUtils::GetFloat(pRefreshOverride, "refresh", refresh))
653 {
654 override.refreshmin = refresh - 0.01f;
655 override.refreshmax = refresh + 0.01f;
656 }
657
658 float refreshmin, refreshmax;
659 if (XMLUtils::GetFloat(pRefreshOverride, "refreshmin", refreshmin) &&
660 XMLUtils::GetFloat(pRefreshOverride, "refreshmax", refreshmax))
661 {
662 override.refreshmin = refreshmin;
663 override.refreshmax = refreshmax;
664 }
665
666 bool fpsCorrect = (override.fpsmin > 0.0f && override.fpsmax >= override.fpsmin);
667 bool refreshCorrect = (override.refreshmin > 0.0f && override.refreshmax >= override.refreshmin);
668
669 if (fpsCorrect && refreshCorrect)
670 m_videoAdjustRefreshOverrides.push_back(override);
671 else
672 CLog::Log(LOGWARNING, "Ignoring malformed refreshrate override, fpsmin:%f fpsmax:%f refreshmin:%f refreshmax:%f",
673 override.fpsmin, override.fpsmax, override.refreshmin, override.refreshmax);
674
675 pRefreshOverride = pRefreshOverride->NextSiblingElement("override");
676 }
677
678 TiXmlElement* pRefreshFallback = pAdjustRefreshrate->FirstChildElement("fallback");
679 while (pRefreshFallback)
680 {
681 RefreshOverride fallback = {0};
682 fallback.fallback = true;
683
684 float refresh;
685 if (XMLUtils::GetFloat(pRefreshFallback, "refresh", refresh))
686 {
687 fallback.refreshmin = refresh - 0.01f;
688 fallback.refreshmax = refresh + 0.01f;
689 }
690
691 float refreshmin, refreshmax;
692 if (XMLUtils::GetFloat(pRefreshFallback, "refreshmin", refreshmin) &&
693 XMLUtils::GetFloat(pRefreshFallback, "refreshmax", refreshmax))
694 {
695 fallback.refreshmin = refreshmin;
696 fallback.refreshmax = refreshmax;
697 }
698
699 if (fallback.refreshmin > 0.0f && fallback.refreshmax >= fallback.refreshmin)
700 m_videoAdjustRefreshOverrides.push_back(fallback);
701 else
702 CLog::Log(LOGWARNING, "Ignoring malformed refreshrate fallback, fpsmin:%f fpsmax:%f refreshmin:%f refreshmax:%f",
703 fallback.fpsmin, fallback.fpsmax, fallback.refreshmin, fallback.refreshmax);
704
705 pRefreshFallback = pRefreshFallback->NextSiblingElement("fallback");
706 }
707 }
708
709 m_DXVACheckCompatibilityPresent = XMLUtils::GetBoolean(pElement,"checkdxvacompatibility", m_DXVACheckCompatibility);
710
711 //0 = disable fps detect, 1 = only detect on timestamps with uniform spacing, 2 detect on all timestamps
712 XMLUtils::GetInt(pElement, "fpsdetect", m_videoFpsDetect, 0, 2);
713 XMLUtils::GetFloat(pElement, "maxtempo", m_maxTempo, 1.5, 2.1);
714 XMLUtils::GetBoolean(pElement, "preferstereostream", m_videoPreferStereoStream);
715
716 // Store global display latency settings
717 TiXmlElement* pVideoLatency = pElement->FirstChildElement("latency");
718 if (pVideoLatency)
719 {
720 float refresh, refreshmin, refreshmax, delay;
721 TiXmlElement* pRefreshVideoLatency = pVideoLatency->FirstChildElement("refresh");
722
723 while (pRefreshVideoLatency)
724 {
725 RefreshVideoLatency videolatency = {0};
726
727 if (XMLUtils::GetFloat(pRefreshVideoLatency, "rate", refresh))
728 {
729 videolatency.refreshmin = refresh - 0.01f;
730 videolatency.refreshmax = refresh + 0.01f;
731 }
732 else if (XMLUtils::GetFloat(pRefreshVideoLatency, "min", refreshmin) &&
733 XMLUtils::GetFloat(pRefreshVideoLatency, "max", refreshmax))
734 {
735 videolatency.refreshmin = refreshmin;
736 videolatency.refreshmax = refreshmax;
737 }
738 if (XMLUtils::GetFloat(pRefreshVideoLatency, "delay", delay, -600.0f, 600.0f))
739 videolatency.delay = delay;
740
741 if (videolatency.refreshmin > 0.0f && videolatency.refreshmax >= videolatency.refreshmin)
742 m_videoRefreshLatency.push_back(videolatency);
743 else
744 CLog::Log(LOGWARNING, "Ignoring malformed display latency <refresh> entry, min:%f max:%f", videolatency.refreshmin, videolatency.refreshmax);
745
746 pRefreshVideoLatency = pRefreshVideoLatency->NextSiblingElement("refresh");
747 }
748
749 // Get default global display latency
750 XMLUtils::GetFloat(pVideoLatency, "delay", m_videoDefaultLatency, -600.0f, 600.0f);
751 }
752 }
753
754 pElement = pRootElement->FirstChildElement("musiclibrary");
755 if (pElement)
756 {
757 XMLUtils::GetInt(pElement, "recentlyaddeditems", m_iMusicLibraryRecentlyAddedItems, 1, INT_MAX);
758 XMLUtils::GetBoolean(pElement, "prioritiseapetags", m_prioritiseAPEv2tags);
759 XMLUtils::GetBoolean(pElement, "allitemsonbottom", m_bMusicLibraryAllItemsOnBottom);
760 XMLUtils::GetBoolean(pElement, "cleanonupdate", m_bMusicLibraryCleanOnUpdate);
761 XMLUtils::GetBoolean(pElement, "artistsortonupdate", m_bMusicLibraryArtistSortOnUpdate);
762 XMLUtils::GetString(pElement, "albumformat", m_strMusicLibraryAlbumFormat);
763 XMLUtils::GetString(pElement, "itemseparator", m_musicItemSeparator);
764 XMLUtils::GetInt(pElement, "dateadded", m_iMusicLibraryDateAdded);
765 XMLUtils::GetBoolean(pElement, "useisodates", m_bMusicLibraryUseISODates);
766 //Music artist name separators
767 TiXmlElement* separators = pElement->FirstChildElement("artistseparators");
768 if (separators)
769 {
770 m_musicArtistSeparators.clear();
771 TiXmlNode* separator = separators->FirstChild("separator");
772 while (separator)
773 {
774 if (separator->FirstChild())
775 m_musicArtistSeparators.push_back(separator->FirstChild()->ValueStr());
776 separator = separator->NextSibling("separator");
777 }
778 }
779
780 SetExtraArtwork(pElement->FirstChildElement("artistextraart"), m_musicArtistExtraArt);
781 SetExtraArtwork(pElement->FirstChildElement("albumextraart"), m_musicAlbumExtraArt);
782 }
783
784 pElement = pRootElement->FirstChildElement("videolibrary");
785 if (pElement)
786 {
787 XMLUtils::GetBoolean(pElement, "allitemsonbottom", m_bVideoLibraryAllItemsOnBottom);
788 XMLUtils::GetInt(pElement, "recentlyaddeditems", m_iVideoLibraryRecentlyAddedItems, 1, INT_MAX);
789 XMLUtils::GetBoolean(pElement, "cleanonupdate", m_bVideoLibraryCleanOnUpdate);
790 XMLUtils::GetBoolean(pElement, "usefasthash", m_bVideoLibraryUseFastHash);
791 XMLUtils::GetString(pElement, "itemseparator", m_videoItemSeparator);
792 XMLUtils::GetBoolean(pElement, "importwatchedstate", m_bVideoLibraryImportWatchedState);
793 XMLUtils::GetBoolean(pElement, "importresumepoint", m_bVideoLibraryImportResumePoint);
794 XMLUtils::GetInt(pElement, "dateadded", m_iVideoLibraryDateAdded);
795
796 SetExtraArtwork(pElement->FirstChildElement("episodeextraart"), m_videoEpisodeExtraArt);
797 SetExtraArtwork(pElement->FirstChildElement("tvshowextraart"), m_videoTvShowExtraArt);
798 SetExtraArtwork(pElement->FirstChildElement("tvseasonextraart"), m_videoTvSeasonExtraArt);
799 SetExtraArtwork(pElement->FirstChildElement("movieextraart"), m_videoMovieExtraArt);
800 SetExtraArtwork(pElement->FirstChildElement("moviesetextraart"), m_videoMovieSetExtraArt);
801 SetExtraArtwork(pElement->FirstChildElement("musicvideoextraart"), m_videoMusicVideoExtraArt);
802 }
803
804 pElement = pRootElement->FirstChildElement("videoscanner");
805 if (pElement)
806 {
807 XMLUtils::GetBoolean(pElement, "ignoreerrors", m_bVideoScannerIgnoreErrors);
808 }
809
810 // Backward-compatibility of ExternalPlayer config
811 pElement = pRootElement->FirstChildElement("externalplayer");
812 if (pElement)
813 {
814 CLog::Log(LOGWARNING, "External player configuration has been removed from advancedsettings.xml. It can now be configured in userdata/playercorefactory.xml");
815 }
816 pElement = pRootElement->FirstChildElement("slideshow");
817 if (pElement)
818 {
819 XMLUtils::GetFloat(pElement, "panamount", m_slideshowPanAmount, 0.0f, 20.0f);
820 XMLUtils::GetFloat(pElement, "zoomamount", m_slideshowZoomAmount, 0.0f, 20.0f);
821 XMLUtils::GetFloat(pElement, "blackbarcompensation", m_slideshowBlackBarCompensation, 0.0f, 50.0f);
822 }
823
824 pElement = pRootElement->FirstChildElement("network");
825 if (pElement)
826 {
827 XMLUtils::GetInt(pElement, "curlclienttimeout", m_curlconnecttimeout, 1, 1000);
828 XMLUtils::GetInt(pElement, "curllowspeedtime", m_curllowspeedtime, 1, 1000);
829 XMLUtils::GetInt(pElement, "curlretries", m_curlretries, 0, 10);
830 XMLUtils::GetInt(pElement, "curlkeepaliveinterval", m_curlKeepAliveInterval, 0, 300);
831 XMLUtils::GetBoolean(pElement, "disableipv6", m_curlDisableIPV6);
832 XMLUtils::GetBoolean(pElement, "disablehttp2", m_curlDisableHTTP2);
833 XMLUtils::GetString(pElement, "catrustfile", m_caTrustFile);
834 }
835
836 pElement = pRootElement->FirstChildElement("cache");
837 if (pElement)
838 {
839 XMLUtils::GetUInt(pElement, "memorysize", m_cacheMemSize);
840 XMLUtils::GetUInt(pElement, "buffermode", m_cacheBufferMode, 0, 4);
841 XMLUtils::GetUInt(pElement, "chunksize", m_cacheChunkSize, 256, 1024 * 1024);
842 XMLUtils::GetFloat(pElement, "readfactor", m_cacheReadFactor);
843 }
844
845 pElement = pRootElement->FirstChildElement("jsonrpc");
846 if (pElement)
847 {
848 XMLUtils::GetBoolean(pElement, "compactoutput", m_jsonOutputCompact);
849 XMLUtils::GetUInt(pElement, "tcpport", m_jsonTcpPort);
850 }
851
852 pElement = pRootElement->FirstChildElement("samba");
853 if (pElement)
854 {
855 XMLUtils::GetString(pElement, "doscodepage", m_sambadoscodepage);
856 XMLUtils::GetInt(pElement, "clienttimeout", m_sambaclienttimeout, 5, 100);
857 XMLUtils::GetBoolean(pElement, "statfiles", m_sambastatfiles);
858 }
859
860 pElement = pRootElement->FirstChildElement("httpdirectory");
861 if (pElement)
862 XMLUtils::GetBoolean(pElement, "statfilesize", m_bHTTPDirectoryStatFilesize);
863
864 pElement = pRootElement->FirstChildElement("ftp");
865 if (pElement)
866 {
867 XMLUtils::GetBoolean(pElement, "remotethumbs", m_bFTPThumbs);
868 }
869
870 pElement = pRootElement->FirstChildElement("loglevel");
871 if (pElement)
872 { // read the loglevel setting, so set the setting advanced to hide it in GUI
873 // as altering it will do nothing - we don't write to advancedsettings.xml
874 XMLUtils::GetInt(pRootElement, "loglevel", m_logLevelHint, LOG_LEVEL_NONE, LOG_LEVEL_MAX);
875 const char* hide = pElement->Attribute("hide");
876 if (hide == NULL || StringUtils::CompareNoCase("false", hide, 5) != 0)
877 {
878 SettingPtr setting = CServiceBroker::GetSettingsComponent()->GetSettings()->GetSetting(CSettings::SETTING_DEBUG_SHOWLOGINFO);
879 if (setting != NULL)
880 setting->SetVisible(false);
881 }
882 m_logLevel = std::max(m_logLevel, m_logLevelHint);
883 CServiceBroker::GetLogging().SetLogLevel(m_logLevel);
884 }
885
886 XMLUtils::GetString(pRootElement, "cddbaddress", m_cddbAddress);
887 XMLUtils::GetBoolean(pRootElement, "addsourceontop", m_addSourceOnTop);
888
889 //airtunes + airplay
890 XMLUtils::GetInt(pRootElement, "airtunesport", m_airTunesPort);
891 XMLUtils::GetInt(pRootElement, "airplayport", m_airPlayPort);
892
893 XMLUtils::GetBoolean(pRootElement, "handlemounting", m_handleMounting);
894
895 #if defined(HAS_SDL) || defined(TARGET_WINDOWS)
896 XMLUtils::GetBoolean(pRootElement, "fullscreen", m_startFullScreen);
897 #endif
898 XMLUtils::GetBoolean(pRootElement, "splash", m_splashImage);
899 XMLUtils::GetBoolean(pRootElement, "showexitbutton", m_showExitButton);
900 XMLUtils::GetBoolean(pRootElement, "canwindowed", m_canWindowed);
901
902 XMLUtils::GetInt(pRootElement, "songinfoduration", m_songInfoDuration, 0, INT_MAX);
903 XMLUtils::GetInt(pRootElement, "playlistretries", m_playlistRetries, -1, 5000);
904 XMLUtils::GetInt(pRootElement, "playlisttimeout", m_playlistTimeout, 0, 5000);
905
906 XMLUtils::GetBoolean(pRootElement,"glrectanglehack", m_GLRectangleHack);
907 XMLUtils::GetInt(pRootElement,"skiploopfilter", m_iSkipLoopFilter, -16, 48);
908
909 XMLUtils::GetBoolean(pRootElement,"virtualshares", m_bVirtualShares);
910 XMLUtils::GetUInt(pRootElement, "packagefoldersize", m_addonPackageFolderSize);
911 XMLUtils::GetBoolean(pRootElement, "try10bitoutput", m_bTry10bitOutput);
912
913 // EPG
914 pElement = pRootElement->FirstChildElement("epg");
915 if (pElement)
916 {
917 XMLUtils::GetInt(pElement, "updatecheckinterval", m_iEpgUpdateCheckInterval);
918 XMLUtils::GetInt(pElement, "cleanupinterval", m_iEpgCleanupInterval);
919 XMLUtils::GetInt(pElement, "activetagcheckinterval", m_iEpgActiveTagCheckInterval);
920 XMLUtils::GetInt(pElement, "retryinterruptedupdateinterval", m_iEpgRetryInterruptedUpdateInterval);
921 XMLUtils::GetInt(pElement, "updateemptytagsinterval", m_iEpgUpdateEmptyTagsInterval);
922 XMLUtils::GetBoolean(pElement, "displayupdatepopup", m_bEpgDisplayUpdatePopup);
923 XMLUtils::GetBoolean(pElement, "displayincrementalupdatepopup", m_bEpgDisplayIncrementalUpdatePopup);
924 }
925
926 // EDL commercial break handling
927 pElement = pRootElement->FirstChildElement("edl");
928 if (pElement)
929 {
930 XMLUtils::GetBoolean(pElement, "mergeshortcommbreaks", m_bEdlMergeShortCommBreaks);
931 XMLUtils::GetInt(pElement, "maxcommbreaklength", m_iEdlMaxCommBreakLength, 0, 10 * 60); // Between 0 and 10 minutes
932 XMLUtils::GetInt(pElement, "mincommbreaklength", m_iEdlMinCommBreakLength, 0, 5 * 60); // Between 0 and 5 minutes
933 XMLUtils::GetInt(pElement, "maxcommbreakgap", m_iEdlMaxCommBreakGap, 0, 5 * 60); // Between 0 and 5 minutes.
934 XMLUtils::GetInt(pElement, "maxstartgap", m_iEdlMaxStartGap, 0, 10 * 60); // Between 0 and 10 minutes
935 XMLUtils::GetInt(pElement, "commbreakautowait", m_iEdlCommBreakAutowait, -60, 60); // Between -60 and 60 seconds
936 XMLUtils::GetInt(pElement, "commbreakautowind", m_iEdlCommBreakAutowind, -60, 60); // Between -60 and 60 seconds
937 }
938
939 // picture exclude regexps
940 TiXmlElement* pPictureExcludes = pRootElement->FirstChildElement("pictureexcludes");
941 if (pPictureExcludes)
942 GetCustomRegexps(pPictureExcludes, m_pictureExcludeFromListingRegExps);
943
944 // picture extensions
945 TiXmlElement* pExts = pRootElement->FirstChildElement("pictureextensions");
946 if (pExts)
947 GetCustomExtensions(pExts, m_pictureExtensions);
948
949 // music extensions
950 pExts = pRootElement->FirstChildElement("musicextensions");
951 if (pExts)
952 GetCustomExtensions(pExts, m_musicExtensions);
953
954 // video extensions
955 pExts = pRootElement->FirstChildElement("videoextensions");
956 if (pExts)
957 GetCustomExtensions(pExts, m_videoExtensions);
958
959 // stub extensions
960 pExts = pRootElement->FirstChildElement("discstubextensions");
961 if (pExts)
962 GetCustomExtensions(pExts, m_discStubExtensions);
963
964 m_vecTokens.clear();
965 CLangInfo::LoadTokens(pRootElement->FirstChild("sorttokens"),m_vecTokens);
966
967 //! @todo Should cache path be given in terms of our predefined paths??
968 //! Are we even going to have predefined paths??
969 std::string tmp;
970 if (XMLUtils::GetPath(pRootElement, "cachepath", tmp))
971 m_cachePath = tmp;
972 URIUtils::AddSlashAtEnd(m_cachePath);
973
974 g_LangCodeExpander.LoadUserCodes(pRootElement->FirstChildElement("languagecodes"));
975
976 // trailer matching regexps
977 TiXmlElement* pTrailerMatching = pRootElement->FirstChildElement("trailermatching");
978 if (pTrailerMatching)
979 GetCustomRegexps(pTrailerMatching, m_trailerMatchRegExps);
980
981 //everything thats a trailer is not a movie
982 m_moviesExcludeFromScanRegExps.insert(m_moviesExcludeFromScanRegExps.end(),
983 m_trailerMatchRegExps.begin(),
984 m_trailerMatchRegExps.end());
985
986 // video stacking regexps
987 TiXmlElement* pVideoStacking = pRootElement->FirstChildElement("moviestacking");
988 if (pVideoStacking)
989 GetCustomRegexps(pVideoStacking, m_videoStackRegExps);
990
991 // folder stacking regexps
992 TiXmlElement* pFolderStacking = pRootElement->FirstChildElement("folderstacking");
993 if (pFolderStacking)
994 GetCustomRegexps(pFolderStacking, m_folderStackRegExps);
995
996 //tv stacking regexps
997 TiXmlElement* pTVStacking = pRootElement->FirstChildElement("tvshowmatching");
998 if (pTVStacking)
999 GetCustomTVRegexps(pTVStacking, m_tvshowEnumRegExps);
1000
1001 //tv multipart enumeration regexp
1002 XMLUtils::GetString(pRootElement, "tvmultipartmatching", m_tvshowMultiPartEnumRegExp);
1003
1004 // path substitutions
1005 TiXmlElement* pPathSubstitution = pRootElement->FirstChildElement("pathsubstitution");
1006 if (pPathSubstitution)
1007 {
1008 m_pathSubstitutions.clear();
1009 CLog::Log(LOGDEBUG,"Configuring path substitutions");
1010 TiXmlNode* pSubstitute = pPathSubstitution->FirstChildElement("substitute");
1011 while (pSubstitute)
1012 {
1013 std::string strFrom, strTo;
1014 TiXmlNode* pFrom = pSubstitute->FirstChild("from");
1015 if (pFrom && !pFrom->NoChildren())
1016 strFrom = CSpecialProtocol::TranslatePath(pFrom->FirstChild()->Value()).c_str();
1017 TiXmlNode* pTo = pSubstitute->FirstChild("to");
1018 if (pTo && !pTo->NoChildren())
1019 strTo = pTo->FirstChild()->Value();
1020
1021 if (!strFrom.empty() && !strTo.empty())
1022 {
1023 CLog::Log(LOGDEBUG," Registering substitution pair:");
1024 CLog::Log(LOGDEBUG, " From: [{}]", CURL::GetRedacted(strFrom));
1025 CLog::Log(LOGDEBUG, " To: [{}]", CURL::GetRedacted(strTo));
1026 m_pathSubstitutions.push_back(std::make_pair(strFrom,strTo));
1027 }
1028 else
1029 {
1030 // error message about missing tag
1031 if (strFrom.empty())
1032 CLog::Log(LOGERROR," Missing <from> tag");
1033 else
1034 CLog::Log(LOGERROR," Missing <to> tag");
1035 }
1036
1037 // get next one
1038 pSubstitute = pSubstitute->NextSiblingElement("substitute");
1039 }
1040 }
1041
1042 XMLUtils::GetInt(pRootElement, "remotedelay", m_remoteDelay, 0, 20);
1043 XMLUtils::GetBoolean(pRootElement, "scanirserver", m_bScanIRServer);
1044
1045 XMLUtils::GetUInt(pRootElement, "fanartres", m_fanartRes, 0, 9999);
1046 XMLUtils::GetUInt(pRootElement, "imageres", m_imageRes, 0, 9999);
1047 if (XMLUtils::GetString(pRootElement, "imagescalingalgorithm", tmp))
1048 m_imageScalingAlgorithm = CPictureScalingAlgorithm::FromString(tmp);
1049 XMLUtils::GetBoolean(pRootElement, "playlistasfolders", m_playlistAsFolders);
1050 XMLUtils::GetBoolean(pRootElement, "uselocalecollation", m_useLocaleCollation);
1051 XMLUtils::GetBoolean(pRootElement, "detectasudf", m_detectAsUdf);
1052
1053 // music thumbs
1054 TiXmlElement* pThumbs = pRootElement->FirstChildElement("musicthumbs");
1055 if (pThumbs)
1056 GetCustomExtensions(pThumbs,m_musicThumbs);
1057
1058 // show art for shoutcast v2 streams (set to false for devices with limited storage)
1059 XMLUtils::GetBoolean(pRootElement, "shoutcastart", m_bShoutcastArt);
1060 // music filename->tag filters
1061 TiXmlElement* filters = pRootElement->FirstChildElement("musicfilenamefilters");
1062 if (filters)
1063 {
1064 TiXmlNode* filter = filters->FirstChild("filter");
1065 while (filter)
1066 {
1067 if (filter->FirstChild())
1068 m_musicTagsFromFileFilters.push_back(filter->FirstChild()->ValueStr());
1069 filter = filter->NextSibling("filter");
1070 }
1071 }
1072
1073 TiXmlElement* pHostEntries = pRootElement->FirstChildElement("hosts");
1074 if (pHostEntries)
1075 {
1076 TiXmlElement* element = pHostEntries->FirstChildElement("entry");
1077 while(element)
1078 {
1079 if(!element->NoChildren())
1080 {
1081 std::string name = XMLUtils::GetAttribute(element, "name");
1082 std::string value = element->FirstChild()->ValueStr();
1083 if (!name.empty())
1084 CDNSNameCache::Add(name, value);
1085 }
1086 element = element->NextSiblingElement("entry");
1087 }
1088 }
1089
1090 XMLUtils::GetString(pRootElement, "cputempcommand", m_cpuTempCmd);
1091 XMLUtils::GetString(pRootElement, "gputempcommand", m_gpuTempCmd);
1092
1093 XMLUtils::GetBoolean(pRootElement, "alwaysontop", m_alwaysOnTop);
1094
1095 TiXmlElement *pPVR = pRootElement->FirstChildElement("pvr");
1096 if (pPVR)
1097 {
1098 XMLUtils::GetInt(pPVR, "timecorrection", m_iPVRTimeCorrection, 0, 1440);
1099 XMLUtils::GetInt(pPVR, "infotoggleinterval", m_iPVRInfoToggleInterval, 0, 30000);
1100 XMLUtils::GetBoolean(pPVR, "channeliconsautoscan", m_bPVRChannelIconsAutoScan);
1101 XMLUtils::GetBoolean(pPVR, "autoscaniconsuserset", m_bPVRAutoScanIconsUserSet);
1102 XMLUtils::GetInt(pPVR, "numericchannelswitchtimeout", m_iPVRNumericChannelSwitchTimeout, 50, 60000);
1103 XMLUtils::GetInt(pPVR, "timeshiftthreshold", m_iPVRTimeshiftThreshold, 0, 60);
1104 XMLUtils::GetBoolean(pPVR, "timeshiftsimpleosd", m_bPVRTimeshiftSimpleOSD);
1105 TiXmlElement* pSortDecription = pPVR->FirstChildElement("pvrrecordings");
1106 if (pSortDecription)
1107 {
1108 const char* XML_SORTMETHOD = "sortmethod";
1109 const char* XML_SORTORDER = "sortorder";
1110 int sortMethod;
1111 // ignore SortByTime for duration defaults
1112 if (XMLUtils::GetInt(pSortDecription, XML_SORTMETHOD, sortMethod, SortByLabel, SortByFile))
1113 {
1114 int sortOrder;
1115 if (XMLUtils::GetInt(pSortDecription, XML_SORTORDER, sortOrder, SortOrderAscending,
1116 SortOrderDescending))
1117 {
1118 m_PVRDefaultSortOrder.sortBy = (SortBy)sortMethod;
1119 m_PVRDefaultSortOrder.sortOrder = (SortOrder)sortOrder;
1120 }
1121 }
1122 }
1123 }
1124
1125 TiXmlElement* pDatabase = pRootElement->FirstChildElement("videodatabase");
1126 if (pDatabase)
1127 {
1128 CLog::Log(LOGWARNING, "VIDEO database configuration is experimental.");
1129 XMLUtils::GetString(pDatabase, "type", m_databaseVideo.type);
1130 XMLUtils::GetString(pDatabase, "host", m_databaseVideo.host);
1131 XMLUtils::GetString(pDatabase, "port", m_databaseVideo.port);
1132 XMLUtils::GetString(pDatabase, "user", m_databaseVideo.user);
1133 XMLUtils::GetString(pDatabase, "pass", m_databaseVideo.pass);
1134 XMLUtils::GetString(pDatabase, "name", m_databaseVideo.name);
1135 XMLUtils::GetString(pDatabase, "key", m_databaseVideo.key);
1136 XMLUtils::GetString(pDatabase, "cert", m_databaseVideo.cert);
1137 XMLUtils::GetString(pDatabase, "ca", m_databaseVideo.ca);
1138 XMLUtils::GetString(pDatabase, "capath", m_databaseVideo.capath);
1139 XMLUtils::GetString(pDatabase, "ciphers", m_databaseVideo.ciphers);
1140 XMLUtils::GetBoolean(pDatabase, "compression", m_databaseVideo.compression);
1141 }
1142
1143 pDatabase = pRootElement->FirstChildElement("musicdatabase");
1144 if (pDatabase)
1145 {
1146 XMLUtils::GetString(pDatabase, "type", m_databaseMusic.type);
1147 XMLUtils::GetString(pDatabase, "host", m_databaseMusic.host);
1148 XMLUtils::GetString(pDatabase, "port", m_databaseMusic.port);
1149 XMLUtils::GetString(pDatabase, "user", m_databaseMusic.user);
1150 XMLUtils::GetString(pDatabase, "pass", m_databaseMusic.pass);
1151 XMLUtils::GetString(pDatabase, "name", m_databaseMusic.name);
1152 XMLUtils::GetString(pDatabase, "key", m_databaseMusic.key);
1153 XMLUtils::GetString(pDatabase, "cert", m_databaseMusic.cert);
1154 XMLUtils::GetString(pDatabase, "ca", m_databaseMusic.ca);
1155 XMLUtils::GetString(pDatabase, "capath", m_databaseMusic.capath);
1156 XMLUtils::GetString(pDatabase, "ciphers", m_databaseMusic.ciphers);
1157 XMLUtils::GetBoolean(pDatabase, "compression", m_databaseMusic.compression);
1158 }
1159
1160 pDatabase = pRootElement->FirstChildElement("tvdatabase");
1161 if (pDatabase)
1162 {
1163 XMLUtils::GetString(pDatabase, "type", m_databaseTV.type);
1164 XMLUtils::GetString(pDatabase, "host", m_databaseTV.host);
1165 XMLUtils::GetString(pDatabase, "port", m_databaseTV.port);
1166 XMLUtils::GetString(pDatabase, "user", m_databaseTV.user);
1167 XMLUtils::GetString(pDatabase, "pass", m_databaseTV.pass);
1168 XMLUtils::GetString(pDatabase, "name", m_databaseTV.name);
1169 XMLUtils::GetString(pDatabase, "key", m_databaseTV.key);
1170 XMLUtils::GetString(pDatabase, "cert", m_databaseTV.cert);
1171 XMLUtils::GetString(pDatabase, "ca", m_databaseTV.ca);
1172 XMLUtils::GetString(pDatabase, "capath", m_databaseTV.capath);
1173 XMLUtils::GetString(pDatabase, "ciphers", m_databaseTV.ciphers);
1174 XMLUtils::GetBoolean(pDatabase, "compression", m_databaseTV.compression);
1175 }
1176
1177 pDatabase = pRootElement->FirstChildElement("epgdatabase");
1178 if (pDatabase)
1179 {
1180 XMLUtils::GetString(pDatabase, "type", m_databaseEpg.type);
1181 XMLUtils::GetString(pDatabase, "host", m_databaseEpg.host);
1182 XMLUtils::GetString(pDatabase, "port", m_databaseEpg.port);
1183 XMLUtils::GetString(pDatabase, "user", m_databaseEpg.user);
1184 XMLUtils::GetString(pDatabase, "pass", m_databaseEpg.pass);
1185 XMLUtils::GetString(pDatabase, "name", m_databaseEpg.name);
1186 XMLUtils::GetString(pDatabase, "key", m_databaseEpg.key);
1187 XMLUtils::GetString(pDatabase, "cert", m_databaseEpg.cert);
1188 XMLUtils::GetString(pDatabase, "ca", m_databaseEpg.ca);
1189 XMLUtils::GetString(pDatabase, "capath", m_databaseEpg.capath);
1190 XMLUtils::GetString(pDatabase, "ciphers", m_databaseEpg.ciphers);
1191 XMLUtils::GetBoolean(pDatabase, "compression", m_databaseEpg.compression);
1192 }
1193
1194 pElement = pRootElement->FirstChildElement("enablemultimediakeys");
1195 if (pElement)
1196 {
1197 XMLUtils::GetBoolean(pRootElement, "enablemultimediakeys", m_enableMultimediaKeys);
1198 }
1199
1200 pElement = pRootElement->FirstChildElement("gui");
1201 if (pElement)
1202 {
1203 XMLUtils::GetBoolean(pElement, "visualizedirtyregions", m_guiVisualizeDirtyRegions);
1204 XMLUtils::GetInt(pElement, "algorithmdirtyregions", m_guiAlgorithmDirtyRegions);
1205 XMLUtils::GetBoolean(pElement, "smartredraw", m_guiSmartRedraw);
1206 }
1207
1208 std::string seekSteps;
1209 XMLUtils::GetString(pRootElement, "seeksteps", seekSteps);
1210 if (!seekSteps.empty())
1211 {
1212 m_seekSteps.clear();
1213 std::vector<std::string> steps = StringUtils::Split(seekSteps, ',');
1214 for(std::vector<std::string>::iterator it = steps.begin(); it != steps.end(); ++it)
1215 m_seekSteps.push_back(atoi((*it).c_str()));
1216 }
1217
1218 XMLUtils::GetBoolean(pRootElement, "opengldebugging", m_openGlDebugging);
1219
1220 // load in the settings overrides
1221 CServiceBroker::GetSettingsComponent()->GetSettings()->LoadHidden(pRootElement);
1222
1223 // Migration of old style art options from advanced setting to GUI setting
1224 MigrateOldArtSettings();
1225 }
1226
Clear()1227 void CAdvancedSettings::Clear()
1228 {
1229 m_videoCleanStringRegExps.clear();
1230 m_moviesExcludeFromScanRegExps.clear();
1231 m_tvshowExcludeFromScanRegExps.clear();
1232 m_videoExcludeFromListingRegExps.clear();
1233 m_videoStackRegExps.clear();
1234 m_folderStackRegExps.clear();
1235 m_allExcludeFromScanRegExps.clear();
1236 m_audioExcludeFromScanRegExps.clear();
1237 m_audioExcludeFromListingRegExps.clear();
1238 m_pictureExcludeFromListingRegExps.clear();
1239
1240 m_pictureExtensions.clear();
1241 m_musicExtensions.clear();
1242 m_videoExtensions.clear();
1243 m_discStubExtensions.clear();
1244
1245 m_userAgent.clear();
1246 }
1247
GetCustomTVRegexps(TiXmlElement * pRootElement,SETTINGS_TVSHOWLIST & settings)1248 void CAdvancedSettings::GetCustomTVRegexps(TiXmlElement *pRootElement, SETTINGS_TVSHOWLIST& settings)
1249 {
1250 TiXmlElement *pElement = pRootElement;
1251 while (pElement)
1252 {
1253 int iAction = 0; // overwrite
1254 // for backward compatibility
1255 const char* szAppend = pElement->Attribute("append");
1256 if ((szAppend && StringUtils::CompareNoCase(szAppend, "yes") == 0))
1257 iAction = 1;
1258 // action takes precedence if both attributes exist
1259 const char* szAction = pElement->Attribute("action");
1260 if (szAction)
1261 {
1262 iAction = 0; // overwrite
1263 if (StringUtils::CompareNoCase(szAction, "append") == 0)
1264 iAction = 1; // append
1265 else if (StringUtils::CompareNoCase(szAction, "prepend") == 0)
1266 iAction = 2; // prepend
1267 }
1268 if (iAction == 0)
1269 settings.clear();
1270 TiXmlNode* pRegExp = pElement->FirstChild("regexp");
1271 int i = 0;
1272 while (pRegExp)
1273 {
1274 if (pRegExp->FirstChild())
1275 {
1276 bool bByDate = false;
1277 int iDefaultSeason = 1;
1278 if (pRegExp->ToElement())
1279 {
1280 std::string byDate = XMLUtils::GetAttribute(pRegExp->ToElement(), "bydate");
1281 if (byDate == "true")
1282 {
1283 bByDate = true;
1284 }
1285 std::string defaultSeason = XMLUtils::GetAttribute(pRegExp->ToElement(), "defaultseason");
1286 if(!defaultSeason.empty())
1287 {
1288 iDefaultSeason = atoi(defaultSeason.c_str());
1289 }
1290 }
1291 std::string regExp = pRegExp->FirstChild()->Value();
1292 if (iAction == 2)
1293 settings.insert(settings.begin() + i++, 1, TVShowRegexp(bByDate,regExp,iDefaultSeason));
1294 else
1295 settings.push_back(TVShowRegexp(bByDate,regExp,iDefaultSeason));
1296 }
1297 pRegExp = pRegExp->NextSibling("regexp");
1298 }
1299
1300 pElement = pElement->NextSiblingElement(pRootElement->Value());
1301 }
1302 }
1303
GetCustomRegexps(TiXmlElement * pRootElement,std::vector<std::string> & settings)1304 void CAdvancedSettings::GetCustomRegexps(TiXmlElement *pRootElement, std::vector<std::string>& settings)
1305 {
1306 TiXmlElement *pElement = pRootElement;
1307 while (pElement)
1308 {
1309 int iAction = 0; // overwrite
1310 // for backward compatibility
1311 const char* szAppend = pElement->Attribute("append");
1312 if ((szAppend && StringUtils::CompareNoCase(szAppend, "yes") == 0))
1313 iAction = 1;
1314 // action takes precedence if both attributes exist
1315 const char* szAction = pElement->Attribute("action");
1316 if (szAction)
1317 {
1318 iAction = 0; // overwrite
1319 if (StringUtils::CompareNoCase(szAction, "append") == 0)
1320 iAction = 1; // append
1321 else if (StringUtils::CompareNoCase(szAction, "prepend") == 0)
1322 iAction = 2; // prepend
1323 }
1324 if (iAction == 0)
1325 settings.clear();
1326 TiXmlNode* pRegExp = pElement->FirstChild("regexp");
1327 int i = 0;
1328 while (pRegExp)
1329 {
1330 if (pRegExp->FirstChild())
1331 {
1332 std::string regExp = pRegExp->FirstChild()->Value();
1333 if (iAction == 2)
1334 settings.insert(settings.begin() + i++, 1, regExp);
1335 else
1336 settings.push_back(regExp);
1337 }
1338 pRegExp = pRegExp->NextSibling("regexp");
1339 }
1340
1341 pElement = pElement->NextSiblingElement(pRootElement->Value());
1342 }
1343 }
1344
GetCustomExtensions(TiXmlElement * pRootElement,std::string & extensions)1345 void CAdvancedSettings::GetCustomExtensions(TiXmlElement *pRootElement, std::string& extensions)
1346 {
1347 std::string extraExtensions;
1348 if (XMLUtils::GetString(pRootElement, "add", extraExtensions) && !extraExtensions.empty())
1349 extensions += "|" + extraExtensions;
1350 if (XMLUtils::GetString(pRootElement, "remove", extraExtensions) && !extraExtensions.empty())
1351 {
1352 std::vector<std::string> exts = StringUtils::Split(extraExtensions, '|');
1353 for (std::vector<std::string>::const_iterator i = exts.begin(); i != exts.end(); ++i)
1354 {
1355 size_t iPos = extensions.find(*i);
1356 if (iPos != std::string::npos)
1357 extensions.erase(iPos,i->size()+1);
1358 }
1359 }
1360 }
1361
AddSettingsFile(const std::string & filename)1362 void CAdvancedSettings::AddSettingsFile(const std::string &filename)
1363 {
1364 m_settingsFiles.push_back(filename);
1365 }
1366
GetLatencyTweak(float refreshrate)1367 float CAdvancedSettings::GetLatencyTweak(float refreshrate)
1368 {
1369 float delay = m_videoDefaultLatency;
1370 for (int i = 0; i < (int) m_videoRefreshLatency.size(); i++)
1371 {
1372 RefreshVideoLatency& videolatency = m_videoRefreshLatency[i];
1373 if (refreshrate >= videolatency.refreshmin && refreshrate <= videolatency.refreshmax)
1374 delay = videolatency.delay;
1375 }
1376
1377 return delay; // in milliseconds
1378 }
1379
SetDebugMode(bool debug)1380 void CAdvancedSettings::SetDebugMode(bool debug)
1381 {
1382 if (debug)
1383 {
1384 int level = std::max(m_logLevelHint, LOG_LEVEL_DEBUG_FREEMEM);
1385 m_logLevel = level;
1386 CServiceBroker::GetLogging().SetLogLevel(level);
1387 CLog::Log(LOGINFO, "Enabled debug logging due to GUI setting. Level %d.", level);
1388 }
1389 else
1390 {
1391 int level = std::min(m_logLevelHint, LOG_LEVEL_DEBUG/*LOG_LEVEL_NORMAL*/);
1392 CLog::Log(LOGINFO, "Disabled debug logging due to GUI setting. Level %d.", level);
1393 m_logLevel = level;
1394 CServiceBroker::GetLogging().SetLogLevel(level);
1395 }
1396 }
1397
SetExtraArtwork(const TiXmlElement * arttypes,std::vector<std::string> & artworkMap)1398 void CAdvancedSettings::SetExtraArtwork(const TiXmlElement* arttypes, std::vector<std::string>& artworkMap)
1399 {
1400 if (!arttypes)
1401 return;
1402 artworkMap.clear();
1403 const TiXmlNode* arttype = arttypes->FirstChild("arttype");
1404 while (arttype)
1405 {
1406 if (arttype->FirstChild())
1407 artworkMap.push_back(arttype->FirstChild()->ValueStr());
1408 arttype = arttype->NextSibling("arttype");
1409 }
1410 }
1411
ConvertToWhitelist(const std::vector<std::string> & oldlist,std::vector<CVariant> & whitelist)1412 void ConvertToWhitelist(const std::vector<std::string>& oldlist, std::vector<CVariant>& whitelist)
1413 {
1414 for (auto& it : oldlist)
1415 {
1416 size_t last_index = it.find_last_not_of("0123456789");
1417 std::string strFamilyType = it.substr(0, last_index + 1); // "fanart" of "fanart16"
1418 if (std::find(whitelist.begin(), whitelist.end(), strFamilyType) == whitelist.end())
1419 whitelist.emplace_back(strFamilyType);
1420 }
1421 }
1422
MigrateOldArtSettings()1423 void CAdvancedSettings::MigrateOldArtSettings()
1424 {
1425 const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings();
1426 if (!settings->GetBool(CSettings::SETTING_MUSICLIBRARY_ARTSETTINGS_UPDATED))
1427 {
1428 CLog::Log(LOGINFO, "Migrating old music library artwork settings to new GUI settings");
1429 // Convert numeric art type variants into simple art type family entry
1430 // e.g. {"banner", "fanart1", "fanart2", "fanart3"... } into { "banner", "fanart"}
1431 if (!m_musicArtistExtraArt.empty())
1432 {
1433 std::vector<CVariant> whitelist;
1434 ConvertToWhitelist(m_musicArtistExtraArt, whitelist);
1435 settings->SetList(CSettings::SETTING_MUSICLIBRARY_ARTISTART_WHITELIST, whitelist);
1436 }
1437 if (!m_musicAlbumExtraArt.empty())
1438 {
1439 std::vector<CVariant> whitelist;
1440 ConvertToWhitelist(m_musicAlbumExtraArt, whitelist);
1441 settings->SetList(CSettings::SETTING_MUSICLIBRARY_ALBUMART_WHITELIST, whitelist);
1442 }
1443
1444 // Convert value like "folder.jpg|Folder.jpg|folder.JPG|Folder.JPG|cover.jpg|Cover.jpg|
1445 // cover.jpeg|thumb.jpg|Thumb.jpg|thumb.JPG|Thumb.JPG" into case-insensitive unique elements
1446 // e.g. {"folder.jpg", "cover.jpg", "cover.jpeg", "thumb.jpg"}
1447 if (!m_musicThumbs.empty())
1448 {
1449 std::vector<std::string> thumbs1 = StringUtils::Split(m_musicThumbs, "|");
1450 std::vector<std::string> thumbs2;
1451 for (auto& it : thumbs1)
1452 {
1453 StringUtils::ToLower(it);
1454 if (std::find(thumbs2.begin(), thumbs2.end(), it) == thumbs2.end())
1455 thumbs2.emplace_back(it);
1456 }
1457 std::vector<CVariant> thumbs;
1458 thumbs.reserve(thumbs2.size());
1459 for (const auto& it : thumbs2)
1460 thumbs.emplace_back(it);
1461 settings->SetList(CSettings::SETTING_MUSICLIBRARY_MUSICTHUMBS, thumbs);
1462 }
1463
1464 // Whitelists configured, set artwork level to custom
1465 if (!m_musicAlbumExtraArt.empty() || !m_musicArtistExtraArt.empty())
1466 settings->SetInt(CSettings::SETTING_MUSICLIBRARY_ARTWORKLEVEL, 2);
1467
1468 // Flag migration of settings so not done again
1469 settings->SetBool(CSettings::SETTING_MUSICLIBRARY_ARTSETTINGS_UPDATED, true);
1470 }
1471
1472 if (!settings->GetBool(CSettings::SETTING_VIDEOLIBRARY_ARTSETTINGS_UPDATED))
1473 {
1474 CLog::Log(LOGINFO, "Migrating old video library artwork settings to new GUI settings");
1475 // Convert numeric art type variants into simple art type family entry
1476 // e.g. {"banner", "fanart1", "fanart2", "fanart3"... } into { "banner", "fanart"}
1477 if (!m_videoEpisodeExtraArt.empty())
1478 {
1479 std::vector<CVariant> whitelist;
1480 ConvertToWhitelist(m_videoEpisodeExtraArt, whitelist);
1481 settings->SetList(CSettings::SETTING_VIDEOLIBRARY_EPISODEART_WHITELIST, whitelist);
1482 }
1483 if (!m_videoTvShowExtraArt.empty())
1484 {
1485 std::vector<CVariant> whitelist;
1486 ConvertToWhitelist(m_videoTvShowExtraArt, whitelist);
1487 settings->SetList(CSettings::SETTING_VIDEOLIBRARY_TVSHOWART_WHITELIST, whitelist);
1488 }
1489 if (!m_videoMovieExtraArt.empty())
1490 {
1491 std::vector<CVariant> whitelist;
1492 ConvertToWhitelist(m_videoMovieExtraArt, whitelist);
1493 settings->SetList(CSettings::SETTING_VIDEOLIBRARY_MOVIEART_WHITELIST, whitelist);
1494 }
1495 if (!m_videoMusicVideoExtraArt.empty())
1496 {
1497 std::vector<CVariant> whitelist;
1498 ConvertToWhitelist(m_videoMusicVideoExtraArt, whitelist);
1499 settings->SetList(CSettings::SETTING_VIDEOLIBRARY_MUSICVIDEOART_WHITELIST, whitelist);
1500 }
1501
1502 // Whitelists configured, set artwork level to custom
1503 if (!m_videoEpisodeExtraArt.empty() || !m_videoTvShowExtraArt.empty()
1504 || !m_videoMovieExtraArt.empty() || !m_videoMusicVideoExtraArt.empty())
1505 settings->SetInt(CSettings::SETTING_VIDEOLIBRARY_ARTWORK_LEVEL,
1506 CSettings::MUSICLIBRARY_ARTWORK_LEVEL_CUSTOM);
1507
1508 // Flag migration of settings so not done again
1509 settings->SetBool(CSettings::SETTING_VIDEOLIBRARY_ARTSETTINGS_UPDATED, true);
1510 }
1511 }
1512