/* * 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 #include #include #include #include using namespace ADDON; CAdvancedSettings::CAdvancedSettings() { m_initialized = false; m_fullScreen = false; } void CAdvancedSettings::OnSettingsLoaded() { const std::shared_ptr 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 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& setting) { if (setting == NULL) return; const std::string &settingId = setting->GetId(); if (settingId == CSettings::SETTING_DEBUG_SHOWLOGINFO) SetDebugMode(std::static_pointer_cast(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 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 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 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 tag"); else CLog::Log(LOGERROR," Missing 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 steps = StringUtils::Split(seekSteps, ','); for(std::vector::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& 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 exts = StringUtils::Split(extraExtensions, '|'); for (std::vector::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& 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& oldlist, std::vector& 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 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 whitelist; ConvertToWhitelist(m_musicArtistExtraArt, whitelist); settings->SetList(CSettings::SETTING_MUSICLIBRARY_ARTISTART_WHITELIST, whitelist); } if (!m_musicAlbumExtraArt.empty()) { std::vector 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 thumbs1 = StringUtils::Split(m_musicThumbs, "|"); std::vector thumbs2; for (auto& it : thumbs1) { StringUtils::ToLower(it); if (std::find(thumbs2.begin(), thumbs2.end(), it) == thumbs2.end()) thumbs2.emplace_back(it); } std::vector 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 whitelist; ConvertToWhitelist(m_videoEpisodeExtraArt, whitelist); settings->SetList(CSettings::SETTING_VIDEOLIBRARY_EPISODEART_WHITELIST, whitelist); } if (!m_videoTvShowExtraArt.empty()) { std::vector whitelist; ConvertToWhitelist(m_videoTvShowExtraArt, whitelist); settings->SetList(CSettings::SETTING_VIDEOLIBRARY_TVSHOWART_WHITELIST, whitelist); } if (!m_videoMovieExtraArt.empty()) { std::vector whitelist; ConvertToWhitelist(m_videoMovieExtraArt, whitelist); settings->SetList(CSettings::SETTING_VIDEOLIBRARY_MOVIEART_WHITELIST, whitelist); } if (!m_videoMusicVideoExtraArt.empty()) { std::vector 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); } }