2 * Copyright (C) 2013-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
9 #include "MediaSettings.h"
11 #include "PlayListPlayer.h"
12 #include "ServiceBroker.h"
13 #include "cores/RetroPlayer/RetroPlayerUtils.h"
14 #include "dialogs/GUIDialogFileBrowser.h"
15 #include "guilib/LocalizeStrings.h"
16 #include "interfaces/AnnouncementManager.h"
17 #include "interfaces/builtins/Builtins.h"
18 #include "messaging/helpers/DialogHelper.h"
19 #include "messaging/helpers/DialogOKHelper.h"
20 #include "music/MusicLibraryQueue.h"
21 #include "settings/Settings.h"
22 #include "settings/dialogs/GUIDialogLibExportSettings.h"
23 #include "settings/lib/Setting.h"
24 #include "storage/MediaManager.h"
25 #include "utils/StringUtils.h"
26 #include "utils/Variant.h"
27 #include "utils/XBMCTinyXML.h"
28 #include "utils/XMLUtils.h"
29 #include "utils/log.h"
30 #include "video/VideoDatabase.h"
31 #include "video/VideoLibraryQueue.h"
38 using namespace KODI::MESSAGING
;
40 using KODI::MESSAGING::HELPERS::DialogResponse
;
42 CMediaSettings::CMediaSettings()
44 m_watchedModes
["files"] = WatchedModeAll
;
45 m_watchedModes
["movies"] = WatchedModeAll
;
46 m_watchedModes
["tvshows"] = WatchedModeAll
;
47 m_watchedModes
["musicvideos"] = WatchedModeAll
;
48 m_watchedModes
["recordings"] = WatchedModeAll
;
50 m_musicPlaylistRepeat
= false;
51 m_musicPlaylistShuffle
= false;
52 m_videoPlaylistRepeat
= false;
53 m_videoPlaylistShuffle
= false;
55 m_mediaStartWindowed
= false;
56 m_additionalSubtitleDirectoryChecked
= 0;
58 m_musicNeedsUpdate
= 0;
59 m_videoNeedsUpdate
= 0;
62 CMediaSettings::~CMediaSettings() = default;
64 CMediaSettings
& CMediaSettings::GetInstance()
66 static CMediaSettings sMediaSettings
;
67 return sMediaSettings
;
70 bool CMediaSettings::Load(const TiXmlNode
*settings
)
75 std::unique_lock
<CCriticalSection
> lock(m_critical
);
76 const TiXmlElement
*pElement
= settings
->FirstChildElement("defaultvideosettings");
80 XMLUtils::GetInt(pElement
, "interlacemethod", interlaceMethod
, VS_INTERLACEMETHOD_NONE
, VS_INTERLACEMETHOD_MAX
);
82 m_defaultVideoSettings
.m_InterlaceMethod
= (EINTERLACEMETHOD
)interlaceMethod
;
84 if (!XMLUtils::GetInt(pElement
, "scalingmethod", scalingMethod
, VS_SCALINGMETHOD_NEAREST
, VS_SCALINGMETHOD_MAX
))
85 scalingMethod
= (int)VS_SCALINGMETHOD_LINEAR
;
86 m_defaultVideoSettings
.m_ScalingMethod
= (ESCALINGMETHOD
)scalingMethod
;
88 XMLUtils::GetInt(pElement
, "viewmode", m_defaultVideoSettings
.m_ViewMode
, ViewModeNormal
, ViewModeZoom110Width
);
89 if (!XMLUtils::GetFloat(pElement
, "zoomamount", m_defaultVideoSettings
.m_CustomZoomAmount
, 0.5f
, 2.0f
))
90 m_defaultVideoSettings
.m_CustomZoomAmount
= 1.0f
;
91 if (!XMLUtils::GetFloat(pElement
, "pixelratio", m_defaultVideoSettings
.m_CustomPixelRatio
, 0.5f
, 2.0f
))
92 m_defaultVideoSettings
.m_CustomPixelRatio
= 1.0f
;
93 if (!XMLUtils::GetFloat(pElement
, "verticalshift", m_defaultVideoSettings
.m_CustomVerticalShift
, -2.0f
, 2.0f
))
94 m_defaultVideoSettings
.m_CustomVerticalShift
= 0.0f
;
95 if (!XMLUtils::GetFloat(pElement
, "volumeamplification", m_defaultVideoSettings
.m_VolumeAmplification
, VOLUME_DRC_MINIMUM
* 0.01f
, VOLUME_DRC_MAXIMUM
* 0.01f
))
96 m_defaultVideoSettings
.m_VolumeAmplification
= VOLUME_DRC_MINIMUM
* 0.01f
;
97 if (!XMLUtils::GetFloat(pElement
, "noisereduction", m_defaultVideoSettings
.m_NoiseReduction
, 0.0f
, 1.0f
))
98 m_defaultVideoSettings
.m_NoiseReduction
= 0.0f
;
99 XMLUtils::GetBoolean(pElement
, "postprocess", m_defaultVideoSettings
.m_PostProcess
);
100 if (!XMLUtils::GetFloat(pElement
, "sharpness", m_defaultVideoSettings
.m_Sharpness
, -1.0f
, 1.0f
))
101 m_defaultVideoSettings
.m_Sharpness
= 0.0f
;
102 XMLUtils::GetBoolean(pElement
, "showsubtitles", m_defaultVideoSettings
.m_SubtitleOn
);
103 if (!XMLUtils::GetFloat(pElement
, "brightness", m_defaultVideoSettings
.m_Brightness
, 0, 100))
104 m_defaultVideoSettings
.m_Brightness
= 50;
105 if (!XMLUtils::GetFloat(pElement
, "contrast", m_defaultVideoSettings
.m_Contrast
, 0, 100))
106 m_defaultVideoSettings
.m_Contrast
= 50;
107 if (!XMLUtils::GetFloat(pElement
, "gamma", m_defaultVideoSettings
.m_Gamma
, 0, 100))
108 m_defaultVideoSettings
.m_Gamma
= 20;
109 if (!XMLUtils::GetFloat(pElement
, "audiodelay", m_defaultVideoSettings
.m_AudioDelay
, -10.0f
, 10.0f
))
110 m_defaultVideoSettings
.m_AudioDelay
= 0.0f
;
111 if (!XMLUtils::GetFloat(pElement
, "subtitledelay", m_defaultVideoSettings
.m_SubtitleDelay
, -10.0f
, 10.0f
))
112 m_defaultVideoSettings
.m_SubtitleDelay
= 0.0f
;
113 XMLUtils::GetBoolean(pElement
, "nonlinstretch", m_defaultVideoSettings
.m_CustomNonLinStretch
);
114 if (!XMLUtils::GetInt(pElement
, "stereomode", m_defaultVideoSettings
.m_StereoMode
))
115 m_defaultVideoSettings
.m_StereoMode
= 0;
116 if (!XMLUtils::GetInt(pElement
, "centermixlevel", m_defaultVideoSettings
.m_CenterMixLevel
))
117 m_defaultVideoSettings
.m_CenterMixLevel
= 0;
120 if (!XMLUtils::GetInt(pElement
, "tonemapmethod", toneMapMethod
, VS_TONEMAPMETHOD_OFF
,
121 VS_TONEMAPMETHOD_MAX
))
122 toneMapMethod
= VS_TONEMAPMETHOD_REINHARD
;
123 m_defaultVideoSettings
.m_ToneMapMethod
= static_cast<ETONEMAPMETHOD
>(toneMapMethod
);
125 if (!XMLUtils::GetFloat(pElement
, "tonemapparam", m_defaultVideoSettings
.m_ToneMapParam
, 0.1f
, 5.0f
))
126 m_defaultVideoSettings
.m_ToneMapParam
= 1.0f
;
129 m_defaultGameSettings
.Reset();
130 pElement
= settings
->FirstChildElement("defaultgamesettings");
131 if (pElement
!= nullptr)
133 std::string videoFilter
;
134 if (XMLUtils::GetString(pElement
, "videofilter", videoFilter
))
135 m_defaultGameSettings
.SetVideoFilter(videoFilter
);
137 std::string stretchMode
;
138 if (XMLUtils::GetString(pElement
, "stretchmode", stretchMode
))
140 RETRO::STRETCHMODE sm
= RETRO::CRetroPlayerUtils::IdentifierToStretchMode(stretchMode
);
141 m_defaultGameSettings
.SetStretchMode(sm
);
145 if (XMLUtils::GetInt(pElement
, "rotation", rotation
, 0, 270) && rotation
>= 0)
146 m_defaultGameSettings
.SetRotationDegCCW(static_cast<unsigned int>(rotation
));
150 pElement
= settings
->FirstChildElement("mymusic");
151 if (pElement
!= NULL
)
153 const TiXmlElement
*pChild
= pElement
->FirstChildElement("playlist");
156 XMLUtils::GetBoolean(pChild
, "repeat", m_musicPlaylistRepeat
);
157 XMLUtils::GetBoolean(pChild
, "shuffle", m_musicPlaylistShuffle
);
159 if (!XMLUtils::GetInt(pElement
, "needsupdate", m_musicNeedsUpdate
, 0, INT_MAX
))
160 m_musicNeedsUpdate
= 0;
163 // Set music playlist player repeat and shuffle from loaded settings
164 if (m_musicPlaylistRepeat
)
165 CServiceBroker::GetPlaylistPlayer().SetRepeat(PLAYLIST::TYPE_MUSIC
, PLAYLIST::RepeatState::ALL
);
167 CServiceBroker::GetPlaylistPlayer().SetRepeat(PLAYLIST::TYPE_MUSIC
,
168 PLAYLIST::RepeatState::NONE
);
169 CServiceBroker::GetPlaylistPlayer().SetShuffle(PLAYLIST::TYPE_MUSIC
, m_musicPlaylistShuffle
);
171 // Read the watchmode settings for the various media views
172 pElement
= settings
->FirstChildElement("myvideos");
173 if (pElement
!= NULL
)
176 if (XMLUtils::GetInt(pElement
, "watchmodemovies", tmp
, (int)WatchedModeAll
, (int)WatchedModeWatched
))
177 m_watchedModes
["movies"] = (WatchedMode
)tmp
;
178 if (XMLUtils::GetInt(pElement
, "watchmodetvshows", tmp
, (int)WatchedModeAll
, (int)WatchedModeWatched
))
179 m_watchedModes
["tvshows"] = (WatchedMode
)tmp
;
180 if (XMLUtils::GetInt(pElement
, "watchmodemusicvideos", tmp
, (int)WatchedModeAll
, (int)WatchedModeWatched
))
181 m_watchedModes
["musicvideos"] = (WatchedMode
)tmp
;
182 if (XMLUtils::GetInt(pElement
, "watchmoderecordings", tmp
, static_cast<int>(WatchedModeAll
), static_cast<int>(WatchedModeWatched
)))
183 m_watchedModes
["recordings"] = static_cast<WatchedMode
>(tmp
);
185 const TiXmlElement
*pChild
= pElement
->FirstChildElement("playlist");
188 XMLUtils::GetBoolean(pChild
, "repeat", m_videoPlaylistRepeat
);
189 XMLUtils::GetBoolean(pChild
, "shuffle", m_videoPlaylistShuffle
);
191 if (!XMLUtils::GetInt(pElement
, "needsupdate", m_videoNeedsUpdate
, 0, INT_MAX
))
192 m_videoNeedsUpdate
= 0;
195 // Set video playlist player repeat and shuffle from loaded settings
196 if (m_videoPlaylistRepeat
)
197 CServiceBroker::GetPlaylistPlayer().SetRepeat(PLAYLIST::TYPE_VIDEO
, PLAYLIST::RepeatState::ALL
);
199 CServiceBroker::GetPlaylistPlayer().SetRepeat(PLAYLIST::TYPE_VIDEO
,
200 PLAYLIST::RepeatState::NONE
);
201 CServiceBroker::GetPlaylistPlayer().SetShuffle(PLAYLIST::TYPE_VIDEO
, m_videoPlaylistShuffle
);
206 bool CMediaSettings::Save(TiXmlNode
*settings
) const
208 if (settings
== NULL
)
211 std::unique_lock
<CCriticalSection
> lock(m_critical
);
212 // default video settings
213 TiXmlElement
videoSettingsNode("defaultvideosettings");
214 TiXmlNode
*pNode
= settings
->InsertEndChild(videoSettingsNode
);
218 XMLUtils::SetInt(pNode
, "interlacemethod", m_defaultVideoSettings
.m_InterlaceMethod
);
219 XMLUtils::SetInt(pNode
, "scalingmethod", m_defaultVideoSettings
.m_ScalingMethod
);
220 XMLUtils::SetFloat(pNode
, "noisereduction", m_defaultVideoSettings
.m_NoiseReduction
);
221 XMLUtils::SetBoolean(pNode
, "postprocess", m_defaultVideoSettings
.m_PostProcess
);
222 XMLUtils::SetFloat(pNode
, "sharpness", m_defaultVideoSettings
.m_Sharpness
);
223 XMLUtils::SetInt(pNode
, "viewmode", m_defaultVideoSettings
.m_ViewMode
);
224 XMLUtils::SetFloat(pNode
, "zoomamount", m_defaultVideoSettings
.m_CustomZoomAmount
);
225 XMLUtils::SetFloat(pNode
, "pixelratio", m_defaultVideoSettings
.m_CustomPixelRatio
);
226 XMLUtils::SetFloat(pNode
, "verticalshift", m_defaultVideoSettings
.m_CustomVerticalShift
);
227 XMLUtils::SetFloat(pNode
, "volumeamplification", m_defaultVideoSettings
.m_VolumeAmplification
);
228 XMLUtils::SetBoolean(pNode
, "showsubtitles", m_defaultVideoSettings
.m_SubtitleOn
);
229 XMLUtils::SetFloat(pNode
, "brightness", m_defaultVideoSettings
.m_Brightness
);
230 XMLUtils::SetFloat(pNode
, "contrast", m_defaultVideoSettings
.m_Contrast
);
231 XMLUtils::SetFloat(pNode
, "gamma", m_defaultVideoSettings
.m_Gamma
);
232 XMLUtils::SetFloat(pNode
, "audiodelay", m_defaultVideoSettings
.m_AudioDelay
);
233 XMLUtils::SetFloat(pNode
, "subtitledelay", m_defaultVideoSettings
.m_SubtitleDelay
);
234 XMLUtils::SetBoolean(pNode
, "nonlinstretch", m_defaultVideoSettings
.m_CustomNonLinStretch
);
235 XMLUtils::SetInt(pNode
, "stereomode", m_defaultVideoSettings
.m_StereoMode
);
236 XMLUtils::SetInt(pNode
, "centermixlevel", m_defaultVideoSettings
.m_CenterMixLevel
);
237 XMLUtils::SetInt(pNode
, "tonemapmethod", m_defaultVideoSettings
.m_ToneMapMethod
);
238 XMLUtils::SetFloat(pNode
, "tonemapparam", m_defaultVideoSettings
.m_ToneMapParam
);
240 // default audio settings for dsp addons
241 TiXmlElement
audioSettingsNode("defaultaudiosettings");
242 pNode
= settings
->InsertEndChild(audioSettingsNode
);
246 // Default game settings
247 TiXmlElement
gameSettingsNode("defaultgamesettings");
248 pNode
= settings
->InsertEndChild(gameSettingsNode
);
249 if (pNode
== nullptr)
252 XMLUtils::SetString(pNode
, "videofilter", m_defaultGameSettings
.VideoFilter());
253 std::string sm
= RETRO::CRetroPlayerUtils::StretchModeToIdentifier(m_defaultGameSettings
.StretchMode());
254 XMLUtils::SetString(pNode
, "stretchmode", sm
);
255 XMLUtils::SetInt(pNode
, "rotation", m_defaultGameSettings
.RotationDegCCW());
258 pNode
= settings
->FirstChild("mymusic");
261 TiXmlElement
videosNode("mymusic");
262 pNode
= settings
->InsertEndChild(videosNode
);
267 TiXmlElement
musicPlaylistNode("playlist");
268 TiXmlNode
*playlistNode
= pNode
->InsertEndChild(musicPlaylistNode
);
269 if (playlistNode
== NULL
)
271 XMLUtils::SetBoolean(playlistNode
, "repeat", m_musicPlaylistRepeat
);
272 XMLUtils::SetBoolean(playlistNode
, "shuffle", m_musicPlaylistShuffle
);
274 XMLUtils::SetInt(pNode
, "needsupdate", m_musicNeedsUpdate
);
277 pNode
= settings
->FirstChild("myvideos");
280 TiXmlElement
videosNode("myvideos");
281 pNode
= settings
->InsertEndChild(videosNode
);
286 XMLUtils::SetInt(pNode
, "watchmodemovies", m_watchedModes
.find("movies")->second
);
287 XMLUtils::SetInt(pNode
, "watchmodetvshows", m_watchedModes
.find("tvshows")->second
);
288 XMLUtils::SetInt(pNode
, "watchmodemusicvideos", m_watchedModes
.find("musicvideos")->second
);
289 XMLUtils::SetInt(pNode
, "watchmoderecordings", m_watchedModes
.find("recordings")->second
);
291 TiXmlElement
videoPlaylistNode("playlist");
292 playlistNode
= pNode
->InsertEndChild(videoPlaylistNode
);
293 if (playlistNode
== NULL
)
295 XMLUtils::SetBoolean(playlistNode
, "repeat", m_videoPlaylistRepeat
);
296 XMLUtils::SetBoolean(playlistNode
, "shuffle", m_videoPlaylistShuffle
);
298 XMLUtils::SetInt(pNode
, "needsupdate", m_videoNeedsUpdate
);
303 void CMediaSettings::OnSettingAction(const std::shared_ptr
<const CSetting
>& setting
)
308 const std::string
&settingId
= setting
->GetId();
309 if (settingId
== CSettings::SETTING_MUSICLIBRARY_CLEANUP
)
311 if (HELPERS::ShowYesNoDialogText(CVariant
{313}, CVariant
{333}) == DialogResponse::CHOICE_YES
)
313 if (CMusicLibraryQueue::GetInstance().IsRunning())
314 HELPERS::ShowOKDialogText(CVariant
{700}, CVariant
{703});
316 CMusicLibraryQueue::GetInstance().CleanLibrary(true);
319 else if (settingId
== CSettings::SETTING_MUSICLIBRARY_EXPORT
)
321 CLibExportSettings m_musicExportSettings
;
322 if (CGUIDialogLibExportSettings::Show(m_musicExportSettings
))
324 // Export music library showing progress dialog
325 CMusicLibraryQueue::GetInstance().ExportLibrary(m_musicExportSettings
, true);
328 else if (settingId
== CSettings::SETTING_MUSICLIBRARY_IMPORT
)
332 CServiceBroker::GetMediaManager().GetLocalDrives(shares
);
333 CServiceBroker::GetMediaManager().GetNetworkLocations(shares
);
334 CServiceBroker::GetMediaManager().GetRemovableDrives(shares
);
336 if (CGUIDialogFileBrowser::ShowAndGetFile(shares
, "musicdb.xml", g_localizeStrings
.Get(651) , path
))
338 // Import data to music library showing progress dialog
339 CMusicLibraryQueue::GetInstance().ImportLibrary(path
, true);
342 else if (settingId
== CSettings::SETTING_VIDEOLIBRARY_CLEANUP
)
344 if (HELPERS::ShowYesNoDialogText(CVariant
{313}, CVariant
{333}) == DialogResponse::CHOICE_YES
)
346 if (!CVideoLibraryQueue::GetInstance().CleanLibraryModal())
347 HELPERS::ShowOKDialogText(CVariant
{700}, CVariant
{703});
350 else if (settingId
== CSettings::SETTING_VIDEOLIBRARY_EXPORT
)
351 CBuiltins::GetInstance().Execute("exportlibrary(video)");
352 else if (settingId
== CSettings::SETTING_VIDEOLIBRARY_IMPORT
)
356 CServiceBroker::GetMediaManager().GetLocalDrives(shares
);
357 CServiceBroker::GetMediaManager().GetNetworkLocations(shares
);
358 CServiceBroker::GetMediaManager().GetRemovableDrives(shares
);
360 if (CGUIDialogFileBrowser::ShowAndGetDirectory(shares
, g_localizeStrings
.Get(651) , path
))
362 CVideoDatabase videodatabase
;
363 videodatabase
.Open();
364 videodatabase
.ImportFromXML(path
);
365 videodatabase
.Close();
370 void CMediaSettings::OnSettingChanged(const std::shared_ptr
<const CSetting
>& setting
)
372 if (setting
== nullptr)
375 if (setting
->GetId() == CSettings::SETTING_VIDEOLIBRARY_SHOWUNWATCHEDPLOTS
)
376 CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::VideoLibrary
, "OnRefresh");
379 int CMediaSettings::GetWatchedMode(const std::string
&content
) const
381 std::unique_lock
<CCriticalSection
> lock(m_critical
);
382 WatchedModes::const_iterator it
= m_watchedModes
.find(GetWatchedContent(content
));
383 if (it
!= m_watchedModes
.end())
386 return WatchedModeAll
;
389 void CMediaSettings::SetWatchedMode(const std::string
&content
, WatchedMode mode
)
391 std::unique_lock
<CCriticalSection
> lock(m_critical
);
392 WatchedModes::iterator it
= m_watchedModes
.find(GetWatchedContent(content
));
393 if (it
!= m_watchedModes
.end())
397 void CMediaSettings::CycleWatchedMode(const std::string
&content
)
399 std::unique_lock
<CCriticalSection
> lock(m_critical
);
400 WatchedModes::iterator it
= m_watchedModes
.find(GetWatchedContent(content
));
401 if (it
!= m_watchedModes
.end())
403 it
->second
= (WatchedMode
)((int)it
->second
+ 1);
404 if (it
->second
> WatchedModeWatched
)
405 it
->second
= WatchedModeAll
;
409 std::string
CMediaSettings::GetWatchedContent(const std::string
&content
)
411 if (content
== "seasons" || content
== "episodes")