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 &params, 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