summaryrefslogtreecommitdiffstats
path: root/xbmc/settings/AdvancedSettings.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--xbmc/settings/AdvancedSettings.cpp1559
1 files changed, 1559 insertions, 0 deletions
diff --git a/xbmc/settings/AdvancedSettings.cpp b/xbmc/settings/AdvancedSettings.cpp
new file mode 100644
index 0000000..4fd93d0
--- /dev/null
+++ b/xbmc/settings/AdvancedSettings.cpp
@@ -0,0 +1,1559 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "AdvancedSettings.h"
+
+#include "LangInfo.h"
+#include "ServiceBroker.h"
+#include "URL.h"
+#include "application/AppParams.h"
+#include "filesystem/SpecialProtocol.h"
+#include "network/DNSNameCache.h"
+#include "profiles/ProfileManager.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "settings/lib/Setting.h"
+#include "settings/lib/SettingsManager.h"
+#include "utils/FileUtils.h"
+#include "utils/LangCodeExpander.h"
+#include "utils/StringUtils.h"
+#include "utils/SystemInfo.h"
+#include "utils/URIUtils.h"
+#include "utils/Variant.h"
+#include "utils/XMLUtils.h"
+#include "utils/log.h"
+
+#include <algorithm>
+#include <climits>
+#include <regex>
+#include <string>
+#include <vector>
+
+using namespace ADDON;
+
+CAdvancedSettings::CAdvancedSettings()
+{
+ m_initialized = false;
+ m_fullScreen = false;
+}
+
+void CAdvancedSettings::OnSettingsLoaded()
+{
+ const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager();
+
+ // load advanced settings
+ Load(*profileManager);
+
+ // default players?
+ CLog::Log(LOGINFO, "Default Video Player: {}", m_videoDefaultPlayer);
+ CLog::Log(LOGINFO, "Default Audio Player: {}", m_audioDefaultPlayer);
+
+ // setup any logging...
+ const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings();
+ if (settings->GetBool(CSettings::SETTING_DEBUG_SHOWLOGINFO))
+ {
+ m_logLevel = std::max(m_logLevelHint, LOG_LEVEL_DEBUG_FREEMEM);
+ CLog::Log(LOGINFO, "Enabled debug logging due to GUI setting ({})", m_logLevel);
+ }
+ else
+ {
+ m_logLevel = std::min(m_logLevelHint, LOG_LEVEL_DEBUG/*LOG_LEVEL_NORMAL*/);
+ CLog::Log(LOGINFO, "Disabled debug logging due to GUI setting. Level {}.", m_logLevel);
+ }
+ CServiceBroker::GetLogging().SetLogLevel(m_logLevel);
+}
+
+void CAdvancedSettings::OnSettingsUnloaded()
+{
+ m_initialized = false;
+}
+
+void CAdvancedSettings::OnSettingChanged(const std::shared_ptr<const CSetting>& setting)
+{
+ if (setting == NULL)
+ return;
+
+ const std::string &settingId = setting->GetId();
+ if (settingId == CSettings::SETTING_DEBUG_SHOWLOGINFO)
+ SetDebugMode(std::static_pointer_cast<const CSettingBool>(setting)->GetValue());
+}
+
+void CAdvancedSettings::Initialize(CSettingsManager& settingsMgr)
+{
+ Initialize();
+
+ const auto params = CServiceBroker::GetAppParams();
+
+ if (params->GetLogLevel() == LOG_LEVEL_DEBUG)
+ {
+ m_logLevel = LOG_LEVEL_DEBUG;
+ m_logLevelHint = LOG_LEVEL_DEBUG;
+ CServiceBroker::GetLogging().SetLogLevel(LOG_LEVEL_DEBUG);
+ }
+
+ const std::string& settingsFile = params->GetSettingsFile();
+ if (!settingsFile.empty())
+ AddSettingsFile(settingsFile);
+
+ if (params->IsStartFullScreen())
+ m_startFullScreen = true;
+
+ if (params->IsStandAlone())
+ m_handleMounting = true;
+
+ settingsMgr.RegisterSettingsHandler(this, true);
+ std::set<std::string> settingSet;
+ settingSet.insert(CSettings::SETTING_DEBUG_SHOWLOGINFO);
+ settingsMgr.RegisterCallback(this, settingSet);
+}
+
+void CAdvancedSettings::Uninitialize(CSettingsManager& settingsMgr)
+{
+ settingsMgr.UnregisterCallback(this);
+ settingsMgr.UnregisterSettingsHandler(this);
+ settingsMgr.UnregisterSettingOptionsFiller("loggingcomponents");
+
+ Clear();
+
+ m_initialized = false;
+}
+
+void CAdvancedSettings::Initialize()
+{
+ if (m_initialized)
+ return;
+
+ m_audioApplyDrc = -1.0f;
+ m_VideoPlayerIgnoreDTSinWAV = false;
+
+ //default hold time of 25 ms, this allows a 20 hertz sine to pass undistorted
+ m_limiterHold = 0.025f;
+ m_limiterRelease = 0.1f;
+
+ m_seekSteps = { 10, 30, 60, 180, 300, 600, 1800 };
+
+ m_audioDefaultPlayer = "paplayer";
+ m_audioPlayCountMinimumPercent = 90.0f;
+
+ m_videoSubsDelayRange = 60;
+ m_videoAudioDelayRange = 10;
+ m_videoUseTimeSeeking = true;
+ m_videoTimeSeekForward = 30;
+ m_videoTimeSeekBackward = -30;
+ m_videoTimeSeekForwardBig = 600;
+ m_videoTimeSeekBackwardBig = -600;
+ m_videoPercentSeekForward = 2;
+ m_videoPercentSeekBackward = -2;
+ m_videoPercentSeekForwardBig = 10;
+ m_videoPercentSeekBackwardBig = -10;
+
+ m_videoPPFFmpegPostProc = "ha:128:7,va,dr";
+ m_videoDefaultPlayer = "VideoPlayer";
+ m_videoIgnoreSecondsAtStart = 3*60;
+ m_videoIgnorePercentAtEnd = 8.0f;
+ m_videoPlayCountMinimumPercent = 90.0f;
+ m_videoVDPAUScaling = -1;
+ m_videoNonLinStretchRatio = 0.5f;
+ m_videoAutoScaleMaxFps = 30.0f;
+ m_videoCaptureUseOcclusionQuery = -1; //-1 is auto detect
+ m_videoVDPAUtelecine = false;
+ m_videoVDPAUdeintSkipChromaHD = false;
+ m_DXVACheckCompatibility = false;
+ m_DXVACheckCompatibilityPresent = false;
+ m_videoFpsDetect = 1;
+ m_maxTempo = 1.55f;
+ m_videoPreferStereoStream = false;
+
+ m_videoDefaultLatency = 0.0;
+
+ m_musicUseTimeSeeking = true;
+ m_musicTimeSeekForward = 10;
+ m_musicTimeSeekBackward = -10;
+ m_musicTimeSeekForwardBig = 60;
+ m_musicTimeSeekBackwardBig = -60;
+ m_musicPercentSeekForward = 1;
+ m_musicPercentSeekBackward = -1;
+ m_musicPercentSeekForwardBig = 10;
+ m_musicPercentSeekBackwardBig = -10;
+
+ m_slideshowPanAmount = 2.5f;
+ m_slideshowZoomAmount = 5.0f;
+ m_slideshowBlackBarCompensation = 20.0f;
+
+ m_songInfoDuration = 10;
+
+ m_cddbAddress = "gnudb.gnudb.org";
+ m_addSourceOnTop = false;
+
+ m_handleMounting = CServiceBroker::GetAppParams()->IsStandAlone();
+
+ m_fullScreenOnMovieStart = true;
+ m_cachePath = "special://temp/";
+
+ m_videoCleanDateTimeRegExp = "(.*[^ _\\,\\.\\(\\)\\[\\]\\-])[ _\\.\\(\\)\\[\\]\\-]+(19[0-9][0-9]|20[0-9][0-9])([ _\\,\\.\\(\\)\\[\\]\\-]|[^0-9]$)?";
+
+ m_videoCleanStringRegExps.clear();
+ m_videoCleanStringRegExps.emplace_back(
+ "[ "
+ "_\\,\\.\\(\\)\\[\\]\\-](10bit|480p|480i|576p|576i|720p|720i|1080p|1080i|2160p|3d|aac|ac3|"
+ "aka|atmos|avi|bd5|bdrip|bluray|brrip|cam|cd[1-9]|custom|dc|ddp|divx|divx5|dolbydigital|"
+ "dolbyvision|dsr|dsrip|dts|dts-hdma|dts-hra|dts-x|dv|dvd|dvd5|dvd9|dvdivx|dvdrip|dvdscr|"
+ "dvdscreener|extended|fragment|fs|h264|h265|hdr|hdr10|hevc|hddvd|hdrip|hdtv|hdtvrip|hrhd|"
+ "hrhdtv|internal|limited|multisubs|nfofix|ntsc|ogg|ogm|pal|pdtv|proper|r3|r5|read.nfo|"
+ "remastered|remux|repack|rerip|retail|screener|se|svcd|tc|telecine|telesync|truehd|ts|uhd|"
+ "unrated|ws|x264|x265|xvid|xvidvd|xxx|web-dl|webrip|www.www|\\[.*\\])([ "
+ "_\\,\\.\\(\\)\\[\\]\\-]|$)");
+ m_videoCleanStringRegExps.emplace_back("(\\[.*\\])");
+
+ // this vector will be inserted at the end to
+ // m_moviesExcludeFromScanRegExps, m_tvshowExcludeFromScanRegExps and
+ // m_audioExcludeFromScanRegExps
+ m_allExcludeFromScanRegExps.clear();
+ m_allExcludeFromScanRegExps.emplace_back("[\\/].+\\.ite[\\/]"); // ignore itunes extras dir
+ m_allExcludeFromScanRegExps.emplace_back("[\\/]\\.\\_");
+ m_allExcludeFromScanRegExps.emplace_back("\\.DS_Store");
+ m_allExcludeFromScanRegExps.emplace_back("\\.AppleDouble");
+
+ m_moviesExcludeFromScanRegExps.clear();
+ m_moviesExcludeFromScanRegExps.emplace_back("-trailer");
+ m_moviesExcludeFromScanRegExps.emplace_back("[!-._ \\\\/]sample[-._ \\\\/]");
+ m_moviesExcludeFromScanRegExps.emplace_back("[\\/](proof|subs)[\\/]");
+ m_moviesExcludeFromScanRegExps.insert(m_moviesExcludeFromScanRegExps.end(),
+ m_allExcludeFromScanRegExps.begin(),
+ m_allExcludeFromScanRegExps.end());
+
+
+ m_tvshowExcludeFromScanRegExps.clear();
+ m_tvshowExcludeFromScanRegExps.emplace_back("[!-._ \\\\/]sample[-._ \\\\/]");
+ m_tvshowExcludeFromScanRegExps.insert(m_tvshowExcludeFromScanRegExps.end(),
+ m_allExcludeFromScanRegExps.begin(),
+ m_allExcludeFromScanRegExps.end());
+
+
+ m_audioExcludeFromScanRegExps.clear();
+ m_audioExcludeFromScanRegExps.insert(m_audioExcludeFromScanRegExps.end(),
+ m_allExcludeFromScanRegExps.begin(),
+ m_allExcludeFromScanRegExps.end());
+
+ m_folderStackRegExps.clear();
+ m_folderStackRegExps.emplace_back("((cd|dvd|dis[ck])[0-9]+)$");
+
+ m_videoStackRegExps.clear();
+ m_videoStackRegExps.emplace_back("(.*?)([ _.-]*(?:cd|dvd|p(?:(?:ar)?t)|dis[ck])[ _.-]*[0-9]+)(.*?)(\\.[^.]+)$");
+ m_videoStackRegExps.emplace_back("(.*?)([ _.-]*(?:cd|dvd|p(?:(?:ar)?t)|dis[ck])[ _.-]*[a-d])(.*?)(\\.[^.]+)$");
+ m_videoStackRegExps.emplace_back("(.*?)([ ._-]*[a-d])(.*?)(\\.[^.]+)$");
+ // This one is a bit too greedy to enable by default. It will stack sequels
+ // in a flat dir structure, but is perfectly safe in a dir-per-vid one.
+ //m_videoStackRegExps.push_back("(.*?)([ ._-]*[0-9])(.*?)(\\.[^.]+)$");
+
+ m_tvshowEnumRegExps.clear();
+ // foo.s01.e01, foo.s01_e01, S01E02 foo, S01 - E02, S01xE02
+ m_tvshowEnumRegExps.push_back(TVShowRegexp(false,"s([0-9]+)[ ._x-]*e([0-9]+(?:(?:[a-i]|\\.[1-9])(?![0-9]))?)([^\\\\/]*)$"));
+ // foo.ep01, foo.EP_01, foo.E01
+ m_tvshowEnumRegExps.push_back(TVShowRegexp(
+ false, "[\\._ -]?()e(?:p[ ._-]?)?([0-9]+(?:(?:[a-i]|\\.[1-9])(?![0-9]))?)([^\\\\/]*)$"));
+ // foo.yyyy.mm.dd.* (byDate=true)
+ m_tvshowEnumRegExps.push_back(TVShowRegexp(true,"([0-9]{4})[\\.-]([0-9]{2})[\\.-]([0-9]{2})"));
+ // foo.mm.dd.yyyy.* (byDate=true)
+ m_tvshowEnumRegExps.push_back(TVShowRegexp(true,"([0-9]{2})[\\.-]([0-9]{2})[\\.-]([0-9]{4})"));
+ // foo.1x09* or just /1x09*
+ m_tvshowEnumRegExps.push_back(TVShowRegexp(false,"[\\\\/\\._ \\[\\(-]([0-9]+)x([0-9]+(?:(?:[a-i]|\\.[1-9])(?![0-9]))?)([^\\\\/]*)$"));
+ // Part I, Pt.VI, Part 1
+ m_tvshowEnumRegExps.push_back(TVShowRegexp(false,"[\\/._ -]p(?:ar)?t[_. -]()([ivx]+|[0-9]+)([._ -][^\\/]*)$"));
+ // This regexp is for matching special episodes by their title, e.g. foo.special.mp4
+ m_tvshowEnumRegExps.push_back(
+ TVShowRegexp(false, "[\\\\/]([^\\\\/]+)\\.special\\.[a-z0-9]+$", 0, true));
+
+ // foo.103*, 103 foo
+ // XXX: This regex is greedy and will match years in show names. It should always be last.
+ m_tvshowEnumRegExps.push_back(TVShowRegexp(false,"[\\\\/\\._ -]([0-9]+)([0-9][0-9](?:(?:[a-i]|\\.[1-9])(?![0-9]))?)([\\._ -][^\\\\/]*)$"));
+
+ m_tvshowMultiPartEnumRegExp = "^[-_ex]+([0-9]+(?:(?:[a-i]|\\.[1-9])(?![0-9]))?)";
+
+ m_remoteDelay = 3;
+ m_bScanIRServer = true;
+
+ m_playlistAsFolders = true;
+ m_detectAsUdf = false;
+
+ m_fanartRes = 1080;
+ m_imageRes = 720;
+ m_imageScalingAlgorithm = CPictureScalingAlgorithm::Default;
+ m_imageQualityJpeg = 4;
+
+ m_sambaclienttimeout = 30;
+ m_sambadoscodepage = "";
+ m_sambastatfiles = true;
+
+ m_bHTTPDirectoryStatFilesize = false;
+
+ m_bFTPThumbs = false;
+
+ m_bShoutcastArt = true;
+
+ m_musicThumbs = "folder.jpg|Folder.jpg|folder.JPG|Folder.JPG|cover.jpg|Cover.jpg|cover.jpeg|thumb.jpg|Thumb.jpg|thumb.JPG|Thumb.JPG";
+ m_musicArtistExtraArt = {};
+ m_musicAlbumExtraArt = {};
+
+ m_bMusicLibraryAllItemsOnBottom = false;
+ m_bMusicLibraryCleanOnUpdate = false;
+ m_bMusicLibraryArtistSortOnUpdate = false;
+ m_iMusicLibraryRecentlyAddedItems = 25;
+ m_strMusicLibraryAlbumFormat = "";
+ m_prioritiseAPEv2tags = false;
+ m_musicItemSeparator = " / ";
+ m_musicArtistSeparators = { ";", " feat. ", " ft. " };
+ m_videoItemSeparator = " / ";
+ m_iMusicLibraryDateAdded = 1; // prefer mtime over ctime and current time
+ m_bMusicLibraryUseISODates = false;
+
+ m_bVideoLibraryAllItemsOnBottom = false;
+ m_iVideoLibraryRecentlyAddedItems = 25;
+ m_bVideoLibraryCleanOnUpdate = false;
+ m_bVideoLibraryUseFastHash = true;
+ m_bVideoScannerIgnoreErrors = false;
+ m_iVideoLibraryDateAdded = 1; // prefer mtime over ctime and current time
+
+ m_videoEpisodeExtraArt = {};
+ m_videoTvShowExtraArt = {};
+ m_videoTvSeasonExtraArt = {};
+ m_videoMovieExtraArt = {};
+ m_videoMovieSetExtraArt = {};
+ m_videoMusicVideoExtraArt = {};
+
+ m_iEpgUpdateCheckInterval = 300; /* Check every X seconds, if EPG data need to be updated. This does not mean that
+ every X seconds an EPG update is actually triggered, it's just the interval how
+ often to check whether an update should be triggered. If this value is greater
+ than GUI setting 'epg.epgupdate' value, then EPG updates will done with the value
+ specified for 'updatecheckinterval', effectively overriding the GUI setting's value. */
+ m_iEpgCleanupInterval = 900; /* Remove old entries from the EPG every X seconds */
+ m_iEpgActiveTagCheckInterval = 60; /* Check for updated active tags every X seconds */
+ m_iEpgRetryInterruptedUpdateInterval = 30; /* Retry an interrupted EPG update after X seconds */
+ m_iEpgUpdateEmptyTagsInterval = 7200; /* If a TV channel has no EPG data, try to obtain data for that channel every
+ X seconds. This overrides the GUI setting 'epg.epgupdate' value, but only
+ for channels without EPG data. If this value is less than 'updatecheckinterval'
+ value, then data update will be done with the interval specified by
+ 'updatecheckinterval'.
+ Example 1: epg.epgupdate = 120 (minutes!), updatecheckinterval = 300,
+ updateemptytagsinterval = 60 => trigger an EPG update for every
+ channel without EPG data every 5 minutes and trigger an EPG update
+ for every channel with EPG data every 2 hours.
+ Example 2: epg.epgupdate = 120 (minutes!), updatecheckinterval = 300,
+ updateemptytagsinterval = 3600 => trigger an EPG update for every
+ channel without EPG data every 2 hours and trigger an EPG update
+ for every channel with EPG data every 1 hour. */
+ m_bEpgDisplayUpdatePopup = true; /* Display a progress popup while updating EPG data from clients */
+ m_bEpgDisplayIncrementalUpdatePopup = false; /* Display a progress popup while doing incremental EPG updates, but
+ only if 'displayupdatepopup' is also enabled. */
+
+ m_bEdlMergeShortCommBreaks = false; // Off by default
+ m_EdlDisplayCommbreakNotifications = true; // On by default
+ m_iEdlMaxCommBreakLength = 8 * 30 + 10; // Just over 8 * 30 second commercial break.
+ m_iEdlMinCommBreakLength = 3 * 30; // 3 * 30 second commercial breaks.
+ m_iEdlMaxCommBreakGap = 4 * 30; // 4 * 30 second commercial breaks.
+ m_iEdlMaxStartGap = 5 * 60; // 5 minutes.
+ m_iEdlCommBreakAutowait = 0; // Off by default
+ m_iEdlCommBreakAutowind = 0; // Off by default
+
+ m_curlconnecttimeout = 30;
+ m_curllowspeedtime = 20;
+ m_curlretries = 2;
+ m_curlKeepAliveInterval = 30;
+ m_curlDisableIPV6 = false; //Certain hardware/OS combinations have trouble
+ //with ipv6.
+ m_curlDisableHTTP2 = false;
+
+#if defined(TARGET_WINDOWS_DESKTOP)
+ m_minimizeToTray = false;
+#endif
+#if defined(TARGET_DARWIN_EMBEDDED)
+ m_startFullScreen = true;
+#else
+ m_startFullScreen = false;
+#endif
+ m_showExitButton = true;
+ m_splashImage = true;
+
+ m_playlistRetries = 100;
+ m_playlistTimeout = 20; // 20 seconds timeout
+ m_GLRectangleHack = false;
+ m_iSkipLoopFilter = 0;
+ m_bVirtualShares = true;
+
+ m_cpuTempCmd = "";
+ m_gpuTempCmd = "";
+#if defined(TARGET_DARWIN)
+ // default for osx is fullscreen always on top
+ m_alwaysOnTop = true;
+#else
+ // default for windows is not always on top
+ m_alwaysOnTop = false;
+#endif
+
+ m_iPVRTimeCorrection = 0;
+ m_iPVRInfoToggleInterval = 5000;
+ m_bPVRChannelIconsAutoScan = true;
+ m_bPVRAutoScanIconsUserSet = false;
+ m_iPVRNumericChannelSwitchTimeout = 2000;
+ m_iPVRTimeshiftThreshold = 10;
+ m_bPVRTimeshiftSimpleOSD = true;
+ m_PVRDefaultSortOrder.sortBy = SortByDate;
+ m_PVRDefaultSortOrder.sortOrder = SortOrderDescending;
+
+ m_cacheMemSize = 1024 * 1024 * 20; // 20 MiB
+ m_cacheBufferMode = CACHE_BUFFER_MODE_NETWORK; // Default (buffer all network filesystems)
+ m_cacheChunkSize = 128 * 1024; // 128 KiB
+
+ // the following setting determines the readRate of a player data
+ // as multiply of the default data read rate
+ m_cacheReadFactor = 4.0f;
+
+ m_addonPackageFolderSize = 200;
+
+ m_jsonOutputCompact = true;
+ m_jsonTcpPort = 9090;
+
+ m_enableMultimediaKeys = false;
+
+ m_canWindowed = true;
+ m_guiVisualizeDirtyRegions = false;
+ m_guiAlgorithmDirtyRegions = 3;
+ m_guiSmartRedraw = false;
+ m_airTunesPort = 36666;
+ m_airPlayPort = 36667;
+
+ m_databaseMusic.Reset();
+ m_databaseVideo.Reset();
+
+ m_useLocaleCollation = true;
+
+ m_pictureExtensions = ".png|.jpg|.jpeg|.bmp|.gif|.ico|.tif|.tiff|.tga|.pcx|.cbz|.zip|.rss|.webp|.jp2|.apng";
+ 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";
+ 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";
+ m_subtitlesExtensions = ".utf|.utf8|.utf-8|.sub|.srt|.smi|.rt|.txt|.ssa|.text|.ssa|.aqt|.jss|"
+ ".ass|.vtt|.idx|.ifo|.zip|.sup";
+ m_discStubExtensions = ".disc";
+ // internal music extensions
+ m_musicExtensions += "|.cdda";
+ // internal video extensions
+ m_videoExtensions += "|.pvr";
+
+ m_stereoscopicregex_3d = "[-. _]3d[-. _]";
+ m_stereoscopicregex_sbs = "[-. _]h?sbs[-. _]";
+ m_stereoscopicregex_tab = "[-. _]h?tab[-. _]";
+
+ m_logLevelHint = m_logLevel = LOG_LEVEL_NORMAL;
+
+ m_openGlDebugging = false;
+
+ m_userAgent = g_sysinfo.GetUserAgent();
+
+ m_nfsTimeout = 30;
+ m_nfsRetries = -1;
+
+ m_initialized = true;
+}
+
+bool CAdvancedSettings::Load(const CProfileManager &profileManager)
+{
+ // NOTE: This routine should NOT set the default of any of these parameters
+ // it should instead use the versions of GetString/Integer/Float that
+ // don't take defaults in. Defaults are set in the constructor above
+ Initialize(); // In case of profile switch.
+ ParseSettingsFile("special://xbmc/system/advancedsettings.xml");
+ for (unsigned int i = 0; i < m_settingsFiles.size(); i++)
+ ParseSettingsFile(m_settingsFiles[i]);
+
+ ParseSettingsFile(profileManager.GetUserDataItem("advancedsettings.xml"));
+
+ // Add the list of disc stub extensions (if any) to the list of video extensions
+ if (!m_discStubExtensions.empty())
+ m_videoExtensions += "|" + m_discStubExtensions;
+
+ return true;
+}
+
+void CAdvancedSettings::ParseSettingsFile(const std::string &file)
+{
+ CXBMCTinyXML advancedXML;
+ if (!CFileUtils::Exists(file))
+ {
+ CLog::Log(LOGINFO, "No settings file to load ({})", file);
+ return;
+ }
+
+ if (!advancedXML.LoadFile(file))
+ {
+ CLog::Log(LOGERROR, "Error loading {}, Line {}\n{}", file, advancedXML.ErrorRow(),
+ advancedXML.ErrorDesc());
+ return;
+ }
+
+ TiXmlElement *pRootElement = advancedXML.RootElement();
+ if (!pRootElement || StringUtils::CompareNoCase(pRootElement->Value(), "advancedsettings") != 0)
+ {
+ CLog::Log(LOGERROR, "Error loading {}, no <advancedsettings> node", file);
+ return;
+ }
+
+ // succeeded - tell the user it worked
+ CLog::Log(LOGINFO, "Loaded settings file from {}", file);
+
+ //Make a copy of the AS.xml and hide advancedsettings passwords
+ CXBMCTinyXML advancedXMLCopy(advancedXML);
+ TiXmlNode *pRootElementCopy = advancedXMLCopy.RootElement();
+ for (const auto& dbname : { "videodatabase", "musicdatabase", "tvdatabase", "epgdatabase" })
+ {
+ TiXmlNode *db = pRootElementCopy->FirstChild(dbname);
+ if (db)
+ {
+ TiXmlNode *passTag = db->FirstChild("pass");
+ if (passTag)
+ {
+ TiXmlNode *pass = passTag->FirstChild();
+ if (pass)
+ {
+ passTag->RemoveChild(pass);
+ passTag->LinkEndChild(new TiXmlText("*****"));
+ }
+ }
+ }
+ }
+ TiXmlNode *network = pRootElementCopy->FirstChild("network");
+ if (network)
+ {
+ TiXmlNode *passTag = network->FirstChild("httpproxypassword");
+ if (passTag)
+ {
+ TiXmlNode *pass = passTag->FirstChild();
+ if (pass)
+ {
+ passTag->RemoveChild(pass);
+ passTag->LinkEndChild(new TiXmlText("*****"));
+ }
+ }
+ if (network->FirstChildElement("nfstimeout"))
+ {
+#ifdef HAS_NFS_SET_TIMEOUT
+ XMLUtils::GetUInt(network, "nfstimeout", m_nfsTimeout, 0, 3600);
+#else
+ CLog::Log(LOGWARNING, "nfstimeout unsupported");
+#endif
+ }
+ if (network->FirstChildElement("nfsretries"))
+ {
+ XMLUtils::GetInt(network, "nfsretries", m_nfsRetries, -1, 30);
+ }
+ }
+
+ // Dump contents of copied AS.xml to debug log
+ TiXmlPrinter printer;
+ printer.SetLineBreak("\n");
+ printer.SetIndent(" ");
+ advancedXMLCopy.Accept(&printer);
+ // redact User/pass in URLs
+ std::regex redactRe("(\\w+://)\\S+:\\S+@");
+ CLog::Log(LOGINFO, "Contents of {} are...\n{}", file,
+ std::regex_replace(printer.CStr(), redactRe, "$1USERNAME:PASSWORD@"));
+
+ TiXmlElement *pElement = pRootElement->FirstChildElement("audio");
+ if (pElement)
+ {
+ XMLUtils::GetString(pElement, "defaultplayer", m_audioDefaultPlayer);
+ // 101 on purpose - can be used to never automark as watched
+ XMLUtils::GetFloat(pElement, "playcountminimumpercent", m_audioPlayCountMinimumPercent, 0.0f, 101.0f);
+
+ XMLUtils::GetBoolean(pElement, "usetimeseeking", m_musicUseTimeSeeking);
+ XMLUtils::GetInt(pElement, "timeseekforward", m_musicTimeSeekForward, 0, 6000);
+ XMLUtils::GetInt(pElement, "timeseekbackward", m_musicTimeSeekBackward, -6000, 0);
+ XMLUtils::GetInt(pElement, "timeseekforwardbig", m_musicTimeSeekForwardBig, 0, 6000);
+ XMLUtils::GetInt(pElement, "timeseekbackwardbig", m_musicTimeSeekBackwardBig, -6000, 0);
+
+ XMLUtils::GetInt(pElement, "percentseekforward", m_musicPercentSeekForward, 0, 100);
+ XMLUtils::GetInt(pElement, "percentseekbackward", m_musicPercentSeekBackward, -100, 0);
+ XMLUtils::GetInt(pElement, "percentseekforwardbig", m_musicPercentSeekForwardBig, 0, 100);
+ XMLUtils::GetInt(pElement, "percentseekbackwardbig", m_musicPercentSeekBackwardBig, -100, 0);
+
+ TiXmlElement* pAudioExcludes = pElement->FirstChildElement("excludefromlisting");
+ if (pAudioExcludes)
+ GetCustomRegexps(pAudioExcludes, m_audioExcludeFromListingRegExps);
+
+ pAudioExcludes = pElement->FirstChildElement("excludefromscan");
+ if (pAudioExcludes)
+ GetCustomRegexps(pAudioExcludes, m_audioExcludeFromScanRegExps);
+
+ XMLUtils::GetFloat(pElement, "applydrc", m_audioApplyDrc);
+ XMLUtils::GetBoolean(pElement, "VideoPlayerignoredtsinwav", m_VideoPlayerIgnoreDTSinWAV);
+
+ XMLUtils::GetFloat(pElement, "limiterhold", m_limiterHold, 0.0f, 100.0f);
+ XMLUtils::GetFloat(pElement, "limiterrelease", m_limiterRelease, 0.001f, 100.0f);
+ XMLUtils::GetUInt(pElement, "maxpassthroughoffsyncduration", m_maxPassthroughOffSyncDuration,
+ 10, 100);
+ }
+
+ pElement = pRootElement->FirstChildElement("x11");
+ if (pElement)
+ {
+ XMLUtils::GetBoolean(pElement, "omlsync", m_omlSync);
+ }
+
+ pElement = pRootElement->FirstChildElement("video");
+ if (pElement)
+ {
+ XMLUtils::GetString(pElement, "stereoscopicregex3d", m_stereoscopicregex_3d);
+ XMLUtils::GetString(pElement, "stereoscopicregexsbs", m_stereoscopicregex_sbs);
+ XMLUtils::GetString(pElement, "stereoscopicregextab", m_stereoscopicregex_tab);
+ XMLUtils::GetFloat(pElement, "subsdelayrange", m_videoSubsDelayRange, 10, 600);
+ XMLUtils::GetFloat(pElement, "audiodelayrange", m_videoAudioDelayRange, 10, 600);
+ XMLUtils::GetString(pElement, "defaultplayer", m_videoDefaultPlayer);
+ XMLUtils::GetBoolean(pElement, "fullscreenonmoviestart", m_fullScreenOnMovieStart);
+ // 101 on purpose - can be used to never automark as watched
+ XMLUtils::GetFloat(pElement, "playcountminimumpercent", m_videoPlayCountMinimumPercent, 0.0f, 101.0f);
+ XMLUtils::GetInt(pElement, "ignoresecondsatstart", m_videoIgnoreSecondsAtStart, 0, 900);
+ XMLUtils::GetFloat(pElement, "ignorepercentatend", m_videoIgnorePercentAtEnd, 0, 100.0f);
+
+ XMLUtils::GetBoolean(pElement, "usetimeseeking", m_videoUseTimeSeeking);
+ XMLUtils::GetInt(pElement, "timeseekforward", m_videoTimeSeekForward, 0, 6000);
+ XMLUtils::GetInt(pElement, "timeseekbackward", m_videoTimeSeekBackward, -6000, 0);
+ XMLUtils::GetInt(pElement, "timeseekforwardbig", m_videoTimeSeekForwardBig, 0, 6000);
+ XMLUtils::GetInt(pElement, "timeseekbackwardbig", m_videoTimeSeekBackwardBig, -6000, 0);
+
+ XMLUtils::GetInt(pElement, "percentseekforward", m_videoPercentSeekForward, 0, 100);
+ XMLUtils::GetInt(pElement, "percentseekbackward", m_videoPercentSeekBackward, -100, 0);
+ XMLUtils::GetInt(pElement, "percentseekforwardbig", m_videoPercentSeekForwardBig, 0, 100);
+ XMLUtils::GetInt(pElement, "percentseekbackwardbig", m_videoPercentSeekBackwardBig, -100, 0);
+
+ TiXmlElement* pVideoExcludes = pElement->FirstChildElement("excludefromlisting");
+ if (pVideoExcludes)
+ GetCustomRegexps(pVideoExcludes, m_videoExcludeFromListingRegExps);
+
+ pVideoExcludes = pElement->FirstChildElement("excludefromscan");
+ if (pVideoExcludes)
+ GetCustomRegexps(pVideoExcludes, m_moviesExcludeFromScanRegExps);
+
+ pVideoExcludes = pElement->FirstChildElement("excludetvshowsfromscan");
+ if (pVideoExcludes)
+ GetCustomRegexps(pVideoExcludes, m_tvshowExcludeFromScanRegExps);
+
+ pVideoExcludes = pElement->FirstChildElement("cleanstrings");
+ if (pVideoExcludes)
+ GetCustomRegexps(pVideoExcludes, m_videoCleanStringRegExps);
+
+ XMLUtils::GetString(pElement,"cleandatetime", m_videoCleanDateTimeRegExp);
+ XMLUtils::GetString(pElement,"ppffmpegpostprocessing",m_videoPPFFmpegPostProc);
+ XMLUtils::GetInt(pElement,"vdpauscaling",m_videoVDPAUScaling);
+ XMLUtils::GetFloat(pElement, "nonlinearstretchratio", m_videoNonLinStretchRatio, 0.01f, 1.0f);
+ XMLUtils::GetFloat(pElement,"autoscalemaxfps",m_videoAutoScaleMaxFps, 0.0f, 1000.0f);
+ XMLUtils::GetInt(pElement, "useocclusionquery", m_videoCaptureUseOcclusionQuery, -1, 1);
+ XMLUtils::GetBoolean(pElement,"vdpauInvTelecine",m_videoVDPAUtelecine);
+ XMLUtils::GetBoolean(pElement,"vdpauHDdeintSkipChroma",m_videoVDPAUdeintSkipChromaHD);
+
+ TiXmlElement* pAdjustRefreshrate = pElement->FirstChildElement("adjustrefreshrate");
+ if (pAdjustRefreshrate)
+ {
+ TiXmlElement* pRefreshOverride = pAdjustRefreshrate->FirstChildElement("override");
+ while (pRefreshOverride)
+ {
+ RefreshOverride override = {};
+
+ float fps;
+ if (XMLUtils::GetFloat(pRefreshOverride, "fps", fps))
+ {
+ override.fpsmin = fps - 0.01f;
+ override.fpsmax = fps + 0.01f;
+ }
+
+ float fpsmin, fpsmax;
+ if (XMLUtils::GetFloat(pRefreshOverride, "fpsmin", fpsmin) &&
+ XMLUtils::GetFloat(pRefreshOverride, "fpsmax", fpsmax))
+ {
+ override.fpsmin = fpsmin;
+ override.fpsmax = fpsmax;
+ }
+
+ float refresh;
+ if (XMLUtils::GetFloat(pRefreshOverride, "refresh", refresh))
+ {
+ override.refreshmin = refresh - 0.01f;
+ override.refreshmax = refresh + 0.01f;
+ }
+
+ float refreshmin, refreshmax;
+ if (XMLUtils::GetFloat(pRefreshOverride, "refreshmin", refreshmin) &&
+ XMLUtils::GetFloat(pRefreshOverride, "refreshmax", refreshmax))
+ {
+ override.refreshmin = refreshmin;
+ override.refreshmax = refreshmax;
+ }
+
+ bool fpsCorrect = (override.fpsmin > 0.0f && override.fpsmax >= override.fpsmin);
+ bool refreshCorrect = (override.refreshmin > 0.0f && override.refreshmax >= override.refreshmin);
+
+ if (fpsCorrect && refreshCorrect)
+ m_videoAdjustRefreshOverrides.push_back(override);
+ else
+ CLog::Log(LOGWARNING,
+ "Ignoring malformed refreshrate override, fpsmin:{:f} fpsmax:{:f} "
+ "refreshmin:{:f} refreshmax:{:f}",
+ override.fpsmin, override.fpsmax, override.refreshmin, override.refreshmax);
+
+ pRefreshOverride = pRefreshOverride->NextSiblingElement("override");
+ }
+
+ TiXmlElement* pRefreshFallback = pAdjustRefreshrate->FirstChildElement("fallback");
+ while (pRefreshFallback)
+ {
+ RefreshOverride fallback = {};
+ fallback.fallback = true;
+
+ float refresh;
+ if (XMLUtils::GetFloat(pRefreshFallback, "refresh", refresh))
+ {
+ fallback.refreshmin = refresh - 0.01f;
+ fallback.refreshmax = refresh + 0.01f;
+ }
+
+ float refreshmin, refreshmax;
+ if (XMLUtils::GetFloat(pRefreshFallback, "refreshmin", refreshmin) &&
+ XMLUtils::GetFloat(pRefreshFallback, "refreshmax", refreshmax))
+ {
+ fallback.refreshmin = refreshmin;
+ fallback.refreshmax = refreshmax;
+ }
+
+ if (fallback.refreshmin > 0.0f && fallback.refreshmax >= fallback.refreshmin)
+ m_videoAdjustRefreshOverrides.push_back(fallback);
+ else
+ CLog::Log(LOGWARNING,
+ "Ignoring malformed refreshrate fallback, fpsmin:{:f} fpsmax:{:f} "
+ "refreshmin:{:f} refreshmax:{:f}",
+ fallback.fpsmin, fallback.fpsmax, fallback.refreshmin, fallback.refreshmax);
+
+ pRefreshFallback = pRefreshFallback->NextSiblingElement("fallback");
+ }
+ }
+
+ m_DXVACheckCompatibilityPresent = XMLUtils::GetBoolean(pElement,"checkdxvacompatibility", m_DXVACheckCompatibility);
+
+ //0 = disable fps detect, 1 = only detect on timestamps with uniform spacing, 2 detect on all timestamps
+ XMLUtils::GetInt(pElement, "fpsdetect", m_videoFpsDetect, 0, 2);
+ XMLUtils::GetFloat(pElement, "maxtempo", m_maxTempo, 1.5, 2.1);
+ XMLUtils::GetBoolean(pElement, "preferstereostream", m_videoPreferStereoStream);
+
+ // Store global display latency settings
+ TiXmlElement* pVideoLatency = pElement->FirstChildElement("latency");
+ if (pVideoLatency)
+ {
+ float refresh, refreshmin, refreshmax, delay;
+ TiXmlElement* pRefreshVideoLatency = pVideoLatency->FirstChildElement("refresh");
+
+ while (pRefreshVideoLatency)
+ {
+ RefreshVideoLatency videolatency = {};
+
+ if (XMLUtils::GetFloat(pRefreshVideoLatency, "rate", refresh))
+ {
+ videolatency.refreshmin = refresh - 0.01f;
+ videolatency.refreshmax = refresh + 0.01f;
+ }
+ else if (XMLUtils::GetFloat(pRefreshVideoLatency, "min", refreshmin) &&
+ XMLUtils::GetFloat(pRefreshVideoLatency, "max", refreshmax))
+ {
+ videolatency.refreshmin = refreshmin;
+ videolatency.refreshmax = refreshmax;
+ }
+ if (XMLUtils::GetFloat(pRefreshVideoLatency, "delay", delay, -600.0f, 600.0f))
+ videolatency.delay = delay;
+
+ if (videolatency.refreshmin > 0.0f && videolatency.refreshmax >= videolatency.refreshmin)
+ m_videoRefreshLatency.push_back(videolatency);
+ else
+ CLog::Log(LOGWARNING,
+ "Ignoring malformed display latency <refresh> entry, min:{:f} max:{:f}",
+ videolatency.refreshmin, videolatency.refreshmax);
+
+ pRefreshVideoLatency = pRefreshVideoLatency->NextSiblingElement("refresh");
+ }
+
+ // Get default global display latency
+ XMLUtils::GetFloat(pVideoLatency, "delay", m_videoDefaultLatency, -600.0f, 600.0f);
+ }
+ }
+
+ pElement = pRootElement->FirstChildElement("musiclibrary");
+ if (pElement)
+ {
+ XMLUtils::GetInt(pElement, "recentlyaddeditems", m_iMusicLibraryRecentlyAddedItems, 1, INT_MAX);
+ XMLUtils::GetBoolean(pElement, "prioritiseapetags", m_prioritiseAPEv2tags);
+ XMLUtils::GetBoolean(pElement, "allitemsonbottom", m_bMusicLibraryAllItemsOnBottom);
+ XMLUtils::GetBoolean(pElement, "cleanonupdate", m_bMusicLibraryCleanOnUpdate);
+ XMLUtils::GetBoolean(pElement, "artistsortonupdate", m_bMusicLibraryArtistSortOnUpdate);
+ XMLUtils::GetString(pElement, "albumformat", m_strMusicLibraryAlbumFormat);
+ XMLUtils::GetString(pElement, "itemseparator", m_musicItemSeparator);
+ XMLUtils::GetInt(pElement, "dateadded", m_iMusicLibraryDateAdded);
+ XMLUtils::GetBoolean(pElement, "useisodates", m_bMusicLibraryUseISODates);
+ //Music artist name separators
+ TiXmlElement* separators = pElement->FirstChildElement("artistseparators");
+ if (separators)
+ {
+ m_musicArtistSeparators.clear();
+ TiXmlNode* separator = separators->FirstChild("separator");
+ while (separator)
+ {
+ if (separator->FirstChild())
+ m_musicArtistSeparators.push_back(separator->FirstChild()->ValueStr());
+ separator = separator->NextSibling("separator");
+ }
+ }
+
+ SetExtraArtwork(pElement->FirstChildElement("artistextraart"), m_musicArtistExtraArt);
+ SetExtraArtwork(pElement->FirstChildElement("albumextraart"), m_musicAlbumExtraArt);
+ }
+
+ pElement = pRootElement->FirstChildElement("videolibrary");
+ if (pElement)
+ {
+ XMLUtils::GetBoolean(pElement, "allitemsonbottom", m_bVideoLibraryAllItemsOnBottom);
+ XMLUtils::GetInt(pElement, "recentlyaddeditems", m_iVideoLibraryRecentlyAddedItems, 1, INT_MAX);
+ XMLUtils::GetBoolean(pElement, "cleanonupdate", m_bVideoLibraryCleanOnUpdate);
+ XMLUtils::GetBoolean(pElement, "usefasthash", m_bVideoLibraryUseFastHash);
+ XMLUtils::GetString(pElement, "itemseparator", m_videoItemSeparator);
+ XMLUtils::GetBoolean(pElement, "importwatchedstate", m_bVideoLibraryImportWatchedState);
+ XMLUtils::GetBoolean(pElement, "importresumepoint", m_bVideoLibraryImportResumePoint);
+ XMLUtils::GetInt(pElement, "dateadded", m_iVideoLibraryDateAdded);
+
+ SetExtraArtwork(pElement->FirstChildElement("episodeextraart"), m_videoEpisodeExtraArt);
+ SetExtraArtwork(pElement->FirstChildElement("tvshowextraart"), m_videoTvShowExtraArt);
+ SetExtraArtwork(pElement->FirstChildElement("tvseasonextraart"), m_videoTvSeasonExtraArt);
+ SetExtraArtwork(pElement->FirstChildElement("movieextraart"), m_videoMovieExtraArt);
+ SetExtraArtwork(pElement->FirstChildElement("moviesetextraart"), m_videoMovieSetExtraArt);
+ SetExtraArtwork(pElement->FirstChildElement("musicvideoextraart"), m_videoMusicVideoExtraArt);
+ }
+
+ pElement = pRootElement->FirstChildElement("videoscanner");
+ if (pElement)
+ {
+ XMLUtils::GetBoolean(pElement, "ignoreerrors", m_bVideoScannerIgnoreErrors);
+ }
+
+ // Backward-compatibility of ExternalPlayer config
+ pElement = pRootElement->FirstChildElement("externalplayer");
+ if (pElement)
+ {
+ CLog::Log(LOGWARNING, "External player configuration has been removed from advancedsettings.xml. It can now be configured in userdata/playercorefactory.xml");
+ }
+ pElement = pRootElement->FirstChildElement("slideshow");
+ if (pElement)
+ {
+ XMLUtils::GetFloat(pElement, "panamount", m_slideshowPanAmount, 0.0f, 20.0f);
+ XMLUtils::GetFloat(pElement, "zoomamount", m_slideshowZoomAmount, 0.0f, 20.0f);
+ XMLUtils::GetFloat(pElement, "blackbarcompensation", m_slideshowBlackBarCompensation, 0.0f, 50.0f);
+ }
+
+ pElement = pRootElement->FirstChildElement("network");
+ if (pElement)
+ {
+ XMLUtils::GetInt(pElement, "curlclienttimeout", m_curlconnecttimeout, 1, 1000);
+ XMLUtils::GetInt(pElement, "curllowspeedtime", m_curllowspeedtime, 1, 1000);
+ XMLUtils::GetInt(pElement, "curlretries", m_curlretries, 0, 10);
+ XMLUtils::GetInt(pElement, "curlkeepaliveinterval", m_curlKeepAliveInterval, 0, 300);
+ XMLUtils::GetBoolean(pElement, "disableipv6", m_curlDisableIPV6);
+ XMLUtils::GetBoolean(pElement, "disablehttp2", m_curlDisableHTTP2);
+ XMLUtils::GetString(pElement, "catrustfile", m_caTrustFile);
+ }
+
+ pElement = pRootElement->FirstChildElement("cache");
+ if (pElement)
+ {
+ XMLUtils::GetUInt(pElement, "memorysize", m_cacheMemSize);
+ XMLUtils::GetUInt(pElement, "buffermode", m_cacheBufferMode, 0, 4);
+ XMLUtils::GetUInt(pElement, "chunksize", m_cacheChunkSize, 256, 1024 * 1024);
+ XMLUtils::GetFloat(pElement, "readfactor", m_cacheReadFactor);
+ }
+
+ pElement = pRootElement->FirstChildElement("jsonrpc");
+ if (pElement)
+ {
+ XMLUtils::GetBoolean(pElement, "compactoutput", m_jsonOutputCompact);
+ XMLUtils::GetUInt(pElement, "tcpport", m_jsonTcpPort);
+ }
+
+ pElement = pRootElement->FirstChildElement("samba");
+ if (pElement)
+ {
+ XMLUtils::GetString(pElement, "doscodepage", m_sambadoscodepage);
+ XMLUtils::GetInt(pElement, "clienttimeout", m_sambaclienttimeout, 5, 100);
+ XMLUtils::GetBoolean(pElement, "statfiles", m_sambastatfiles);
+ }
+
+ pElement = pRootElement->FirstChildElement("httpdirectory");
+ if (pElement)
+ XMLUtils::GetBoolean(pElement, "statfilesize", m_bHTTPDirectoryStatFilesize);
+
+ pElement = pRootElement->FirstChildElement("ftp");
+ if (pElement)
+ {
+ XMLUtils::GetBoolean(pElement, "remotethumbs", m_bFTPThumbs);
+ }
+
+ pElement = pRootElement->FirstChildElement("loglevel");
+ if (pElement)
+ { // read the loglevel setting, so set the setting advanced to hide it in GUI
+ // as altering it will do nothing - we don't write to advancedsettings.xml
+ XMLUtils::GetInt(pRootElement, "loglevel", m_logLevelHint, LOG_LEVEL_NONE, LOG_LEVEL_MAX);
+ const char* hide = pElement->Attribute("hide");
+ if (hide == NULL || StringUtils::CompareNoCase("false", hide, 5) != 0)
+ {
+ SettingPtr setting = CServiceBroker::GetSettingsComponent()->GetSettings()->GetSetting(CSettings::SETTING_DEBUG_SHOWLOGINFO);
+ if (setting != NULL)
+ setting->SetVisible(false);
+ }
+ m_logLevel = std::max(m_logLevel, m_logLevelHint);
+ CServiceBroker::GetLogging().SetLogLevel(m_logLevel);
+ }
+
+ XMLUtils::GetString(pRootElement, "cddbaddress", m_cddbAddress);
+ XMLUtils::GetBoolean(pRootElement, "addsourceontop", m_addSourceOnTop);
+
+ //airtunes + airplay
+ XMLUtils::GetInt(pRootElement, "airtunesport", m_airTunesPort);
+ XMLUtils::GetInt(pRootElement, "airplayport", m_airPlayPort);
+
+ XMLUtils::GetBoolean(pRootElement, "handlemounting", m_handleMounting);
+ XMLUtils::GetBoolean(pRootElement, "automountopticalmedia", m_autoMountOpticalMedia);
+
+#if defined(TARGET_WINDOWS_DESKTOP)
+ XMLUtils::GetBoolean(pRootElement, "minimizetotray", m_minimizeToTray);
+#endif
+#if defined(TARGET_DARWIN_OSX) || defined(TARGET_WINDOWS)
+ XMLUtils::GetBoolean(pRootElement, "fullscreen", m_startFullScreen);
+#endif
+ XMLUtils::GetBoolean(pRootElement, "splash", m_splashImage);
+ XMLUtils::GetBoolean(pRootElement, "showexitbutton", m_showExitButton);
+ XMLUtils::GetBoolean(pRootElement, "canwindowed", m_canWindowed);
+
+ XMLUtils::GetInt(pRootElement, "songinfoduration", m_songInfoDuration, 0, INT_MAX);
+ XMLUtils::GetInt(pRootElement, "playlistretries", m_playlistRetries, -1, 5000);
+ XMLUtils::GetInt(pRootElement, "playlisttimeout", m_playlistTimeout, 0, 5000);
+
+ XMLUtils::GetBoolean(pRootElement,"glrectanglehack", m_GLRectangleHack);
+ XMLUtils::GetInt(pRootElement,"skiploopfilter", m_iSkipLoopFilter, -16, 48);
+
+ XMLUtils::GetBoolean(pRootElement,"virtualshares", m_bVirtualShares);
+ XMLUtils::GetUInt(pRootElement, "packagefoldersize", m_addonPackageFolderSize);
+
+ // EPG
+ pElement = pRootElement->FirstChildElement("epg");
+ if (pElement)
+ {
+ XMLUtils::GetInt(pElement, "updatecheckinterval", m_iEpgUpdateCheckInterval);
+ XMLUtils::GetInt(pElement, "cleanupinterval", m_iEpgCleanupInterval);
+ XMLUtils::GetInt(pElement, "activetagcheckinterval", m_iEpgActiveTagCheckInterval);
+ XMLUtils::GetInt(pElement, "retryinterruptedupdateinterval", m_iEpgRetryInterruptedUpdateInterval);
+ XMLUtils::GetInt(pElement, "updateemptytagsinterval", m_iEpgUpdateEmptyTagsInterval);
+ XMLUtils::GetBoolean(pElement, "displayupdatepopup", m_bEpgDisplayUpdatePopup);
+ XMLUtils::GetBoolean(pElement, "displayincrementalupdatepopup", m_bEpgDisplayIncrementalUpdatePopup);
+ }
+
+ // EDL commercial break handling
+ pElement = pRootElement->FirstChildElement("edl");
+ if (pElement)
+ {
+ XMLUtils::GetBoolean(pElement, "mergeshortcommbreaks", m_bEdlMergeShortCommBreaks);
+ XMLUtils::GetBoolean(pElement, "displaycommbreaknotifications",
+ m_EdlDisplayCommbreakNotifications);
+ XMLUtils::GetInt(pElement, "maxcommbreaklength", m_iEdlMaxCommBreakLength, 0, 10 * 60); // Between 0 and 10 minutes
+ XMLUtils::GetInt(pElement, "mincommbreaklength", m_iEdlMinCommBreakLength, 0, 5 * 60); // Between 0 and 5 minutes
+ XMLUtils::GetInt(pElement, "maxcommbreakgap", m_iEdlMaxCommBreakGap, 0, 5 * 60); // Between 0 and 5 minutes.
+ XMLUtils::GetInt(pElement, "maxstartgap", m_iEdlMaxStartGap, 0, 10 * 60); // Between 0 and 10 minutes
+ XMLUtils::GetInt(pElement, "commbreakautowait", m_iEdlCommBreakAutowait, -60, 60); // Between -60 and 60 seconds
+ XMLUtils::GetInt(pElement, "commbreakautowind", m_iEdlCommBreakAutowind, -60, 60); // Between -60 and 60 seconds
+ }
+
+ // picture exclude regexps
+ TiXmlElement* pPictureExcludes = pRootElement->FirstChildElement("pictureexcludes");
+ if (pPictureExcludes)
+ GetCustomRegexps(pPictureExcludes, m_pictureExcludeFromListingRegExps);
+
+ // picture extensions
+ TiXmlElement* pExts = pRootElement->FirstChildElement("pictureextensions");
+ if (pExts)
+ GetCustomExtensions(pExts, m_pictureExtensions);
+
+ // music extensions
+ pExts = pRootElement->FirstChildElement("musicextensions");
+ if (pExts)
+ GetCustomExtensions(pExts, m_musicExtensions);
+
+ // video extensions
+ pExts = pRootElement->FirstChildElement("videoextensions");
+ if (pExts)
+ GetCustomExtensions(pExts, m_videoExtensions);
+
+ // stub extensions
+ pExts = pRootElement->FirstChildElement("discstubextensions");
+ if (pExts)
+ GetCustomExtensions(pExts, m_discStubExtensions);
+
+ m_vecTokens.clear();
+ CLangInfo::LoadTokens(pRootElement->FirstChild("sorttokens"),m_vecTokens);
+
+ //! @todo Should cache path be given in terms of our predefined paths??
+ //! Are we even going to have predefined paths??
+ std::string tmp;
+ if (XMLUtils::GetPath(pRootElement, "cachepath", tmp))
+ m_cachePath = tmp;
+ URIUtils::AddSlashAtEnd(m_cachePath);
+
+ g_LangCodeExpander.LoadUserCodes(pRootElement->FirstChildElement("languagecodes"));
+
+ // trailer matching regexps
+ TiXmlElement* pTrailerMatching = pRootElement->FirstChildElement("trailermatching");
+ if (pTrailerMatching)
+ GetCustomRegexps(pTrailerMatching, m_trailerMatchRegExps);
+
+ //everything that's a trailer is not a movie
+ m_moviesExcludeFromScanRegExps.insert(m_moviesExcludeFromScanRegExps.end(),
+ m_trailerMatchRegExps.begin(),
+ m_trailerMatchRegExps.end());
+
+ // video stacking regexps
+ TiXmlElement* pVideoStacking = pRootElement->FirstChildElement("moviestacking");
+ if (pVideoStacking)
+ GetCustomRegexps(pVideoStacking, m_videoStackRegExps);
+
+ // folder stacking regexps
+ TiXmlElement* pFolderStacking = pRootElement->FirstChildElement("folderstacking");
+ if (pFolderStacking)
+ GetCustomRegexps(pFolderStacking, m_folderStackRegExps);
+
+ //tv stacking regexps
+ TiXmlElement* pTVStacking = pRootElement->FirstChildElement("tvshowmatching");
+ if (pTVStacking)
+ GetCustomTVRegexps(pTVStacking, m_tvshowEnumRegExps);
+
+ //tv multipart enumeration regexp
+ XMLUtils::GetString(pRootElement, "tvmultipartmatching", m_tvshowMultiPartEnumRegExp);
+
+ // path substitutions
+ TiXmlElement* pPathSubstitution = pRootElement->FirstChildElement("pathsubstitution");
+ if (pPathSubstitution)
+ {
+ m_pathSubstitutions.clear();
+ CLog::Log(LOGDEBUG,"Configuring path substitutions");
+ TiXmlNode* pSubstitute = pPathSubstitution->FirstChildElement("substitute");
+ while (pSubstitute)
+ {
+ std::string strFrom, strTo;
+ TiXmlNode* pFrom = pSubstitute->FirstChild("from");
+ if (pFrom && !pFrom->NoChildren())
+ strFrom = CSpecialProtocol::TranslatePath(pFrom->FirstChild()->Value()).c_str();
+ TiXmlNode* pTo = pSubstitute->FirstChild("to");
+ if (pTo && !pTo->NoChildren())
+ strTo = pTo->FirstChild()->Value();
+
+ if (!strFrom.empty() && !strTo.empty())
+ {
+ CLog::Log(LOGDEBUG," Registering substitution pair:");
+ CLog::Log(LOGDEBUG, " From: [{}]", CURL::GetRedacted(strFrom));
+ CLog::Log(LOGDEBUG, " To: [{}]", CURL::GetRedacted(strTo));
+ m_pathSubstitutions.push_back(std::make_pair(strFrom,strTo));
+ }
+ else
+ {
+ // error message about missing tag
+ if (strFrom.empty())
+ CLog::Log(LOGERROR," Missing <from> tag");
+ else
+ CLog::Log(LOGERROR," Missing <to> tag");
+ }
+
+ // get next one
+ pSubstitute = pSubstitute->NextSiblingElement("substitute");
+ }
+ }
+
+ XMLUtils::GetInt(pRootElement, "remotedelay", m_remoteDelay, 0, 20);
+ XMLUtils::GetBoolean(pRootElement, "scanirserver", m_bScanIRServer);
+
+ XMLUtils::GetUInt(pRootElement, "fanartres", m_fanartRes, 0, 9999);
+ XMLUtils::GetUInt(pRootElement, "imageres", m_imageRes, 0, 9999);
+ if (XMLUtils::GetString(pRootElement, "imagescalingalgorithm", tmp))
+ m_imageScalingAlgorithm = CPictureScalingAlgorithm::FromString(tmp);
+ XMLUtils::GetUInt(pRootElement, "imagequalityjpeg", m_imageQualityJpeg, 0, 21);
+ XMLUtils::GetBoolean(pRootElement, "playlistasfolders", m_playlistAsFolders);
+ XMLUtils::GetBoolean(pRootElement, "uselocalecollation", m_useLocaleCollation);
+ XMLUtils::GetBoolean(pRootElement, "detectasudf", m_detectAsUdf);
+
+ // music thumbs
+ TiXmlElement* pThumbs = pRootElement->FirstChildElement("musicthumbs");
+ if (pThumbs)
+ GetCustomExtensions(pThumbs,m_musicThumbs);
+
+ // show art for shoutcast v2 streams (set to false for devices with limited storage)
+ XMLUtils::GetBoolean(pRootElement, "shoutcastart", m_bShoutcastArt);
+ // music filename->tag filters
+ TiXmlElement* filters = pRootElement->FirstChildElement("musicfilenamefilters");
+ if (filters)
+ {
+ TiXmlNode* filter = filters->FirstChild("filter");
+ while (filter)
+ {
+ if (filter->FirstChild())
+ m_musicTagsFromFileFilters.push_back(filter->FirstChild()->ValueStr());
+ filter = filter->NextSibling("filter");
+ }
+ }
+
+ TiXmlElement* pHostEntries = pRootElement->FirstChildElement("hosts");
+ if (pHostEntries)
+ {
+ TiXmlElement* element = pHostEntries->FirstChildElement("entry");
+ while(element)
+ {
+ if(!element->NoChildren())
+ {
+ std::string name = XMLUtils::GetAttribute(element, "name");
+ std::string value = element->FirstChild()->ValueStr();
+ if (!name.empty())
+ CDNSNameCache::Add(name, value);
+ }
+ element = element->NextSiblingElement("entry");
+ }
+ }
+
+ XMLUtils::GetString(pRootElement, "cputempcommand", m_cpuTempCmd);
+ XMLUtils::GetString(pRootElement, "gputempcommand", m_gpuTempCmd);
+
+ XMLUtils::GetBoolean(pRootElement, "alwaysontop", m_alwaysOnTop);
+
+ TiXmlElement *pPVR = pRootElement->FirstChildElement("pvr");
+ if (pPVR)
+ {
+ XMLUtils::GetInt(pPVR, "timecorrection", m_iPVRTimeCorrection, 0, 1440);
+ XMLUtils::GetInt(pPVR, "infotoggleinterval", m_iPVRInfoToggleInterval, 0, 30000);
+ XMLUtils::GetBoolean(pPVR, "channeliconsautoscan", m_bPVRChannelIconsAutoScan);
+ XMLUtils::GetBoolean(pPVR, "autoscaniconsuserset", m_bPVRAutoScanIconsUserSet);
+ XMLUtils::GetInt(pPVR, "numericchannelswitchtimeout", m_iPVRNumericChannelSwitchTimeout, 50, 60000);
+ XMLUtils::GetInt(pPVR, "timeshiftthreshold", m_iPVRTimeshiftThreshold, 0, 60);
+ XMLUtils::GetBoolean(pPVR, "timeshiftsimpleosd", m_bPVRTimeshiftSimpleOSD);
+ TiXmlElement* pSortDecription = pPVR->FirstChildElement("pvrrecordings");
+ if (pSortDecription)
+ {
+ const char* XML_SORTMETHOD = "sortmethod";
+ const char* XML_SORTORDER = "sortorder";
+ int sortMethod;
+ // ignore SortByTime for duration defaults
+ if (XMLUtils::GetInt(pSortDecription, XML_SORTMETHOD, sortMethod, SortByLabel, SortByFile))
+ {
+ int sortOrder;
+ if (XMLUtils::GetInt(pSortDecription, XML_SORTORDER, sortOrder, SortOrderAscending,
+ SortOrderDescending))
+ {
+ m_PVRDefaultSortOrder.sortBy = (SortBy)sortMethod;
+ m_PVRDefaultSortOrder.sortOrder = (SortOrder)sortOrder;
+ }
+ }
+ }
+ }
+
+ TiXmlElement* pDatabase = pRootElement->FirstChildElement("videodatabase");
+ if (pDatabase)
+ {
+ CLog::Log(LOGWARNING, "VIDEO database configuration is experimental.");
+ XMLUtils::GetString(pDatabase, "type", m_databaseVideo.type);
+ XMLUtils::GetString(pDatabase, "host", m_databaseVideo.host);
+ XMLUtils::GetString(pDatabase, "port", m_databaseVideo.port);
+ XMLUtils::GetString(pDatabase, "user", m_databaseVideo.user);
+ XMLUtils::GetString(pDatabase, "pass", m_databaseVideo.pass);
+ XMLUtils::GetString(pDatabase, "name", m_databaseVideo.name);
+ XMLUtils::GetString(pDatabase, "key", m_databaseVideo.key);
+ XMLUtils::GetString(pDatabase, "cert", m_databaseVideo.cert);
+ XMLUtils::GetString(pDatabase, "ca", m_databaseVideo.ca);
+ XMLUtils::GetString(pDatabase, "capath", m_databaseVideo.capath);
+ XMLUtils::GetString(pDatabase, "ciphers", m_databaseVideo.ciphers);
+ XMLUtils::GetBoolean(pDatabase, "compression", m_databaseVideo.compression);
+ }
+
+ pDatabase = pRootElement->FirstChildElement("musicdatabase");
+ if (pDatabase)
+ {
+ XMLUtils::GetString(pDatabase, "type", m_databaseMusic.type);
+ XMLUtils::GetString(pDatabase, "host", m_databaseMusic.host);
+ XMLUtils::GetString(pDatabase, "port", m_databaseMusic.port);
+ XMLUtils::GetString(pDatabase, "user", m_databaseMusic.user);
+ XMLUtils::GetString(pDatabase, "pass", m_databaseMusic.pass);
+ XMLUtils::GetString(pDatabase, "name", m_databaseMusic.name);
+ XMLUtils::GetString(pDatabase, "key", m_databaseMusic.key);
+ XMLUtils::GetString(pDatabase, "cert", m_databaseMusic.cert);
+ XMLUtils::GetString(pDatabase, "ca", m_databaseMusic.ca);
+ XMLUtils::GetString(pDatabase, "capath", m_databaseMusic.capath);
+ XMLUtils::GetString(pDatabase, "ciphers", m_databaseMusic.ciphers);
+ XMLUtils::GetBoolean(pDatabase, "compression", m_databaseMusic.compression);
+ }
+
+ pDatabase = pRootElement->FirstChildElement("tvdatabase");
+ if (pDatabase)
+ {
+ XMLUtils::GetString(pDatabase, "type", m_databaseTV.type);
+ XMLUtils::GetString(pDatabase, "host", m_databaseTV.host);
+ XMLUtils::GetString(pDatabase, "port", m_databaseTV.port);
+ XMLUtils::GetString(pDatabase, "user", m_databaseTV.user);
+ XMLUtils::GetString(pDatabase, "pass", m_databaseTV.pass);
+ XMLUtils::GetString(pDatabase, "name", m_databaseTV.name);
+ XMLUtils::GetString(pDatabase, "key", m_databaseTV.key);
+ XMLUtils::GetString(pDatabase, "cert", m_databaseTV.cert);
+ XMLUtils::GetString(pDatabase, "ca", m_databaseTV.ca);
+ XMLUtils::GetString(pDatabase, "capath", m_databaseTV.capath);
+ XMLUtils::GetString(pDatabase, "ciphers", m_databaseTV.ciphers);
+ XMLUtils::GetBoolean(pDatabase, "compression", m_databaseTV.compression);
+ }
+
+ pDatabase = pRootElement->FirstChildElement("epgdatabase");
+ if (pDatabase)
+ {
+ XMLUtils::GetString(pDatabase, "type", m_databaseEpg.type);
+ XMLUtils::GetString(pDatabase, "host", m_databaseEpg.host);
+ XMLUtils::GetString(pDatabase, "port", m_databaseEpg.port);
+ XMLUtils::GetString(pDatabase, "user", m_databaseEpg.user);
+ XMLUtils::GetString(pDatabase, "pass", m_databaseEpg.pass);
+ XMLUtils::GetString(pDatabase, "name", m_databaseEpg.name);
+ XMLUtils::GetString(pDatabase, "key", m_databaseEpg.key);
+ XMLUtils::GetString(pDatabase, "cert", m_databaseEpg.cert);
+ XMLUtils::GetString(pDatabase, "ca", m_databaseEpg.ca);
+ XMLUtils::GetString(pDatabase, "capath", m_databaseEpg.capath);
+ XMLUtils::GetString(pDatabase, "ciphers", m_databaseEpg.ciphers);
+ XMLUtils::GetBoolean(pDatabase, "compression", m_databaseEpg.compression);
+ }
+
+ pElement = pRootElement->FirstChildElement("enablemultimediakeys");
+ if (pElement)
+ {
+ XMLUtils::GetBoolean(pRootElement, "enablemultimediakeys", m_enableMultimediaKeys);
+ }
+
+ pElement = pRootElement->FirstChildElement("gui");
+ if (pElement)
+ {
+ XMLUtils::GetBoolean(pElement, "visualizedirtyregions", m_guiVisualizeDirtyRegions);
+ XMLUtils::GetInt(pElement, "algorithmdirtyregions", m_guiAlgorithmDirtyRegions);
+ XMLUtils::GetBoolean(pElement, "smartredraw", m_guiSmartRedraw);
+ }
+
+ std::string seekSteps;
+ XMLUtils::GetString(pRootElement, "seeksteps", seekSteps);
+ if (!seekSteps.empty())
+ {
+ m_seekSteps.clear();
+ std::vector<std::string> steps = StringUtils::Split(seekSteps, ',');
+ for(std::vector<std::string>::iterator it = steps.begin(); it != steps.end(); ++it)
+ m_seekSteps.push_back(atoi((*it).c_str()));
+ }
+
+ XMLUtils::GetBoolean(pRootElement, "opengldebugging", m_openGlDebugging);
+
+ // load in the settings overrides
+ CServiceBroker::GetSettingsComponent()->GetSettings()->LoadHidden(pRootElement);
+
+ // Migration of old style art options from advanced setting to GUI setting
+ MigrateOldArtSettings();
+}
+
+void CAdvancedSettings::Clear()
+{
+ m_videoCleanStringRegExps.clear();
+ m_moviesExcludeFromScanRegExps.clear();
+ m_tvshowExcludeFromScanRegExps.clear();
+ m_videoExcludeFromListingRegExps.clear();
+ m_videoStackRegExps.clear();
+ m_folderStackRegExps.clear();
+ m_allExcludeFromScanRegExps.clear();
+ m_audioExcludeFromScanRegExps.clear();
+ m_audioExcludeFromListingRegExps.clear();
+ m_pictureExcludeFromListingRegExps.clear();
+
+ m_pictureExtensions.clear();
+ m_musicExtensions.clear();
+ m_videoExtensions.clear();
+ m_discStubExtensions.clear();
+
+ m_userAgent.clear();
+}
+
+void CAdvancedSettings::GetCustomTVRegexps(TiXmlElement *pRootElement, SETTINGS_TVSHOWLIST& settings)
+{
+ TiXmlElement *pElement = pRootElement;
+ while (pElement)
+ {
+ int iAction = 0; // overwrite
+ // for backward compatibility
+ const char* szAppend = pElement->Attribute("append");
+ if ((szAppend && StringUtils::CompareNoCase(szAppend, "yes") == 0))
+ iAction = 1;
+ // action takes precedence if both attributes exist
+ const char* szAction = pElement->Attribute("action");
+ if (szAction)
+ {
+ iAction = 0; // overwrite
+ if (StringUtils::CompareNoCase(szAction, "append") == 0)
+ iAction = 1; // append
+ else if (StringUtils::CompareNoCase(szAction, "prepend") == 0)
+ iAction = 2; // prepend
+ }
+ if (iAction == 0)
+ settings.clear();
+ TiXmlNode* pRegExp = pElement->FirstChild("regexp");
+ int i = 0;
+ while (pRegExp)
+ {
+ if (pRegExp->FirstChild())
+ {
+ bool bByDate = false;
+ bool byTitle = false;
+ int iDefaultSeason = 1;
+ if (pRegExp->ToElement())
+ {
+ std::string byDate = XMLUtils::GetAttribute(pRegExp->ToElement(), "bydate");
+ if (byDate == "true")
+ {
+ bByDate = true;
+ }
+ std::string byTitleAttr = XMLUtils::GetAttribute(pRegExp->ToElement(), "bytitle");
+ byTitle = (byTitleAttr == "true");
+ std::string defaultSeason = XMLUtils::GetAttribute(pRegExp->ToElement(), "defaultseason");
+ if(!defaultSeason.empty())
+ {
+ iDefaultSeason = atoi(defaultSeason.c_str());
+ }
+ }
+ std::string regExp = pRegExp->FirstChild()->Value();
+ if (iAction == 2)
+ {
+ settings.insert(settings.begin() + i++, 1,
+ TVShowRegexp(bByDate, regExp, iDefaultSeason, byTitle));
+ }
+ else
+ {
+ settings.push_back(TVShowRegexp(bByDate, regExp, iDefaultSeason, byTitle));
+ }
+ }
+ pRegExp = pRegExp->NextSibling("regexp");
+ }
+
+ pElement = pElement->NextSiblingElement(pRootElement->Value());
+ }
+}
+
+void CAdvancedSettings::GetCustomRegexps(TiXmlElement *pRootElement, std::vector<std::string>& settings)
+{
+ TiXmlElement *pElement = pRootElement;
+ while (pElement)
+ {
+ int iAction = 0; // overwrite
+ // for backward compatibility
+ const char* szAppend = pElement->Attribute("append");
+ if ((szAppend && StringUtils::CompareNoCase(szAppend, "yes") == 0))
+ iAction = 1;
+ // action takes precedence if both attributes exist
+ const char* szAction = pElement->Attribute("action");
+ if (szAction)
+ {
+ iAction = 0; // overwrite
+ if (StringUtils::CompareNoCase(szAction, "append") == 0)
+ iAction = 1; // append
+ else if (StringUtils::CompareNoCase(szAction, "prepend") == 0)
+ iAction = 2; // prepend
+ }
+ if (iAction == 0)
+ settings.clear();
+ TiXmlNode* pRegExp = pElement->FirstChild("regexp");
+ int i = 0;
+ while (pRegExp)
+ {
+ if (pRegExp->FirstChild())
+ {
+ std::string regExp = pRegExp->FirstChild()->Value();
+ if (iAction == 2)
+ settings.insert(settings.begin() + i++, 1, regExp);
+ else
+ settings.push_back(regExp);
+ }
+ pRegExp = pRegExp->NextSibling("regexp");
+ }
+
+ pElement = pElement->NextSiblingElement(pRootElement->Value());
+ }
+}
+
+void CAdvancedSettings::GetCustomExtensions(TiXmlElement *pRootElement, std::string& extensions)
+{
+ std::string extraExtensions;
+ if (XMLUtils::GetString(pRootElement, "add", extraExtensions) && !extraExtensions.empty())
+ extensions += "|" + extraExtensions;
+ if (XMLUtils::GetString(pRootElement, "remove", extraExtensions) && !extraExtensions.empty())
+ {
+ std::vector<std::string> exts = StringUtils::Split(extraExtensions, '|');
+ for (std::vector<std::string>::const_iterator i = exts.begin(); i != exts.end(); ++i)
+ {
+ size_t iPos = extensions.find(*i);
+ if (iPos != std::string::npos)
+ extensions.erase(iPos,i->size()+1);
+ }
+ }
+}
+
+void CAdvancedSettings::AddSettingsFile(const std::string &filename)
+{
+ m_settingsFiles.push_back(filename);
+}
+
+float CAdvancedSettings::GetLatencyTweak(float refreshrate)
+{
+ float delay = m_videoDefaultLatency;
+ for (int i = 0; i < (int) m_videoRefreshLatency.size(); i++)
+ {
+ RefreshVideoLatency& videolatency = m_videoRefreshLatency[i];
+ if (refreshrate >= videolatency.refreshmin && refreshrate <= videolatency.refreshmax)
+ delay = videolatency.delay;
+ }
+
+ return delay; // in milliseconds
+}
+
+void CAdvancedSettings::SetDebugMode(bool debug)
+{
+ if (debug)
+ {
+ int level = std::max(m_logLevelHint, LOG_LEVEL_DEBUG_FREEMEM);
+ m_logLevel = level;
+ CServiceBroker::GetLogging().SetLogLevel(level);
+ CLog::Log(LOGINFO, "Enabled debug logging due to GUI setting. Level {}.", level);
+ }
+ else
+ {
+ int level = std::min(m_logLevelHint, LOG_LEVEL_DEBUG/*LOG_LEVEL_NORMAL*/);
+ CLog::Log(LOGINFO, "Disabled debug logging due to GUI setting. Level {}.", level);
+ m_logLevel = level;
+ CServiceBroker::GetLogging().SetLogLevel(level);
+ }
+}
+
+void CAdvancedSettings::SetExtraArtwork(const TiXmlElement* arttypes, std::vector<std::string>& artworkMap)
+{
+ if (!arttypes)
+ return;
+ artworkMap.clear();
+ const TiXmlNode* arttype = arttypes->FirstChild("arttype");
+ while (arttype)
+ {
+ if (arttype->FirstChild())
+ artworkMap.push_back(arttype->FirstChild()->ValueStr());
+ arttype = arttype->NextSibling("arttype");
+ }
+}
+
+void ConvertToWhitelist(const std::vector<std::string>& oldlist, std::vector<CVariant>& whitelist)
+{
+ for (auto& it : oldlist)
+ {
+ size_t last_index = it.find_last_not_of("0123456789");
+ std::string strFamilyType = it.substr(0, last_index + 1); // "fanart" of "fanart16"
+ if (std::find(whitelist.begin(), whitelist.end(), strFamilyType) == whitelist.end())
+ whitelist.emplace_back(strFamilyType);
+ }
+}
+
+void CAdvancedSettings::MigrateOldArtSettings()
+{
+ const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings();
+ if (!settings->GetBool(CSettings::SETTING_MUSICLIBRARY_ARTSETTINGS_UPDATED))
+ {
+ CLog::Log(LOGINFO, "Migrating old music library artwork settings to new GUI settings");
+ // Convert numeric art type variants into simple art type family entry
+ // e.g. {"banner", "fanart1", "fanart2", "fanart3"... } into { "banner", "fanart"}
+ if (!m_musicArtistExtraArt.empty())
+ {
+ std::vector<CVariant> whitelist;
+ ConvertToWhitelist(m_musicArtistExtraArt, whitelist);
+ settings->SetList(CSettings::SETTING_MUSICLIBRARY_ARTISTART_WHITELIST, whitelist);
+ }
+ if (!m_musicAlbumExtraArt.empty())
+ {
+ std::vector<CVariant> whitelist;
+ ConvertToWhitelist(m_musicAlbumExtraArt, whitelist);
+ settings->SetList(CSettings::SETTING_MUSICLIBRARY_ALBUMART_WHITELIST, whitelist);
+ }
+
+ // Convert value like "folder.jpg|Folder.jpg|folder.JPG|Folder.JPG|cover.jpg|Cover.jpg|
+ // cover.jpeg|thumb.jpg|Thumb.jpg|thumb.JPG|Thumb.JPG" into case-insensitive unique elements
+ // e.g. {"folder.jpg", "cover.jpg", "cover.jpeg", "thumb.jpg"}
+ if (!m_musicThumbs.empty())
+ {
+ std::vector<std::string> thumbs1 = StringUtils::Split(m_musicThumbs, "|");
+ std::vector<std::string> thumbs2;
+ for (auto& it : thumbs1)
+ {
+ StringUtils::ToLower(it);
+ if (std::find(thumbs2.begin(), thumbs2.end(), it) == thumbs2.end())
+ thumbs2.emplace_back(it);
+ }
+ std::vector<CVariant> thumbs;
+ thumbs.reserve(thumbs2.size());
+ for (const auto& it : thumbs2)
+ thumbs.emplace_back(it);
+ settings->SetList(CSettings::SETTING_MUSICLIBRARY_MUSICTHUMBS, thumbs);
+ }
+
+ // Whitelists configured, set artwork level to custom
+ if (!m_musicAlbumExtraArt.empty() || !m_musicArtistExtraArt.empty())
+ settings->SetInt(CSettings::SETTING_MUSICLIBRARY_ARTWORKLEVEL, 2);
+
+ // Flag migration of settings so not done again
+ settings->SetBool(CSettings::SETTING_MUSICLIBRARY_ARTSETTINGS_UPDATED, true);
+ }
+
+ if (!settings->GetBool(CSettings::SETTING_VIDEOLIBRARY_ARTSETTINGS_UPDATED))
+ {
+ CLog::Log(LOGINFO, "Migrating old video library artwork settings to new GUI settings");
+ // Convert numeric art type variants into simple art type family entry
+ // e.g. {"banner", "fanart1", "fanart2", "fanart3"... } into { "banner", "fanart"}
+ if (!m_videoEpisodeExtraArt.empty())
+ {
+ std::vector<CVariant> whitelist;
+ ConvertToWhitelist(m_videoEpisodeExtraArt, whitelist);
+ settings->SetList(CSettings::SETTING_VIDEOLIBRARY_EPISODEART_WHITELIST, whitelist);
+ }
+ if (!m_videoTvShowExtraArt.empty())
+ {
+ std::vector<CVariant> whitelist;
+ ConvertToWhitelist(m_videoTvShowExtraArt, whitelist);
+ settings->SetList(CSettings::SETTING_VIDEOLIBRARY_TVSHOWART_WHITELIST, whitelist);
+ }
+ if (!m_videoMovieExtraArt.empty())
+ {
+ std::vector<CVariant> whitelist;
+ ConvertToWhitelist(m_videoMovieExtraArt, whitelist);
+ settings->SetList(CSettings::SETTING_VIDEOLIBRARY_MOVIEART_WHITELIST, whitelist);
+ }
+ if (!m_videoMusicVideoExtraArt.empty())
+ {
+ std::vector<CVariant> whitelist;
+ ConvertToWhitelist(m_videoMusicVideoExtraArt, whitelist);
+ settings->SetList(CSettings::SETTING_VIDEOLIBRARY_MUSICVIDEOART_WHITELIST, whitelist);
+ }
+
+ // Whitelists configured, set artwork level to custom
+ if (!m_videoEpisodeExtraArt.empty() || !m_videoTvShowExtraArt.empty()
+ || !m_videoMovieExtraArt.empty() || !m_videoMusicVideoExtraArt.empty())
+ settings->SetInt(CSettings::SETTING_VIDEOLIBRARY_ARTWORK_LEVEL,
+ CSettings::MUSICLIBRARY_ARTWORK_LEVEL_CUSTOM);
+
+ // Flag migration of settings so not done again
+ settings->SetBool(CSettings::SETTING_VIDEOLIBRARY_ARTSETTINGS_UPDATED, true);
+ }
+}